From eb5f36d43cc203966358f7faf521084dabb5f9c9 Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Tue, 6 Oct 2026 05:04:53 +0000 Subject: [PATCH 01/22] feat(wasix): integrate bundled Windows V8 engine and native caches --- .github/workflows/ci.yml | 6 + .github/workflows/wasix-host.yml | 104 +- .gitignore | 3 + Cargo.lock | 1605 ++++++++++++- Cargo.toml | 6 + release-please-config.json | 25 + .../packages/win32-x64-msvc/package.json | 2 +- .../node-addon/tools/check-release-assets.mts | 15 +- .../node-addon/tools/package-platform.mts | 6 +- .../tools/workspace-runtime-contract.mts | 6 + .../x86_64-pc-windows-msvc/build-support.rs | 10 +- .../x86_64-pc-windows-msvc/build-support.rs | 10 +- src/wasix/runtime/engine/README.md | 34 + src/wasix/runtime/engine/contract.mts | 12 + .../crates/v8-windows-x64-msvc/Cargo.toml | 12 + .../crates/v8-windows-x64-msvc/build.rs | 12 + .../crates/v8-windows-x64-msvc/src/lib.rs | 7 + .../engine/crates/wasmer-journal/Cargo.toml | 120 + .../crates/wasmer-wasix-types/Cargo.toml | 107 + .../engine/crates/wasmer-wasix/Cargo.toml | 491 ++++ .../runtime/engine/crates/wasmer/Cargo.toml | 318 +++ .../runtime/engine/crates/wasmer/build.rs | 14 + .../runtime/engine/licenses/V8-LICENSES.txt | 2116 +++++++++++++++++ .../runtime/engine/licenses/Wasmer-LICENSE | 21 + src/wasix/runtime/engine/package.mts | 193 ++ src/wasix/runtime/engine/package.test.mts | 136 ++ ...cached-loading-and-terminal-shutdown.patch | 187 ++ .../engine/patches/wasmer-wasix/series | 1 + ...ive-ownership-and-terminal-lifetimes.patch | 1330 +++++++++++ .../runtime/engine/patches/wasmer/series | 1 + src/wasix/runtime/engine/prepare-sources.mts | 76 + src/wasix/runtime/engine/prepare-sources.sh | 12 + src/wasix/runtime/engine/source.toml | 48 + src/wasix/runtime/engine/windows/bindings.rs | 2042 ++++++++++++++++ src/wasix/runtime/engine/windows/bridge.cpp | 65 + .../runtime/engine/windows/build-caches.mts | 109 + src/wasix/runtime/engine/windows/build.mts | 173 ++ src/wasix/runtime/engine/windows/build.sh | 22 + .../runtime/engine/windows/cache-producer.c | 112 + src/wasix/runtime/engine/windows/dispatch.mts | 140 ++ .../runtime/engine/windows/dispatch.test.mts | 21 + src/wasix/runtime/engine/windows/kernel32.def | 18 + src/wasix/runtime/engine/windows/loader.rs | 259 ++ src/wasix/runtime/moon.yml | 58 +- src/wasix/runtime/release.toml | 6 + .../tools/check-release-assets.test.mts | 2 +- src/wasix/runtime/tools/download-assets.sh | 3 + src/wasix/runtime/tools/package-carriers.mts | 17 + ...iboliphaunt-wasix-cargo-artifacts.test.mts | 4 +- ...age_liboliphaunt_wasix_cargo_artifacts.mts | 3 + src/wasix/runtime/tools/test-packaging.sh | 1 + .../runtime/tools/wasix-aot-manifest.mts | 11 +- .../tools/wasix-cargo-artifact-contract.mts | 5 +- .../tools/wasix-cargo-toolchain-policy.mts | 13 +- .../wasix-cargo-toolchain-policy.test.mts | 38 +- src/wasix/runtime/tools/xtask/Cargo.toml | 14 +- src/wasix/runtime/tools/xtask/moon.yml | 1 + .../runtime/tools/xtask/src/aot_serializer.rs | 145 +- .../runtime/tools/xtask/src/asset_pipeline.rs | 33 +- src/wasix/sdks/rust/Cargo.toml | 24 +- src/wasix/sdks/rust/THIRD_PARTY_NOTICES.md | 9 +- src/wasix/sdks/rust/moon.yml | 3 + src/wasix/sdks/rust/src/oliphaunt/aot.rs | 67 +- .../sdks/rust/src/oliphaunt/postgres_mod.rs | 1 + src/wasix/sdks/rust/src/oliphaunt/tools.rs | 2 +- .../tools/prepare-rust-release-source.mts | 19 +- tools/packaging/cargo-source-package.mts | 10 + tools/packaging/cargo-source-package.test.mts | 13 + tools/packaging/release-notices.mts | 62 +- ...oad-wasix-runtime-build-artifacts.test.mts | 5 +- 70 files changed, 10368 insertions(+), 208 deletions(-) create mode 100644 src/wasix/runtime/engine/README.md create mode 100644 src/wasix/runtime/engine/contract.mts create mode 100644 src/wasix/runtime/engine/crates/v8-windows-x64-msvc/Cargo.toml create mode 100644 src/wasix/runtime/engine/crates/v8-windows-x64-msvc/build.rs create mode 100644 src/wasix/runtime/engine/crates/v8-windows-x64-msvc/src/lib.rs create mode 100644 src/wasix/runtime/engine/crates/wasmer-journal/Cargo.toml create mode 100644 src/wasix/runtime/engine/crates/wasmer-wasix-types/Cargo.toml create mode 100644 src/wasix/runtime/engine/crates/wasmer-wasix/Cargo.toml create mode 100644 src/wasix/runtime/engine/crates/wasmer/Cargo.toml create mode 100644 src/wasix/runtime/engine/crates/wasmer/build.rs create mode 100644 src/wasix/runtime/engine/licenses/V8-LICENSES.txt create mode 100644 src/wasix/runtime/engine/licenses/Wasmer-LICENSE create mode 100644 src/wasix/runtime/engine/package.mts create mode 100644 src/wasix/runtime/engine/package.test.mts create mode 100644 src/wasix/runtime/engine/patches/wasmer-wasix/0001-cached-loading-and-terminal-shutdown.patch create mode 100644 src/wasix/runtime/engine/patches/wasmer-wasix/series create mode 100644 src/wasix/runtime/engine/patches/wasmer/0001-native-ownership-and-terminal-lifetimes.patch create mode 100644 src/wasix/runtime/engine/patches/wasmer/series create mode 100644 src/wasix/runtime/engine/prepare-sources.mts create mode 100644 src/wasix/runtime/engine/prepare-sources.sh create mode 100644 src/wasix/runtime/engine/source.toml create mode 100644 src/wasix/runtime/engine/windows/bindings.rs create mode 100644 src/wasix/runtime/engine/windows/bridge.cpp create mode 100644 src/wasix/runtime/engine/windows/build-caches.mts create mode 100644 src/wasix/runtime/engine/windows/build.mts create mode 100644 src/wasix/runtime/engine/windows/build.sh create mode 100644 src/wasix/runtime/engine/windows/cache-producer.c create mode 100644 src/wasix/runtime/engine/windows/dispatch.mts create mode 100644 src/wasix/runtime/engine/windows/dispatch.test.mts create mode 100644 src/wasix/runtime/engine/windows/kernel32.def create mode 100644 src/wasix/runtime/engine/windows/loader.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d05739c8..d7cee6c41 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2327,6 +2327,12 @@ jobs: rsync -a "$raw_target_dir/" "$destination/" done + - name: Download qualified Windows engine + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + with: + name: liboliphaunt-wasix-windows-engine + path: . + - name: Package WASIX release assets env: OLIPHAUNT_MOON_TRANSFERRED_DEPS_JSON: '["liboliphaunt-wasix:runtime-portable", "liboliphaunt-wasix:runtime-aot"]' diff --git a/.github/workflows/wasix-host.yml b/.github/workflows/wasix-host.yml index 060b1f665..332e046a8 100644 --- a/.github/workflows/wasix-host.yml +++ b/.github/workflows/wasix-host.yml @@ -28,9 +28,93 @@ defaults: run: shell: bash jobs: + windows-engine: + name: WASIX Windows engine + if: ${{ contains(inputs.matrix, 'windows-x64-msvc') }} + runs-on: windows-2022 + timeout-minutes: 40 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + - uses: ./.github/actions/setup-moon + with: + cache-partition: wasix-windows-engine + - uses: ./.github/actions/setup-rust + with: + cache-save-if: ${{ inputs.cache-save-if }} + - uses: ./.github/actions/setup-msvc + - name: Produce pinned Windows engine + run: moon run liboliphaunt-wasix:engine-build + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + with: + name: liboliphaunt-wasix-windows-engine + include-hidden-files: true + path: | + target/oliphaunt-wasix/engine/windows-x64-msvc/oliphaunt_wee8.dll + target/oliphaunt-wasix/engine/windows-x64-msvc/cache-*.exe + target/oliphaunt-wasix/engine/windows-x64-msvc/source.json + target/oliphaunt-wasix/engine/windows-x64-msvc/LICENSE.v8 + src/wasix/runtime/engine/crates/*/upstream/** + src/wasix/runtime/engine/crates/wasmer/prebuilt/embedded_bindings.rs + if-no-files-found: error + windows-caches: + name: WASIX Windows baseline caches + needs: windows-engine + if: ${{ contains(inputs.matrix, 'windows-x64-msvc') }} + runs-on: ubuntu-24.04 + timeout-minutes: 90 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + - uses: ./.github/actions/setup-moon + with: + cache-partition: wasix-windows-caches + - uses: ./.github/actions/setup-rust + with: + cache-save-if: ${{ inputs.cache-save-if }} + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + with: + name: liboliphaunt-wasix-windows-engine + path: . + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + with: + name: liboliphaunt-wasix-runtime-portable + path: . + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + if: ${{ contains(fromJson(inputs.job-targets)['liboliphaunt-wasix-aot'], 'postgres-tools-wasix:build-aot') }} + with: + name: postgres-tools-wasix-compiler-output + path: target/postgres-tools/wasix/assets + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + if: ${{ contains(fromJson(inputs.job-targets)['liboliphaunt-wasix-aot'], 'extension-artifacts-wasix:build-aot') }} + with: + name: extensions-wasix-compiler-output + path: target/extensions/wasix/assets + - name: Set up CPU profile producer + run: sudo apt-get update && sudo apt-get install --no-install-recommends -y wine64 libz-mingw-w64 qemu-user + - name: Produce and read genuine SSE4.1 native caches + env: + BUILD_TOOLS: ${{ contains(fromJson(inputs.job-targets)['liboliphaunt-wasix-aot'], 'postgres-tools-wasix:build-aot') }} + BUILD_EXTENSIONS: ${{ contains(fromJson(inputs.job-targets)['liboliphaunt-wasix-aot'], 'extension-artifacts-wasix:build-aot') }} + run: | + products=(runtime) + if [ "$BUILD_TOOLS" = true ]; then products+=(tools); fi + if [ "$BUILD_EXTENSIONS" = true ]; then products+=(extensions); fi + bun src/wasix/runtime/engine/windows/build-caches.mts "${products[@]}" + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + with: + name: liboliphaunt-wasix-windows-baseline-caches + path: target/oliphaunt-wasix/engine/windows-x64-msvc/cache/sse41 + if-no-files-found: error build: name: WASIX host (${{ matrix.target_id }}) runs-on: ${{ matrix.os }} + needs: [windows-engine, windows-caches] + if: ${{ !cancelled() }} timeout-minutes: 240 strategy: fail-fast: false @@ -57,6 +141,21 @@ jobs: if: ${{ runner.os == 'Windows' }} uses: ./.github/actions/setup-msvc + - name: Download same-run Windows engine + if: ${{ runner.os == 'Windows' }} + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + with: + name: liboliphaunt-wasix-windows-engine + path: . + - name: Download same-run Windows baseline caches + if: ${{ runner.os == 'Windows' }} + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + with: + name: liboliphaunt-wasix-windows-baseline-caches + path: target/oliphaunt-wasix/engine/windows-x64-msvc/cache/sse41 + - name: Prepare engine source + run: moon run liboliphaunt-wasix:engine-sources + - name: Download portable WASIX build outputs uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: @@ -78,6 +177,7 @@ jobs: path: target/extensions/wasix/assets - name: Install Wasmer LLVM 22.1 for AOT generation + if: ${{ runner.os != 'Windows' }} uses: ./.github/actions/setup-wasmer-llvm with: cache-save-if: ${{ inputs.cache-save-if }} @@ -89,7 +189,7 @@ jobs: - name: Build, validate, and smoke target AOT artifacts env: AOT_TARGET: ${{ matrix.target }} - OLIPHAUNT_MOON_TRANSFERRED_DEPS_JSON: '["liboliphaunt-wasix:runtime-portable", "postgres-tools-wasix:compiler-output", "extension-artifacts-wasix:compiler-output"]' + OLIPHAUNT_MOON_TRANSFERRED_DEPS_JSON: '["liboliphaunt-wasix:runtime-portable", "postgres-tools-wasix:compiler-output", "extension-artifacts-wasix:compiler-output", "liboliphaunt-wasix:engine-build"]' run: .github/scripts/run-planned-moon-job.sh liboliphaunt-wasix-aot - name: Upload target PostgreSQL WASIX tools @@ -165,7 +265,7 @@ jobs: OLIPHAUNT_WASIX_EXTENSION_ARTIFACT_ROOT: ${{ github.workspace }}/target/extension-artifacts OLIPHAUNT_WASIX_GENERATED_ASSETS_DIR: ${{ github.workspace }}/target/oliphaunt-wasix/assets OLIPHAUNT_WASIX_NAPI_ARTIFACT_SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }} - OLIPHAUNT_MOON_TRANSFERRED_DEPS_JSON: '["extension-artifacts-wasix:build-target", "extension-artifacts-wasix:build-aot", "liboliphaunt-wasix:runtime-aot"]' + OLIPHAUNT_MOON_TRANSFERRED_DEPS_JSON: '["liboliphaunt-wasix:engine-build", "extension-artifacts-wasix:build-target", "extension-artifacts-wasix:build-aot", "liboliphaunt-wasix:runtime-aot"]' run: .github/scripts/run-planned-moon-job.sh wasix-napi - name: Upload WASIX Node-API release assets diff --git a/.gitignore b/.gitignore index 43cebf661..46f037d34 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,9 @@ node_modules/ /src/docs/out/ /src/wasix/runtime/crates/assets/assets/ /src/wasix/runtime/crates/aot/*/artifacts/ +/src/wasix/runtime/engine/crates/*/upstream/ +/src/wasix/runtime/engine/crates/*/.prepared.json +/src/wasix/runtime/engine/crates/*/prebuilt/ /src/native/sdks/react-native/ios/vendor/oliphaunt-swift/ /src/native/sdks/react-native/ios/vendor/liboliphaunt.xcframework/ **/.DS_Store diff --git a/Cargo.lock b/Cargo.lock index 4a57d0495..b598ffd29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,6 +54,15 @@ version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "1.0.0" @@ -116,6 +125,12 @@ version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + [[package]] name = "arrayref" version = "0.3.9" @@ -207,12 +222,41 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8ab6b55fe97976e46f91ddbed8d147d966475dc29b2032757ba47e02376fbc3" +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "aws-lc-rs" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + [[package]] name = "backtrace" version = "0.3.76" @@ -249,6 +293,15 @@ dependencies = [ "serde", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bincode" version = "2.0.1" @@ -352,6 +405,9 @@ name = "bumpalo" version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" +dependencies = [ + "allocator-api2", +] [[package]] name = "bumpalo-herd" @@ -518,7 +574,11 @@ version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ + "iana-time-zone", + "js-sys", "num-traits", + "wasm-bindgen", + "windows-link", ] [[package]] @@ -644,6 +704,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "370c83b49aedf022ee27942e8ae1d9de1cf40dc9653ee6550e4455d08f6406f9" +[[package]] +name = "combine" +version = "4.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -700,6 +770,22 @@ version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147be55d677052dabc6b22252d5dd0fd4c29c8c27aa4f2fbef0f94aa003b406f" +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "corosensei" version = "0.3.4" @@ -749,6 +835,132 @@ dependencies = [ "libc", ] +[[package]] +name = "cranelift-assembler-x64" +version = "0.136.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658e8623bea025d142fb11c299b8c6572ffe9fd663b52887c4dc124735cc130b" +dependencies = [ + "cranelift-assembler-x64-meta", +] + +[[package]] +name = "cranelift-assembler-x64-meta" +version = "0.136.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b12b8c55c31c3d8598c2de9a657c3b978f905f6886c43e02216e0b15fe45a3" +dependencies = [ + "cranelift-srcgen", +] + +[[package]] +name = "cranelift-bforest" +version = "0.136.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c9b7896b744283c68051c6fa53dff5919bdca5f3ad30f2502cbe03346fc27c" +dependencies = [ + "cranelift-entity", + "wasmtime-internal-core", +] + +[[package]] +name = "cranelift-bitset" +version = "0.136.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d44fd93e9b7d5d76846a5d7e96f4f31c84e7c84079bf69e2bf17e5d08f8d607" +dependencies = [ + "wasmtime-internal-core", +] + +[[package]] +name = "cranelift-codegen" +version = "0.136.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24cca39a8327756e489ee65c7f2ff79a6f7c8f2837f83fef046d1899c781c59d" +dependencies = [ + "bumpalo", + "cranelift-assembler-x64", + "cranelift-bforest", + "cranelift-bitset", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-isle", + "gimli 0.33.0", + "hashbrown 0.17.1", + "libm", + "log", + "regalloc2", + "rustc-hash", + "serde", + "smallvec", + "target-lexicon", + "wasmtime-internal-core", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.136.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26de7849112293681a19f7525c689e1d73dc6eb2687355170fb7458c4da8d353" +dependencies = [ + "cranelift-assembler-x64-meta", + "cranelift-codegen-shared", + "cranelift-srcgen", + "heck 0.5.0", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.136.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df315a8b1cfd6e620971e3d4249bd2c4528167717e8f48a6317a87c55891bcb8" + +[[package]] +name = "cranelift-control" +version = "0.136.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3fff7610fb17f228f16598e1a044db79a12806ece7496ff2c69cf1a27a79a5" +dependencies = [ + "arbitrary", +] + +[[package]] +name = "cranelift-entity" +version = "0.136.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589be6584f586f8b21fe17e68cb10505d1cb9a3aa42af1173b8024a12f3ec96d" +dependencies = [ + "cranelift-bitset", + "wasmtime-internal-core", +] + +[[package]] +name = "cranelift-frontend" +version = "0.136.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413f3f1d97d6157bd2c4e2edf1f22966a849b8d524834558e563c5a4a3bc5791" +dependencies = [ + "cranelift-codegen", + "hashbrown 0.17.1", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.136.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b46ea04636aa506eb49e5a61c54270314912de7f1c33ad7f7c2c57cb0784e23" + +[[package]] +name = "cranelift-srcgen" +version = "0.136.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4944ef05513e1c1a85a945cf8f29d95ddea8bcbb19e0bfd4e71063450967420" + [[package]] name = "crc" version = "3.4.0" @@ -1125,6 +1337,45 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +[[package]] +name = "dynasm" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd358e74d2f8d71a11b7a2c51c67ad5df3de06003b0596875e47bc09126c894e" +dependencies = [ + "bitflags 2.13.2", + "byteorder", + "lazy_static", + "proc-macro-error3", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dynasmrt" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6efe03f6bd356ae85eeea7ee14c2bf54e664a949d089d238364b5a99afbc4116" +dependencies = [ + "byteorder", + "dynasm", + "fnv", + "memmap2 0.9.10", +] + +[[package]] +name = "educe" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4bd92664bf78c4d3dba9b7cdafce6fa15b13ed3ed16175218196942e99168a8" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "either" version = "1.16.0" @@ -1172,6 +1423,26 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "enum-ordinalize" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89dd01549b09589510cf0647475075d12071456586d70f5c75c98ae2a5537677" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65863d15a4ce2888bd2f0f543cc963d3879c3a022c8ee43f6141d479a3ac815" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + [[package]] name = "enumset" version = "1.1.13" @@ -1312,6 +1583,21 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1473,8 +1759,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -1513,6 +1801,18 @@ version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +[[package]] +name = "gimli" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7f043f89559805f8c7cacc432749b2fa0d0a0a9ee46ce47164ed5ba7f126c" +dependencies = [ + "fnv", + "hashbrown 0.16.1", + "indexmap", + "stable_deref_trait", +] + [[package]] name = "gimli" version = "0.34.0" @@ -1592,12 +1892,20 @@ dependencies = [ "foldhash 0.1.5", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + [[package]] name = "hashbrown" version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.2.0", "serde", "serde_core", @@ -1696,78 +2004,223 @@ dependencies = [ ] [[package]] -name = "hybrid-array" -version = "0.4.12" +name = "http-body" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ - "typenum", + "bytes", + "http", ] [[package]] -name = "icu_collections" -version = "2.2.0" +name = "http-body-util" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", ] [[package]] -name = "icu_locale_core" -version = "2.2.0" +name = "http-serde" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +checksum = "0f056c8559e3757392c8d091e796416e4649d8e49e88b8d76df6c002f05027fd" dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", + "http", + "serde", ] [[package]] -name = "icu_normalizer" -version = "2.2.0" +name = "httparse" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] -name = "icu_normalizer_data" -version = "2.2.0" +name = "httpdate" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] -name = "icu_properties" -version = "2.2.0" +name = "hybrid-array" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", + "typenum", ] [[package]] -name = "icu_properties_data" -version = "2.2.0" +name = "hyper" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa8e654703247911e29c23fbeaa261834bd9bb74efba2f9acddc37bfb127f53" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc03d96684f9226b8a787cdb71488417b53ab5ea8fdb1dac946cb9431cc8bff" +dependencies = [ + "base64 0.23.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" @@ -1952,6 +2405,55 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.117", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "jobserver" version = "0.1.34" @@ -2027,6 +2529,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + [[package]] name = "liboliphaunt-native-bindings" version = "0.2.0" @@ -2278,6 +2786,33 @@ version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" +[[package]] +name = "loupe" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4de4e09ccbef442225e81339e930ed93dc339d4f1b2eaf479f89590ebeb9bf13" +dependencies = [ + "indexmap", + "loupe-derive", + "rustversion", +] + +[[package]] +name = "loupe-derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8362a975c18ea799abfcb1902140370acd73d239f8c2f74d6e2c5f6a30929f" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "lru-slab" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f" + [[package]] name = "lz4_flex" version = "0.14.0" @@ -2403,6 +2938,16 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" +[[package]] +name = "msvc-demangler" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeff6bd154a309b2ada5639b2661ca6ae4599b34e8487dc276d2cd637da2d76" +dependencies = [ + "bitflags 2.13.2", + "itoa", +] + [[package]] name = "munge" version = "0.4.7" @@ -2481,6 +3026,23 @@ dependencies = [ "libloading 0.9.0", ] +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nix" version = "0.30.1" @@ -2790,6 +3352,10 @@ dependencies = [ "oliphaunt-wasix-tools-aot-aarch64-unknown-linux-gnu", "oliphaunt-wasix-tools-aot-x86_64-pc-windows-msvc", "oliphaunt-wasix-tools-aot-x86_64-unknown-linux-gnu", + "oliphaunt-wasmer", + "oliphaunt-wasmer-journal", + "oliphaunt-wasmer-wasix", + "oliphaunt-wasmer-wasix-types", "serde", "serde_json", "sha2 0.10.9", @@ -2801,13 +3367,9 @@ dependencies = [ "virtual-fs", "virtual-mio", "virtual-net", - "wasmer", "wasmer-config", - "wasmer-journal", "wasmer-package", "wasmer-types", - "wasmer-wasix", - "wasmer-wasix-types", "webc", "zstd 0.13.3", ] @@ -2881,34 +3443,256 @@ dependencies = [ ] [[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" +name = "oliphaunt-wasmer" +version = "0.3.1" +dependencies = [ + "bytes", + "corosensei", + "dashmap", + "derive_more", + "futures", + "hashbrown 0.17.1", + "indexmap", + "itertools 0.15.0", + "js-sys", + "loupe", + "more-asserts", + "oliphaunt-wasmer-v8-windows-x64-msvc", + "paste", + "rkyv", + "serde", + "serde-wasm-bindgen", + "shared-buffer", + "symbolic-demangle", + "target-lexicon", + "tempfile", + "thiserror", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasmer-compiler", + "wasmer-compiler-cranelift", + "wasmer-compiler-llvm", + "wasmer-compiler-singlepass", + "wasmer-derive", + "wasmer-types", + "wasmer-vm", + "wasmparser 0.259.0", + "wat", + "windows-sys 0.61.2", +] [[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +name = "oliphaunt-wasmer-journal" +version = "0.3.1" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.23.1", + "bincode 2.0.1", + "bytecheck", + "bytes", + "derive_more", + "lz4_flex", + "num_enum", + "oliphaunt-wasmer", + "oliphaunt-wasmer-wasix-types", + "rkyv", + "serde", + "serde_json", + "shared-buffer", + "thiserror", + "tracing", + "virtual-fs", + "virtual-net", + "wasmer-config", +] + +[[package]] +name = "oliphaunt-wasmer-v8-windows-x64-msvc" +version = "0.3.1" + +[[package]] +name = "oliphaunt-wasmer-wasix" +version = "0.3.1" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.23.1", + "bincode 2.0.1", + "blake3", + "bus", + "bytecheck", + "bytes", + "chrono", + "cooked-waker", + "crossbeam-channel", + "dashmap", + "derive_more", + "flate2", + "fnv", + "fs_extra", + "futures", + "getrandom 0.3.4", + "getrandom 0.4.2", + "heapless", + "hex", + "http", + "http-body-util", + "hyper", + "hyper-util", + "itertools 0.15.0", + "js-sys", + "libc", + "libtest-mimic", + "linked_hash_set", + "lz4_flex", + "num_enum", + "oliphaunt-wasmer", + "oliphaunt-wasmer-journal", + "oliphaunt-wasmer-wasix-types", + "once_cell", + "petgraph", + "pin-project", + "pin-utils", + "rand 0.10.1", + "rayon", + "reqwest", + "rkyv", + "rusty_pool", + "semver", + "serde", + "serde_derive", + "serde_json", + "serde_yaml", + "sha2 0.11.0", + "shared-buffer", + "tempfile", + "terminal_size", + "termios", + "thiserror", + "tokio", + "tokio-stream", + "toml 1.1.2+spec-1.1.0", + "tower", + "tower-http 0.7.1", + "tracing", + "url", + "urlencoding", + "virtual-fs", + "virtual-mio", + "virtual-net", + "waker-fn", + "walkdir", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-encoder 0.259.0", + "wasmer-config", + "wasmer-package", + "wasmer-types", + "wasmparser 0.259.0", + "wcgi", + "wcgi-host", + "web-sys", + "webc", + "windows-sys 0.61.2", + "xxhash-rust", + "zstd 0.14.0", +] + +[[package]] +name = "oliphaunt-wasmer-wasix-types" +version = "0.3.1" +dependencies = [ + "anyhow", + "bitflags 2.13.2", + "byteorder", + "num_enum", + "oliphaunt-wasmer", + "serde", + "time", + "tracing", + "wai-bindgen-gen-core", + "wai-bindgen-gen-rust", + "wai-bindgen-gen-rust-wasm", + "wai-bindgen-rust", + "wai-parser", + "wasmer-derive", + "wasmer-types", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "openssl" +version = "0.10.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" +dependencies = [ + "bitflags 2.13.2", + "cfg-if", + "foreign-types", + "libc", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-sys" +version = "0.9.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -3189,6 +3973,16 @@ dependencies = [ "quote", ] +[[package]] +name = "proc-macro-error-attr3" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e564d14133360e1ae169ffde5da25881b5fa47261665b8e5713c212c27799da" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "proc-macro-error2" version = "2.0.1" @@ -3201,6 +3995,18 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "proc-macro-error3" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0d4471b3436c22106b21913b1dda531558918ae9b7ec55d58aa84b43552233" +dependencies = [ + "proc-macro-error-attr3", + "proc-macro2", + "quote", + "syn 3.0.5", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -3264,6 +4070,63 @@ dependencies = [ "unicase", ] +[[package]] +name = "quinn" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4051e23e9185c255a7e33ef59cdbca87a22d359052eecd22fc6b901fb37d9d11" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e750cca55fe4f0439a15d0bb529da9651e79993e8e72c61a899a36d462befbe" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.4.2", + "lru-slab", + "rand 0.10.1", + "rand_pcg", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af66907df18639dcf4db56ca65490cabc4b27a97dbadd96f2926cca73298f016" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.61.2", +] + [[package]] name = "quote" version = "1.0.45" @@ -3370,6 +4233,15 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + [[package]] name = "rangemap" version = "1.7.1" @@ -3445,6 +4317,20 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "regalloc2" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "757712e8e61590d6d4f5d563483755538b5aa13467837a3b41cd9832509a7f85" +dependencies = [ + "allocator-api2", + "bumpalo", + "hashbrown 0.17.1", + "log", + "rustc-hash", + "smallvec", +] + [[package]] name = "regex" version = "1.12.3" @@ -3507,6 +4393,64 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51743d3e274e2b18df81c4dc6caf8a5b8e15dbe799e0dca05c7617380094e884" +[[package]] +name = "reqwest" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a1cfa75cc186dd73d5818e510e042e40927bccc9c236b061cea97e1eb08029" +dependencies = [ + "base64 0.23.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "native-tls", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tokio-rustls", + "tokio-util", + "tower", + "tower-http 0.6.11", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rkyv" version = "0.8.16" @@ -3595,6 +4539,81 @@ dependencies = [ "rustix", ] +[[package]] +name = "rustls" +version = "0.23.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1167586491e2b18b8bfbb293e8180ec17c201c4f076d7cb3070ca964e7598f98" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eec689c0bc40ff2458a5977b6619cb718087084a18e02a131c599b62d05e1a5f" + +[[package]] +name = "rustls-webpki" +version = "0.103.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -3648,6 +4667,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "schemars" version = "1.2.1" @@ -3701,6 +4729,29 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.13.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "self_cell" version = "1.2.2" @@ -3738,6 +4789,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -3872,12 +4933,32 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + [[package]] name = "simd-adler32" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "simd_cesu8" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +dependencies = [ + "rustc_version", + "simdutf8", +] + [[package]] name = "simdutf8" version = "0.1.5" @@ -4134,6 +5215,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1245acf80236b4a0d99e9216532102a1670950e79c70b980b607c2040966e83d" dependencies = [ "cpp_demangle 0.4.5", + "msvc-demangler", "rustc-demangle", "symbolic-common", ] @@ -4171,6 +5253,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.13.2" @@ -4336,6 +5427,7 @@ dependencies = [ "libc", "mio", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.61.2", @@ -4352,6 +5444,16 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-postgres" version = "0.7.18" @@ -4378,6 +5480,31 @@ dependencies = [ "whoami 2.1.2", ] +[[package]] +name = "tokio-rustls" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-serde" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf600e7036b17782571dd44fa0a5cea3c82f60db5137f774a325a76a0d6852b" +dependencies = [ + "bincode 1.3.3", + "bytes", + "educe", + "futures-core", + "futures-sink", + "pin-project", + "serde", +] + [[package]] name = "tokio-stream" version = "0.1.18" @@ -4478,6 +5605,71 @@ version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags 2.13.2", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-http" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a05a66a4fdd61cbbe0a1d755ffe0ca6aba159dd4820936a0ff8a8278245b9c" +dependencies = [ + "bitflags 2.13.2", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "percent-encoding", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.44" @@ -4526,6 +5718,12 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "twox-hash" version = "2.1.2" @@ -4720,6 +5918,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "unty" version = "0.0.4" @@ -4805,6 +6009,7 @@ dependencies = [ "tracing", "virtual-mio", "wasmer-package", + "web-time", "webc", ] @@ -4834,7 +6039,7 @@ dependencies = [ "anyhow", "async-trait", "base64 0.23.1", - "bincode", + "bincode 2.0.1", "bytecheck", "bytes", "derive_more", @@ -4850,6 +6055,8 @@ dependencies = [ "socket2", "thiserror", "tokio", + "tokio-serde", + "tokio-util", "tracing", "virtual-mio", ] @@ -4942,6 +6149,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec4cdd0dd910afe868b7ef477227d8d538b46b3075031afee8a9f2acb0a2ed0b" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -5003,6 +6219,16 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.122" @@ -5064,6 +6290,16 @@ dependencies = [ "wasmparser 0.259.0", ] +[[package]] +name = "wasm-encoder" +version = "0.261.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2608e8bb6d67fd68f5a8d0eb1363d6e7bcbc1f8ded5a0bd3a1e382462b876b22" +dependencies = [ + "leb128fmt", + "wasmparser 0.261.0", +] + [[package]] name = "wasm-metadata" version = "0.244.0" @@ -5076,6 +6312,19 @@ dependencies = [ "wasmparser 0.244.0", ] +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmer" version = "7.5.0" @@ -5129,6 +6378,7 @@ dependencies = [ "leb128", "libc", "libwild", + "loupe", "macho-unwind-info", "memmap2 0.9.10", "more-asserts", @@ -5139,6 +6389,8 @@ dependencies = [ "region", "rkyv", "self_cell", + "serde", + "serde_bytes", "shared-buffer", "smallvec", "strum", @@ -5152,6 +6404,29 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "wasmer-compiler-cranelift" +version = "7.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3554998433d7f02c7154b663e3f8f2337b3476968ef341ce1d527eeef2bb6" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "indexmap", + "itertools 0.15.0", + "leb128", + "more-asserts", + "object 0.40.0", + "rayon", + "regalloc2", + "smallvec", + "target-lexicon", + "tracing", + "wasmer-compiler", + "wasmer-types", +] + [[package]] name = "wasmer-compiler-llvm" version = "7.5.0" @@ -5180,6 +6455,28 @@ dependencies = [ "wasmer-vm", ] +[[package]] +name = "wasmer-compiler-singlepass" +version = "7.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece6505e2270f1083d4e677a7cf36d11870f682c3b05fd0a4687094ef85fdd5b" +dependencies = [ + "byteorder", + "dynasm", + "dynasmrt", + "enumset", + "fixedbitset", + "gimli 0.34.0", + "itertools 0.15.0", + "more-asserts", + "object 0.40.0", + "rayon", + "smallvec", + "target-lexicon", + "wasmer-compiler", + "wasmer-types", +] + [[package]] name = "wasmer-config" version = "0.705.0" @@ -5224,7 +6521,7 @@ dependencies = [ "anyhow", "async-trait", "base64 0.23.1", - "bincode", + "bincode 2.0.1", "bytecheck", "bytes", "derive_more", @@ -5284,10 +6581,12 @@ dependencies = [ "hex", "indexmap", "itertools 0.15.0", + "loupe", "more-asserts", "num_enum", "rkyv", "serde", + "serde_bytes", "sha2 0.11.0", "target-lexicon", "thiserror", @@ -5313,12 +6612,14 @@ dependencies = [ "itertools 0.15.0", "libc", "libunwind", + "loupe", "mach2", "more-asserts", "parking_lot", "region", "rustversion", "scopeguard", + "serde", "thiserror", "wasmer-types", "windows-sys 0.61.2", @@ -5333,7 +6634,7 @@ dependencies = [ "anyhow", "async-trait", "base64 0.23.1", - "bincode", + "bincode 2.0.1", "blake3", "bus", "bytecheck", @@ -5469,6 +6770,63 @@ dependencies = [ "serde", ] +[[package]] +name = "wasmtime-internal-core" +version = "49.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f24a558f82f07e4e76a8b848288901d9f663e4e95c25f6d73627daca5f4ce93" +dependencies = [ + "hashbrown 0.17.1", + "libm", +] + +[[package]] +name = "wast" +version = "261.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "776443145731a4062e5b0d392892a2005909b6ab72d9fdc3cad53dd1a714e44a" +dependencies = [ + "bumpalo", + "leb128fmt", + "memchr", + "unicode-width", + "wasm-encoder 0.261.0", +] + +[[package]] +name = "wat" +version = "1.261.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7b4d1a49ea73a8f3326e74e3a05db667001b16bd1035ed3356fc1a0ed05ca7f" +dependencies = [ + "wast", +] + +[[package]] +name = "wcgi" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3977728575e8a79833db34f835b810dd7e1affdec0fa18cf141f060421cade6a" +dependencies = [ + "http", + "http-serde", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "wcgi-host" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d78a0358eebb970afcacc158f4172d2b7355d37572b350f1ed46dc44fe2e464" +dependencies = [ + "http", + "serde", + "tokio", + "wcgi", +] + [[package]] name = "web-sys" version = "0.3.99" @@ -5479,6 +6837,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webc" version = "12.0.1" @@ -5507,6 +6875,15 @@ dependencies = [ "url", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "weedle2" version = "5.0.0" @@ -5579,12 +6956,65 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -5594,6 +7024,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -5874,13 +7313,13 @@ version = "0.0.0" dependencies = [ "anyhow", "async-trait", + "oliphaunt-wasmer", "serde", "serde_json", "sha2 0.10.9", "tar", "toml 0.9.12+spec-1.1.0", "walkdir", - "wasmer", "wasmer-types", "wasmparser 0.261.0", "zstd 0.13.3", @@ -5956,6 +7395,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + [[package]] name = "zerotrie" version = "0.2.4" diff --git a/Cargo.toml b/Cargo.toml index 36dee7f08..7ff63baff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,12 @@ members = [ "src/native/runtime/packaging", "src/native/postgres-tools/tests", "src/wasix/runtime/tools/xtask", + "src/wasix/runtime/engine/crates/wasmer", + "src/wasix/runtime/engine/crates/wasmer-wasix-types", + "src/wasix/runtime/engine/crates/wasmer-journal", + "src/wasix/runtime/engine/crates/wasmer-wasix", + "src/wasix/runtime/engine/crates/v8-windows-x64-msvc", + ] resolver = "3" diff --git a/release-please-config.json b/release-please-config.json index 7d7334c6e..eb54d8d29 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -311,6 +311,31 @@ "type": "toml", "path": "crates/aot/x86_64-unknown-linux-gnu/Cargo.toml", "jsonpath": "$.package.version" + }, + { + "type": "toml", + "path": "engine/crates/wasmer/Cargo.toml", + "jsonpath": "$.package.version" + }, + { + "type": "toml", + "path": "engine/crates/wasmer-wasix/Cargo.toml", + "jsonpath": "$.package.version" + }, + { + "type": "toml", + "path": "engine/crates/wasmer-wasix-types/Cargo.toml", + "jsonpath": "$.package.version" + }, + { + "type": "toml", + "path": "engine/crates/wasmer-journal/Cargo.toml", + "jsonpath": "$.package.version" + }, + { + "type": "toml", + "path": "engine/crates/v8-windows-x64-msvc/Cargo.toml", + "jsonpath": "$.package.version" } ] }, diff --git a/src/wasix/node-addon/packages/win32-x64-msvc/package.json b/src/wasix/node-addon/packages/win32-x64-msvc/package.json index 1d3477ad7..949c82549 100644 --- a/src/wasix/node-addon/packages/win32-x64-msvc/package.json +++ b/src/wasix/node-addon/packages/win32-x64-msvc/package.json @@ -2,7 +2,7 @@ "name": "@oliphaunt/wasix-napi-win32-x64-msvc", "version": "0.2.0", "description": "Windows x64 MSVC prebuilt Oliphaunt WASIX Node-API runtime.", - "license": "MIT AND PostgreSQL AND Unicode-3.0 AND Apache-2.0", + "license": "MIT AND PostgreSQL AND Unicode-3.0 AND Apache-2.0 AND BSD-3-Clause AND BSD-2-Clause AND SunPro AND CC0-1.0 AND Zlib AND Python-2.0", "type": "commonjs", "repository": { "type": "git", diff --git a/src/wasix/node-addon/tools/check-release-assets.mts b/src/wasix/node-addon/tools/check-release-assets.mts index c479b8699..33aecb6a9 100644 --- a/src/wasix/node-addon/tools/check-release-assets.mts +++ b/src/wasix/node-addon/tools/check-release-assets.mts @@ -4,7 +4,7 @@ import { createHash } from 'node:crypto'; import { readFileSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { workspaceCarrierManifest } from './workspace-runtime-contract.mts'; +import { wasixNapiNoticeProfile, workspaceCarrierManifest } from './workspace-runtime-contract.mts'; import { finalizeHelperAssets } from '../../../../tools/packaging/finalize-helper-assets.mts'; import { inspectPlatformBinaryEntries } from '../../../../tools/packaging/platform-binary-contract.mts'; import { readPortableArchiveEntries } from '../../../../tools/packaging/portable-archive.mts'; @@ -37,8 +37,6 @@ import { const PREFIX = 'check-wasix-napi-release-assets.mts'; const PRODUCT = 'oliphaunt-wasix-napi'; const KIND = 'wasix-napi-addon'; -const PROFILE = 'wasix-napi-addon'; -const PACKAGE_LICENSE = releaseProfilePackageLicense(PROFILE).spdx; const BINARY = 'oliphaunt_wasix_napi.node'; const PRODUCT_MANIFEST = JSON.parse( readFileSync(path.join(ROOT, 'src/wasix/node-addon/package.json'), 'utf8'), @@ -217,9 +215,10 @@ export function assertWasixNapiCarrierManifest( if (manifest.name !== target.npmPackage || manifest.version !== version) { throw new Error(`${label} must identify ${target.npmPackage}@${version}`); } - if (manifest.license !== PACKAGE_LICENSE) { + const packageLicense = releaseProfilePackageLicense(wasixNapiNoticeProfile(target.target)).spdx; + if (manifest.license !== packageLicense) { throw new Error( - `${label} package license must be ${PACKAGE_LICENSE}, got ${JSON.stringify(manifest.license)}`, + `${label} package license must be ${packageLicense}, got ${JSON.stringify(manifest.license)}`, ); } if ( @@ -328,7 +327,11 @@ export function assertWasixNapiPlatformEntries( } function assertPayload(entries, { prefix = '', label, target, version, npm = false, contract }) { - assertReleaseNoticesInEntries(entries, { profile: PROFILE, prefix, label }); + assertReleaseNoticesInEntries(entries, { + profile: wasixNapiNoticeProfile(target.target), + prefix, + label, + }); const member = (name) => (prefix ? `${prefix}/${name}` : name); const provenance = archiveJson(entries, member('artifact-provenance.json'), label); if ( diff --git a/src/wasix/node-addon/tools/package-platform.mts b/src/wasix/node-addon/tools/package-platform.mts index 19165f3e9..4a3249eec 100755 --- a/src/wasix/node-addon/tools/package-platform.mts +++ b/src/wasix/node-addon/tools/package-platform.mts @@ -4,7 +4,7 @@ import { createHash } from 'node:crypto'; import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { workspaceCarrierManifest } from './workspace-runtime-contract.mts'; +import { wasixNapiNoticeProfile, workspaceCarrierManifest } from './workspace-runtime-contract.mts'; import { archiveDirectory } from '../../../../tools/packaging/archive-directory.mts'; import { readPortableArchiveEntries } from '../../../../tools/packaging/portable-archive.mts'; @@ -129,7 +129,7 @@ async function main() { const packagePrebuilds = path.join(packageWork, 'prebuilds'); mkdirSync(packagePrebuilds, { recursive: true }); cpSync(path.join(prebuildDirectory, BINARY), path.join(packagePrebuilds, BINARY)); - stageReleaseNotices(packageWork, { profile: 'wasix-napi-addon' }); + stageReleaseNotices(packageWork, { profile: wasixNapiNoticeProfile(target) }); const windowsRuntimeNames = target === 'windows-x64-msvc' ? stageWindowsVcRuntime({ @@ -175,7 +175,7 @@ async function main() { mkdirSync(releaseStage, { recursive: true }); mkdirSync(releaseAssets, { recursive: true }); cpSync(path.join(prebuildDirectory, BINARY), path.join(releaseStage, BINARY)); - stageReleaseNotices(releaseStage, { profile: 'wasix-napi-addon' }); + stageReleaseNotices(releaseStage, { profile: wasixNapiNoticeProfile(target) }); cpSync( path.join(packageWork, 'artifact-provenance.json'), path.join(releaseStage, 'artifact-provenance.json'), diff --git a/src/wasix/node-addon/tools/workspace-runtime-contract.mts b/src/wasix/node-addon/tools/workspace-runtime-contract.mts index f3af2e276..32fd20e8b 100644 --- a/src/wasix/node-addon/tools/workspace-runtime-contract.mts +++ b/src/wasix/node-addon/tools/workspace-runtime-contract.mts @@ -1,10 +1,15 @@ import { readFileSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; +import { releaseProfilePackageLicense } from '../../../../tools/packaging/release-notices.mts'; import { readCargoPackageNameVersion } from '../../../../tools/packaging/cargo-source-package.mts'; const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../..'); +export function wasixNapiNoticeProfile(target) { + return target === 'windows-x64-msvc' ? 'wasix-napi-addon-windows' : 'wasix-napi-addon'; +} + export function workspaceRuntimeVersion(root = ROOT) { const version = readFileSync(path.join(root, 'src/wasix/runtime/VERSION'), 'utf8').trim(); if (!/^(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)$/u.test(version)) { @@ -23,6 +28,7 @@ export function workspaceCarrierManifest(manifest, declaredContract, root = ROOT ).version; return { ...manifest, + license: releaseProfilePackageLicense(wasixNapiNoticeProfile(manifest.oliphaunt.target)).spdx, oliphaunt: { ...manifest.oliphaunt, runtimeVersion, diff --git a/src/wasix/postgres-tools/crates/aot/x86_64-pc-windows-msvc/build-support.rs b/src/wasix/postgres-tools/crates/aot/x86_64-pc-windows-msvc/build-support.rs index e8ca99b64..314deb6d9 100644 --- a/src/wasix/postgres-tools/crates/aot/x86_64-pc-windows-msvc/build-support.rs +++ b/src/wasix/postgres-tools/crates/aot/x86_64-pc-windows-msvc/build-support.rs @@ -144,7 +144,7 @@ fn write_generated_aot(out: &Path, target: &str, artifact_dir: &Path) { let Some(file_name) = file.file_name().and_then(|name| name.to_str()) else { continue; }; - let Some(stem) = file_name.strip_suffix("-llvm-opta.bin.zst") else { + let Some(stem) = file_name.strip_suffix("-v8.bin.zst") else { continue; }; let artifact_name = artifact_name_from_file_stem(stem); @@ -162,7 +162,7 @@ fn write_generated_aot(out: &Path, target: &str, artifact_dir: &Path) { let text = format!( "pub const TARGET_TRIPLE: &str = {:?};\n\ - pub const ENGINE: &str = \"llvm-opta\";\n\ + pub const ENGINE: &str = \"v8\";\n\ pub const HAS_EMBEDDED_AOT: bool = true;\n\ pub const MANIFEST_JSON: &str = include_str!({});\n\ #[rustfmt::skip]\n\ @@ -188,11 +188,11 @@ fn write_generated_aot(out: &Path, target: &str, artifact_dir: &Path) { fn write_source_only_aot(out: &Path, target: &str) { let manifest = format!( - "{{\"format-version\":1,\"target-triple\":{target:?},\"engine\":\"llvm-opta\",\"wasmer-version\":\"7.5.0\",\"wasmer-wasix-version\":\"0.705.0\",\"artifacts\":[]}}" + "{{\"format-version\":1,\"target-triple\":{target:?},\"engine\":\"v8\",\"wasmer-version\":\"7.5.0\",\"wasmer-wasix-version\":\"0.705.0\",\"artifacts\":[]}}" ); let text = format!( "pub const TARGET_TRIPLE: &str = {target:?};\n\ - pub const ENGINE: &str = \"llvm-opta\";\n\ + pub const ENGINE: &str = \"v8\";\n\ pub const HAS_EMBEDDED_AOT: bool = false;\n\ pub const MANIFEST_JSON: &str = r#\"{manifest}\"#;\n\ pub fn artifact_bytes(_name: &str) -> Option<&'static [u8]> {{ None }}\n" @@ -233,7 +233,7 @@ fn write_core_aot_manifest(source: &Path, destination: &Path) -> Vec { serde_json::from_str(&text).expect("parse generated WASIX AOT manifest"); assert_eq!( manifest.get("engine").and_then(serde_json::Value::as_str), - Some("llvm-opta"), + Some("v8"), "stale WASIX AOT profile; rebuild artifacts before compiling the carrier" ); let artifacts = manifest diff --git a/src/wasix/runtime/crates/aot/x86_64-pc-windows-msvc/build-support.rs b/src/wasix/runtime/crates/aot/x86_64-pc-windows-msvc/build-support.rs index 7d317c193..7b19a128e 100644 --- a/src/wasix/runtime/crates/aot/x86_64-pc-windows-msvc/build-support.rs +++ b/src/wasix/runtime/crates/aot/x86_64-pc-windows-msvc/build-support.rs @@ -138,7 +138,7 @@ fn write_generated_aot(out: &Path, target: &str, artifact_dir: &Path) { let Some(file_name) = file.file_name().and_then(|name| name.to_str()) else { continue; }; - let Some(stem) = file_name.strip_suffix("-llvm-opta.bin.zst") else { + let Some(stem) = file_name.strip_suffix("-v8.bin.zst") else { continue; }; let artifact_name = artifact_name_from_file_stem(stem); @@ -156,7 +156,7 @@ fn write_generated_aot(out: &Path, target: &str, artifact_dir: &Path) { let text = format!( "pub const TARGET_TRIPLE: &str = {:?};\n\ - pub const ENGINE: &str = \"llvm-opta\";\n\ + pub const ENGINE: &str = \"v8\";\n\ pub const HAS_EMBEDDED_AOT: bool = true;\n\ pub const MANIFEST_JSON: &str = include_str!({});\n\ #[rustfmt::skip]\n\ @@ -182,11 +182,11 @@ fn write_generated_aot(out: &Path, target: &str, artifact_dir: &Path) { fn write_source_only_aot(out: &Path, target: &str) { let manifest = format!( - "{{\"format-version\":1,\"target-triple\":{target:?},\"engine\":\"llvm-opta\",\"wasmer-version\":\"7.5.0\",\"wasmer-wasix-version\":\"0.705.0\",\"artifacts\":[]}}" + "{{\"format-version\":1,\"target-triple\":{target:?},\"engine\":\"v8\",\"wasmer-version\":\"7.5.0\",\"wasmer-wasix-version\":\"0.705.0\",\"artifacts\":[]}}" ); let text = format!( "pub const TARGET_TRIPLE: &str = {target:?};\n\ - pub const ENGINE: &str = \"llvm-opta\";\n\ + pub const ENGINE: &str = \"v8\";\n\ pub const HAS_EMBEDDED_AOT: bool = false;\n\ pub const MANIFEST_JSON: &str = r#\"{manifest}\"#;\n\ pub fn artifact_bytes(_name: &str) -> Option<&'static [u8]> {{ None }}\n" @@ -227,7 +227,7 @@ fn write_core_aot_manifest(source: &Path, destination: &Path) -> Vec { serde_json::from_str(&text).expect("parse generated WASIX AOT manifest"); assert_eq!( manifest.get("engine").and_then(serde_json::Value::as_str), - Some("llvm-opta"), + Some("v8"), "stale WASIX AOT profile; rebuild artifacts before compiling the carrier" ); let artifacts = manifest diff --git a/src/wasix/runtime/engine/README.md b/src/wasix/runtime/engine/README.md new file mode 100644 index 000000000..9198e0dbe --- /dev/null +++ b/src/wasix/runtime/engine/README.md @@ -0,0 +1,34 @@ +# Native WASIX engine + +This directory owns the private Wasmer/WASIX source family used by the Rust SDK +and Node addon. Its package versions follow `liboliphaunt-wasix`; upstream versions +and immutable input digests live in `source.toml`. The browser engine remains a +separate toolchain. + +Linux and macOS use the existing LLVM producer and headless loader. Windows uses +V8 because Wasmer 7.5 no longer supports its LLVM backend there. All consumers +receive precompiled artifacts and the Windows engine DLL through their normal +SDK packages. Runtime loading never compiles a missing guest or extension. + +The desktop x64 minimum is SSE4.1. Windows artifacts contain genuine native +caches for CPUs with and without CET, compiled with the same pinned DLL and +flags. V8 validates the original cache headers and selects a compatible profile; +we do not rewrite CPU masks or fall back to compilation. + +The Wasmer patch owns C API handles, preserves borrowed call arguments, copies +memory views across growth, and coordinates terminal interrupts with store +lifetimes. The WASIX patch enforces cached module loading and terminal shutdown. +These are internal compatibility patches, not a promise to support every V8 +backend API exposed upstream. + +Use `moon run liboliphaunt-wasix:engine-sources` to replay the pinned source +patches. The Windows host workflow produces the DLL and baseline caches before +normal AOT, runtime, extension, and SDK checks. Packaging freezes those same-run +inputs using the existing deterministic Cargo carrier helpers, verifies source +and byte identities, and includes the upstream notices. Release assembly +consumes these outputs without building an engine. + +Qualification must cover all desktop targets, the complete extension catalog, +and installed Rust/npm consumers. Research artifacts do not qualify a production +commit. Changes to source pins, patches, flags, or DLL bindings require fresh +native caches and qualification. diff --git a/src/wasix/runtime/engine/contract.mts b/src/wasix/runtime/engine/contract.mts new file mode 100644 index 000000000..bb6be37fe --- /dev/null +++ b/src/wasix/runtime/engine/contract.mts @@ -0,0 +1,12 @@ +export const ENGINE_SOURCE_CRATES = Object.freeze([ + 'wasmer', + 'wasmer-wasix-types', + 'wasmer-journal', + 'wasmer-wasix', +]); + +export const ENGINE_PAYLOAD_PACKAGE = 'oliphaunt-wasmer-v8-windows-x64-msvc'; +export const ENGINE_CARGO_PACKAGES = Object.freeze([ + ...ENGINE_SOURCE_CRATES.map((name) => `oliphaunt-${name}`), + ENGINE_PAYLOAD_PACKAGE, +]); diff --git a/src/wasix/runtime/engine/crates/v8-windows-x64-msvc/Cargo.toml b/src/wasix/runtime/engine/crates/v8-windows-x64-msvc/Cargo.toml new file mode 100644 index 000000000..818fd0c8b --- /dev/null +++ b/src/wasix/runtime/engine/crates/v8-windows-x64-msvc/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "oliphaunt-wasmer-v8-windows-x64-msvc" +version = "0.3.1" +edition = "2024" +rust-version = "1.96" +description = "Bundled Windows V8 engine for the Oliphaunt WASIX runtime" +repository = "https://github.com/f0rr0/oliphaunt" +license = "MIT AND BSD-3-Clause AND BSD-2-Clause AND Apache-2.0 AND Unicode-3.0 AND SunPro AND CC0-1.0 AND Zlib AND Python-2.0" +publish = false + +[lib] +path = "src/lib.rs" diff --git a/src/wasix/runtime/engine/crates/v8-windows-x64-msvc/build.rs b/src/wasix/runtime/engine/crates/v8-windows-x64-msvc/build.rs new file mode 100644 index 000000000..65f30c997 --- /dev/null +++ b/src/wasix/runtime/engine/crates/v8-windows-x64-msvc/build.rs @@ -0,0 +1,12 @@ +use std::{env, fs, path::PathBuf}; + +fn main() { + let root = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); + let source = root.join( + "../../../../../../target/oliphaunt-wasix/engine/windows-x64-msvc/oliphaunt_wee8.dll", + ); + println!("cargo:rerun-if-changed={}", source.display()); + let output = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + fs::copy(&source, output.join("oliphaunt_wee8.dll")) + .expect("build the Windows engine through liboliphaunt-wasix:engine-build first"); +} diff --git a/src/wasix/runtime/engine/crates/v8-windows-x64-msvc/src/lib.rs b/src/wasix/runtime/engine/crates/v8-windows-x64-msvc/src/lib.rs new file mode 100644 index 000000000..9adc832ae --- /dev/null +++ b/src/wasix/runtime/engine/crates/v8-windows-x64-msvc/src/lib.rs @@ -0,0 +1,7 @@ +//! Internal build carrier. Release packaging supplies the same verified engine bytes. +pub fn write_dll(path: &std::path::Path) -> std::io::Result<()> { + std::fs::write( + path, + include_bytes!(concat!(env!("OUT_DIR"), "/oliphaunt_wee8.dll")), + ) +} diff --git a/src/wasix/runtime/engine/crates/wasmer-journal/Cargo.toml b/src/wasix/runtime/engine/crates/wasmer-journal/Cargo.toml new file mode 100644 index 000000000..ec7edfbc1 --- /dev/null +++ b/src/wasix/runtime/engine/crates/wasmer-journal/Cargo.toml @@ -0,0 +1,120 @@ +[package] +edition = "2024" +rust-version = "1.96" +name = "oliphaunt-wasmer-journal" +version = "0.3.1" +authors = ["Wasmer Engineering Team "] +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Journaling functionality used by Wasmer to save and restore WASM state" +homepage = "https://wasmer.io/" +readme = false +license = "MIT" +repository = "https://github.com/wasmerio/wasmer" + +[package.metadata.docs.rs] +features = [ + "wasmer/sys", + "wasmer/compiler", +] +rustc-args = [ + "--cfg", + "docsrs", +] + +[features] +default = [ + "log-file", + "wasmer/sys-default", +] +log-file = ["shared-buffer"] + +[lib] +name = "wasmer_journal" +path = "upstream/src/lib.rs" + +[dependencies.anyhow] +version = "1.0" + +[dependencies.async-trait] +version = "0.1.68" + +[dependencies.base64] +version = "0.23.0" + +[dependencies.bincode] +version = "=2.0.1" + +[dependencies.bytecheck] +version = "0.8.2" + +[dependencies.bytes] +version = "1.11.1" + +[dependencies.derive_more] +version = "2.0.1" +features = [ + "debug", + "display", +] + +[dependencies.lz4_flex] +version = "0.14.0" + +[dependencies.num_enum] +version = "0.7.3" + +[dependencies.rkyv] +version = "0.8.13" +features = [ + "indexmap-2", + "bytes-1", +] + +[dependencies.serde] +version = "1" +features = ["derive"] +default-features = false + +[dependencies.serde_json] +version = "1" + +[dependencies.shared-buffer] +version = "0.1.4" +optional = true + +[dependencies.thiserror] +version = "2.0.17" + +[dependencies.tracing] +version = "0.1" +default-features = false + +[dependencies.virtual-fs] +version = "0.705.0" +default-features = false + +[dependencies.virtual-net] +version = "0.705.0" +features = ["rkyv"] +default-features = false + +[dependencies.wasmer] +package = "oliphaunt-wasmer" +path = "../wasmer" +version = "*" +default-features = false + +[dependencies.wasmer-config] +version = "=0.705.0" +default-features = false + +[dependencies.wasmer-wasix-types] +package = "oliphaunt-wasmer-wasix-types" +path = "../wasmer-wasix-types" +version = "*" +features = ["enable-serde"] diff --git a/src/wasix/runtime/engine/crates/wasmer-wasix-types/Cargo.toml b/src/wasix/runtime/engine/crates/wasmer-wasix-types/Cargo.toml new file mode 100644 index 000000000..11eb9168a --- /dev/null +++ b/src/wasix/runtime/engine/crates/wasmer-wasix-types/Cargo.toml @@ -0,0 +1,107 @@ +[package] +edition = "2024" +rust-version = "1.96" +name = "oliphaunt-wasmer-wasix-types" +version = "0.3.1" +authors = ["Wasmer Engineering Team "] +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "WASI and WASIX types for Wasmer WebAssembly runtime" +homepage = "https://wasmer.io/" +readme = "upstream/README.md" +keywords = [ + "wasm", + "webassembly", + "wasi", + "sandbox", + "ABI", +] +categories = [ + "wasm", + "os", +] +license = "MIT" +repository = "https://github.com/wasmerio/wasmer" + +[package.metadata.docs.rs] +features = [ + "wasmer/sys", + "wasmer/cranelift", +] +rustc-args = [ + "--cfg", + "docsrs", +] + +[features] +enable-serde = [ + "serde", + "wasmer-types/serde", +] + +[lib] +name = "wasmer_wasix_types" +path = "upstream/src/lib.rs" + +[dependencies.anyhow] +version = "1.0" + +[dependencies.bitflags] +version = "2.13.2" + +[dependencies.byteorder] +version = "1.3" + +[dependencies.num_enum] +version = "0.7.3" + +[dependencies.serde] +version = "1" +features = ["derive"] +optional = true +default-features = false + +[dependencies.time] +version = "0.3.47" +features = ["formatting"] +default-features = false + +[dependencies.tracing] +version = "0.1" +default-features = false + +[dependencies.wai-bindgen-gen-core] +version = "0.2.1" +default-features = false + +[dependencies.wai-bindgen-gen-rust] +version = "0.2.1" +default-features = false + +[dependencies.wai-bindgen-gen-rust-wasm] +version = "0.2.1" +default-features = false + +[dependencies.wai-bindgen-rust] +version = "0.2.1" +features = ["macros"] +default-features = false + +[dependencies.wai-parser] +version = "0.2.1" + +[dependencies.wasmer] +package = "oliphaunt-wasmer" +path = "../wasmer" +version = "*" +default-features = false + +[dependencies.wasmer-derive] +version = "=7.5.0" + +[dependencies.wasmer-types] +version = "=7.5.0" diff --git a/src/wasix/runtime/engine/crates/wasmer-wasix/Cargo.toml b/src/wasix/runtime/engine/crates/wasmer-wasix/Cargo.toml new file mode 100644 index 000000000..2720db3c8 --- /dev/null +++ b/src/wasix/runtime/engine/crates/wasmer-wasix/Cargo.toml @@ -0,0 +1,491 @@ +[package] +edition = "2024" +rust-version = "1.96" +name = "oliphaunt-wasmer-wasix" +version = "0.3.1" +authors = ["Wasmer Engineering Team "] +build = false +exclude = ["tests/wasm_tests/*/**"] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "WASI and WASIX implementation library for Wasmer WebAssembly runtime" +homepage = "https://wasmer.io/" +readme = "upstream/README.md" +keywords = [ + "wasm", + "webassembly", + "wasi", + "sandbox", + "ABI", +] +categories = [ + "wasm", + "os", +] +license = "MIT" +repository = "https://github.com/wasmerio/wasmer" + +[package.metadata.docs.rs] +features = [ + "wasmer/sys", + "wasmer/cranelift", + "sys-default", +] +rustc-args = [ + "--cfg", + "docsrs", +] + +[features] +compiler = [] +ctrlc = ["tokio/signal"] +default = ["sys-default"] +disable-all-logging = [ + "tracing/release_max_level_off", + "tracing/max_level_off", +] +enable-serde = [ + "wasmer-wasix-types/enable-serde", + "wasmer/enable-serde", +] +extra-logging = [] +host-fs = ["virtual-fs/host-fs"] +host-reqwest = ["reqwest"] +host-threads = [] +host-vnet = ["virtual-net/host-net"] +journal = [ + "tokio/fs", + "wasmer-journal/log-file", +] +js = [ + "virtual-fs/js", + "getrandom/wasm_js", + "chrono", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wasmer/js-default", + "wasmer/wasm-types-polyfill", + "wasmer/wat", + "wasmer/js-serializable-module", +] +js-default = ["js"] +llvm = ["wasmer/llvm"] +logging = ["tracing/log"] +remote-vnet = ["virtual-net/remote"] +singlepass = ["wasmer/singlepass"] +sys = [ + "webc/mmap", + "time", + "virtual-mio/sys", + "logging", + "host-fs", + "journal", + "sys-poll", + "sys-thread", + "host-vnet", + "host-threads", + "host-reqwest", + "ctrlc", + "wasmer/wat", + "wasmer/js-serializable-module", + "wasmer/sys", + "wasmer/headless", +] +sys-default = ["sys"] +sys-minimal = [ + "wasmer/sys", + "sys-thread", + "tokio/fs", +] +sys-poll = [] +sys-thread = [ + "tokio/rt", + "tokio/time", + "tokio/rt-multi-thread", + "rusty_pool", +] +test-js = [ + "js", + "wasmer/wat", +] +time = ["tokio/time"] +v8 = ["wasmer/v8"] + +[lib] +name = "wasmer_wasix" +path = "upstream/src/lib.rs" + +[dependencies.anyhow] +version = "1.0" + +[dependencies.async-trait] +version = "0.1.68" + +[dependencies.base64] +version = "0.23.0" + +[dependencies.bincode] +version = "=2.0.1" + +[dependencies.blake3] +version = "1.0" + +[dependencies.bus] +version = "2.4.1" + +[dependencies.bytecheck] +version = "0.8.2" + +[dependencies.bytes] +version = "1.11.1" + +[dependencies.chrono] +version = "0.4.38" +features = [ + "wasmbind", + "std", + "clock", +] +optional = true +default-features = false + +[dependencies.cooked-waker] +version = "5" + +[dependencies.crossbeam-channel] +version = "0.5.15" + +[dependencies.dashmap] +version = "6.1.0" + +[dependencies.derive_more] +version = "2.0.1" +features = [ + "debug", + "display", +] + +[dependencies.flate2] +version = "1.0.34" + +[dependencies.fnv] +version = "1.0.3" + +[dependencies.fs_extra] +version = "1.3.0" + +[dependencies.futures] +version = "0.3.30" + +[dependencies.getrandom] +version = "0.4.1" + +[dependencies.heapless] +version = "0.9.2" + +[dependencies.hex] +version = "0.4" + +[dependencies.http] +version = "1.4.0" + +[dependencies.http-body-util] +version = "0.1.1" +optional = true + +[dependencies.hyper] +version = "1.8.1" +features = ["server"] +optional = true + +[dependencies.hyper-util] +version = "0.1.5" +features = [ + "server", + "server-graceful", + "tokio", + "service", + "client", +] +optional = true + +[dependencies.itertools] +version = "0.15" + +[dependencies.js-sys] +version = "0.3.77" +optional = true + +[dependencies.libtest-mimic] +version = "0.8.2" + +[dependencies.linked_hash_set] +version = "0.1.6" + +[dependencies.lz4_flex] +version = "0.14.0" + +[dependencies.num_enum] +version = "0.7.3" + +[dependencies.once_cell] +version = "1.17.1" + +[dependencies.petgraph] +version = "0.8.3" + +[dependencies.pin-project] +version = "1.0.12" + +[dependencies.pin-utils] +version = "0.1.0" + +[dependencies.rand] +version = "0.10.1" + +[dependencies.rayon] +version = "1.7.0" +optional = true + +[dependencies.rkyv] +version = "0.8.13" +features = [ + "indexmap-2", + "bytes-1", +] + +[dependencies.rusty_pool] +version = "1.0.0" +optional = true + +[dependencies.semver] +version = "1.0" + +[dependencies.serde] +version = "1" +features = ["derive"] +default-features = false + +[dependencies.serde_derive] +version = "1" + +[dependencies.serde_json] +version = "1" + +[dependencies.serde_yaml] +version = "0.9.34" + +[dependencies.sha2] +version = "0.11.0" + +[dependencies.shared-buffer] +version = "0.1.4" + +[dependencies.tempfile] +version = "3.6.0" + +[dependencies.thiserror] +version = "2.0.17" + +[dependencies.tokio] +version = "1.39.0" +features = [ + "sync", + "macros", + "time", + "rt", +] +default-features = false + +[dependencies.tokio-stream] +version = "0.1" +features = ["sync"] + +[dependencies.toml] +version = "1.1.0" +features = ["preserve_order"] + +[dependencies.tower] +version = "0.5.2" +features = [ + "make", + "util", +] +optional = true + +[dependencies.tower-http] +version = "0.7.0" +features = [ + "trace", + "util", + "catch-panic", + "cors", +] +optional = true + +[dependencies.tracing] +version = "0.1" +default-features = false + +[dependencies.url] +version = "2.5" + +[dependencies.urlencoding] +version = "2" + +[dependencies.virtual-fs] +version = "0.705.0" +features = ["webc-fs"] +default-features = false + +[dependencies.virtual-mio] +version = "0.705.0" +default-features = false + +[dependencies.virtual-net] +version = "0.705.0" +features = ["rkyv"] +default-features = false + +[dependencies.waker-fn] +version = "1.1" + +[dependencies.walkdir] +version = "2.3.2" + +[dependencies.wasm-bindgen] +version = "0.2.120" +optional = true + +[dependencies.wasm-bindgen-futures] +version = "0.4.70" +optional = true + +[dependencies.wasm-encoder] +version = "0.259.0" +features = ["std"] +default-features = false + +[dependencies.wasmer] +package = "oliphaunt-wasmer" +path = "../wasmer" +version = "*" +features = ["experimental-async"] +default-features = false + +[dependencies.wasmer-config] +version = "0.705.0" + +[dependencies.wasmer-journal] +package = "oliphaunt-wasmer-journal" +path = "../wasmer-journal" +version = "*" +default-features = false + +[dependencies.wasmer-package] +version = "0.705.0" + +[dependencies.wasmer-types] +version = "=7.5.0" +default-features = false + +[dependencies.wasmer-wasix-types] +package = "oliphaunt-wasmer-wasix-types" +path = "../wasmer-wasix-types" +version = "*" +features = ["enable-serde"] + +[dependencies.wasmparser] +version = "0.259.0" +features = [ + "validate", + "features", + "simd", +] +default-features = false + +[dependencies.wcgi] +version = "0.3.0" +optional = true + +[dependencies.wcgi-host] +version = "0.3.0" +optional = true + +[dependencies.web-sys] +version = "0.3.64" +features = [ + "Request", + "RequestInit", + "Window", + "WorkerGlobalScope", + "RequestMode", + "Response", + "Headers", +] +optional = true + +[dependencies.webc] +version = "=12.0.1" +features = [ + "v2", + "v3", +] +default-features = false + +[dependencies.xxhash-rust] +version = "0.8.10" +features = ["xxh64"] +default-features = false + +[target.'cfg(all(unix, not(target_os="ios")))'.dependencies.termios] +version = "0.3" + +[target.'cfg(any(target_arch = "riscv64", target_arch = "loongarch64"))'.dependencies.reqwest] +version = "0.13.0" +features = [ + "native-tls", + "json", + "stream", + "socks", + "blocking", +] +optional = true +default-features = false + +[target.'cfg(not(any(target_arch = "riscv64", target_arch = "loongarch64")))'.dependencies.reqwest] +version = "0.13.0" +features = [ + "rustls", + "json", + "stream", + "socks", + "blocking", +] +optional = true +default-features = false + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.terminal_size] +version = "0.4.3" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.zstd] +version = "0.14.0" + +[target.'cfg(target_arch = "wasm32")'.dependencies.getrandom03] +version = "0.3.4" +features = ["wasm_js"] +package = "getrandom" + +[target."cfg(unix)".dependencies.libc] +version = "0.2.178" +default-features = false + +[target."cfg(windows)".dependencies.windows-sys] +version = "0.61.2" +features = ["Win32_System_SystemInformation"] +default-features = false diff --git a/src/wasix/runtime/engine/crates/wasmer/Cargo.toml b/src/wasix/runtime/engine/crates/wasmer/Cargo.toml new file mode 100644 index 000000000..dcc87185a --- /dev/null +++ b/src/wasix/runtime/engine/crates/wasmer/Cargo.toml @@ -0,0 +1,318 @@ +[package] +edition = "2024" +rust-version = "1.96" +name = "oliphaunt-wasmer" +version = "0.3.1" +authors = ["Wasmer Engineering Team "] +build = "build.rs" +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "High-performance WebAssembly runtime" +homepage = "https://wasmer.io/" +readme = "upstream/README.md" +keywords = [ + "wasm", + "webassembly", + "runtime", + "vm", +] +categories = ["wasm"] +license = "MIT" +repository = "https://github.com/wasmerio/wasmer" + +[package.metadata.wasm-pack.profile.release] +wasm-opt = false + +[package.metadata.docs.rs] +features = [ + "compiler", + "core", + "cranelift", + "singlepass", + "static-artifact-create", + "static-artifact-load", + "sys", + "sys-default", + "wasmer-artifact-create", + "wasmer-artifact-load", +] +rustc-args = [ + "--cfg", + "docsrs", +] + +[badges.maintenance] +status = "actively-developed" + +[features] +artifact-size = [ + "dep:loupe", + "wasmer-vm/artifact-size", + "wasmer-compiler/artifact-size", +] +baremetal = ["wasmer-vm/baremetal"] +compiler = [ + "sys", + "dep:wasmer-compiler", + "wasmer-compiler/translator", + "wasmer-compiler/compiler", +] +core = ["hashbrown"] +cranelift = [ + "compiler", + "wasmer-compiler-cranelift", +] +default = ["sys-default"] +demangle = ["dep:symbolic-demangle"] +enable-serde = [ + "wasmer-vm/enable-serde", + "wasmer-compiler/enable-serde", + "wasmer-types/enable-serde", +] +experimental-async = [ + "dep:futures", + "dep:corosensei", +] +experimental-host-interrupt = ["wasmer-vm/experimental-host-interrupt"] +headless = [] +js = [ + "wasm-bindgen", + "wasm-bindgen-futures", + "js-sys", +] +js-default = [ + "js", + "std", + "wasm-types-polyfill", +] +js-serializable-module = [] +llvm = [ + "compiler", + "wasmer-compiler-llvm", +] +sanitizer = ["wasmer-vm/sanitizer"] +singlepass = [ + "compiler", + "wasmer-compiler-singlepass", +] +static-artifact-create = ["wasmer-compiler/static-artifact-create"] +static-artifact-load = ["wasmer-compiler/static-artifact-load"] +std = [] +sys = [ + "std", + "dep:wasmer-vm", + "dep:wasmer-compiler", +] +sys-default = [ + "sys", + "wat", + "cranelift", + "demangle", +] +unsafe-cothread = ["sys"] +v8 = [ + "dep:oliphaunt-wasmer-v8-windows-x64-msvc", + "wasm-c-api", + "std", +] +v8-default = [ + "v8", + "wat", +] +wasm-c-api = ["wasm-types-polyfill"] +wasm-types-polyfill = ["wasmparser"] +wasmer-artifact-create = ["wasmer-compiler/wasmer-artifact-create"] +wasmer-artifact-load = ["wasmer-compiler/wasmer-artifact-load"] +wat = [ + "dep:wat", + "wasmparser", +] + +[lib] +name = "wasmer" +path = "upstream/src/lib.rs" + +[dependencies.bytes] +version = "1.11.1" + +[dependencies.derive_more] +version = "2.0.1" +features = [ + "debug", + "display", + "from", + "debug", + "unwrap", +] + +[dependencies.indexmap] +version = "2" + +[dependencies.loupe] +version = "0.2.0" +features = [ + "indexmap", + "enable-indexmap", +] +optional = true + +[dependencies.more-asserts] +version = "0.3.1" + +[dependencies.paste] +version = "1.0.15" + +[dependencies.shared-buffer] +version = "0.1.4" + +[dependencies.symbolic-demangle] +version = "13" +features = [ + "rust", + "cpp", + "msvc", +] +optional = true +default-features = false + +[dependencies.thiserror] +version = "2.0.17" + +[dependencies.tracing] +version = "0.1" +default-features = true + +[dependencies.wat] +version = "1.258.0" +optional = true + +[target.'cfg(all(not(target_arch = "wasm32"), target_os = "windows"))'.dependencies.windows-sys] +version = "0.61.2" +default-features = false + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.corosensei] +version = "0.3.0" +optional = true + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.dashmap] +version = "6.1.0" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.futures] +version = "0.3.30" +optional = true + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.itertools] +version = "0.15" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.js-sys] +version = "0.3.77" +optional = true + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.rkyv] +version = "0.8.13" +features = [ + "indexmap-2", + "bytes-1", +] + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.target-lexicon] +version = "0.13.3" +default-features = false + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wasm-bindgen] +version = "0.2.120" +optional = true + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wasmer-compiler] +version = "=7.5.0" +optional = true + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wasmer-compiler-cranelift] +version = "=7.5.0" +optional = true + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wasmer-compiler-llvm] +version = "=7.5.0" +optional = true + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wasmer-compiler-singlepass] +version = "=7.5.0" +optional = true + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wasmer-derive] +version = "=7.5.0" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wasmer-types] +version = "=7.5.0" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wasmer-vm] +version = "=7.5.0" +optional = true + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wasmparser] +version = "0.259.0" +features = [ + "validate", + "features", + "simd", +] +optional = true +default-features = false + +[target.'cfg(target_arch = "wasm32")'.dependencies.hashbrown] +version = "0.17.0" +optional = true + +[target.'cfg(target_arch = "wasm32")'.dependencies.js-sys] +version = "0.3.77" + +[target.'cfg(target_arch = "wasm32")'.dependencies.serde] +version = "1" +default-features = false + +[target.'cfg(target_arch = "wasm32")'.dependencies.serde-wasm-bindgen] +version = "0.6.5" + +[target.'cfg(target_arch = "wasm32")'.dependencies.target-lexicon] +version = "0.13.3" +default-features = false + +[target.'cfg(target_arch = "wasm32")'.dependencies.wasm-bindgen] +version = "0.2.120" + +[target.'cfg(target_arch = "wasm32")'.dependencies.wasm-bindgen-futures] +version = "0.4.70" +optional = true + +[target.'cfg(target_arch = "wasm32")'.dependencies.wasmer-compiler] +version = "=7.5.0" + +[target.'cfg(target_arch = "wasm32")'.dependencies.wasmer-derive] +version = "=7.5.0" + +[target.'cfg(target_arch = "wasm32")'.dependencies.wasmer-types] +version = "=7.5.0" +features = ["std"] +default-features = false + +[target.'cfg(target_arch = "wasm32")'.dependencies.wasmparser] +version = "0.259.0" +features = [ + "validate", + "features", + "simd", +] +optional = true +default-features = false + + +[target.'cfg(windows)'.dependencies.tempfile] +version = "3" + +[build-dependencies.oliphaunt-wasmer-v8-windows-x64-msvc] +version = "*" +path = "../v8-windows-x64-msvc" +optional = true diff --git a/src/wasix/runtime/engine/crates/wasmer/build.rs b/src/wasix/runtime/engine/crates/wasmer/build.rs new file mode 100644 index 000000000..aa7e1e973 --- /dev/null +++ b/src/wasix/runtime/engine/crates/wasmer/build.rs @@ -0,0 +1,14 @@ +#[cfg(feature = "v8")] +fn main() { + use std::{env, fs, path::PathBuf}; + assert_eq!(env::var("TARGET").unwrap(), "x86_64-pc-windows-msvc"); + let root = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); + let output = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let bindings = root.join("prebuilt/embedded_bindings.rs"); + println!("cargo:rerun-if-changed={}", bindings.display()); + fs::copy(bindings, output.join("v8_bindings.rs")).unwrap(); + oliphaunt_wasmer_v8_windows_x64_msvc::write_dll(&output.join("oliphaunt_wee8.dll")).unwrap(); +} + +#[cfg(not(feature = "v8"))] +fn main() {} diff --git a/src/wasix/runtime/engine/licenses/V8-LICENSES.txt b/src/wasix/runtime/engine/licenses/V8-LICENSES.txt new file mode 100644 index 000000000..0ff2be227 --- /dev/null +++ b/src/wasix/runtime/engine/licenses/V8-LICENSES.txt @@ -0,0 +1,2116 @@ +V8 13.6.233.17 / wee8 11.9.7 Windows runtime licenses + +--- LICENSE --- +Source: https://raw.githubusercontent.com/v8/v8/b0a55a7dad7f536cce1f9aaddba89894c8533946/LICENSE +SHA-256: 6ab33af8774a0f396ee3aeeb761e3229057682d6f9fa7f572e390c2cb3a6e509 + +This license applies to all parts of V8 that are not externally +maintained libraries. The externally maintained libraries used by V8 +are: + + - PCRE test suite, located in + test/mjsunit/third_party/regexp-pcre/regexp-pcre.js. This is based on the + test suite from PCRE-7.3, which is copyrighted by the University + of Cambridge and Google, Inc. The copyright notice and license + are embedded in regexp-pcre.js. + + - Layout tests, located in test/mjsunit/third_party/object-keys. These are + based on layout tests from webkit.org which are copyrighted by + Apple Computer, Inc. and released under a 3-clause BSD license. + + - Strongtalk assembler, the basis of the files assembler-arm-inl.h, + assembler-arm.cc, assembler-arm.h, assembler-ia32-inl.h, + assembler-ia32.cc, assembler-ia32.h, assembler-x64-inl.h, + assembler-x64.cc, assembler-x64.h, assembler.cc and assembler.h. + This code is copyrighted by Sun Microsystems Inc. and released + under a 3-clause BSD license. + + - Valgrind client API header, located at third_party/valgrind/valgrind.h + This is released under the BSD license. + + - The Wasm C/C++ API headers, located at third_party/wasm-api/wasm.{h,hh} + This is released under the Apache license. The API's upstream prototype + implementation also formed the basis of V8's implementation in + src/wasm/c-api.cc. + +These libraries have their own licenses; we recommend you read them, +as their terms may differ from the terms below. + +Further license information can be found in LICENSE files located in +sub-directories. + +Copyright 2014, the V8 project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT +OWNER 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. + +--- LICENSE.v8 --- +Source: https://raw.githubusercontent.com/v8/v8/b0a55a7dad7f536cce1f9aaddba89894c8533946/LICENSE.v8 +SHA-256: 4af93c12062c58058378de2397dc1c92bbff9ddfb1d583a01c84127557ce97ca + +Copyright 2006-2011, the V8 project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT +OWNER 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. + +--- LICENSE.fdlibm --- +Source: https://raw.githubusercontent.com/v8/v8/b0a55a7dad7f536cce1f9aaddba89894c8533946/LICENSE.fdlibm +SHA-256: e7115e18444dae09d17f361ddc365fb1d342640fe500796209c63f7c80dfae10 + +Copyright (C) 1993-2004 by Sun Microsystems, Inc. All rights reserved. + +Developed at SunSoft, a Sun Microsystems, Inc. business. +Permission to use, copy, modify, and distribute this +software is freely granted, provided that this notice +is preserved. + +--- LICENSE.strongtalk --- +Source: https://raw.githubusercontent.com/v8/v8/b0a55a7dad7f536cce1f9aaddba89894c8533946/LICENSE.strongtalk +SHA-256: 6a585a9f466654abc8fc0829d56b1bc987e3a073d31faa03bba37d33640a23cd + +Copyright (c) 1994-2006 Sun Microsystems 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: + +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +- Redistribution 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. + +- Neither the name of Sun Microsystems or the names of contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT OWNER 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. + +--- third_party/wasm-api/LICENSE --- +Source: https://raw.githubusercontent.com/v8/v8/b0a55a7dad7f536cce1f9aaddba89894c8533946/third_party/wasm-api/LICENSE +SHA-256: c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08 + + 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 {yyyy} {name of copyright owner} + + 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. + +--- third_party/inspector_protocol/LICENSE --- +Source: https://raw.githubusercontent.com/v8/v8/b0a55a7dad7f536cce1f9aaddba89894c8533946/third_party/inspector_protocol/LICENSE +SHA-256: 450ebb9e9e50c2809d9ca581931fc8946d8d5aa0c8c35aae9ac471342fe4a88e + +// Copyright 2016 The Chromium Authors +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT +// OWNER 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. + +--- third_party/rapidhash-v8/LICENSE --- +Source: https://raw.githubusercontent.com/v8/v8/b0a55a7dad7f536cce1f9aaddba89894c8533946/third_party/rapidhash-v8/LICENSE +SHA-256: db672df7faf793fbe28b8074fdd10822363e1525816b45c9627754885de842b0 + +/* + * rapidhash - Very fast, high quality, platform independant hashing algorithm. + * Copyright (C) 2024 Nicolas De Carli + * + * Based on 'wyhash', by Wang Yi + * + * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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. + * + * You can contact the author at: + * - rapidhash source repository: https://github.com/Nicoshev/rapidhash + */ + +--- third_party/siphash/LICENSE --- +Source: https://raw.githubusercontent.com/v8/v8/b0a55a7dad7f536cce1f9aaddba89894c8533946/third_party/siphash/LICENSE +SHA-256: 36ffd9dc085d529a7e60e1276d73ae5a030b020313e6c5408593a6ae2af39673 + +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + + +--- third_party/utf8-decoder/LICENSE --- +Source: https://raw.githubusercontent.com/v8/v8/b0a55a7dad7f536cce1f9aaddba89894c8533946/third_party/utf8-decoder/LICENSE +SHA-256: 3f712e5fbdfdbd5ee7d9b8c8152580220df55de47f4eba2f26c95c4de19ad096 + +Copyright (c) 2008-2009 Bjoern Hoehrmann + +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. + +--- third_party/v8/builtins/LICENSE --- +Source: https://raw.githubusercontent.com/v8/v8/b0a55a7dad7f536cce1f9aaddba89894c8533946/third_party/v8/builtins/LICENSE +SHA-256: b9a6d9320b8f2693e8d41e496ce56caadacaddcca9be2a64a61749278f425cf2 + +A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations, which became +Zope Corporation. In 2001, the Python Software Foundation (PSF, see +https://www.python.org/psf/) was formed, a non-profit organization +created specifically to own Python-related Intellectual Property. +Zope Corporation was a sponsoring member of the PSF. + +All Python releases are Open Source (see http://www.opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2 and above 2.1.1 2001-now PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Python Software Foundation; All +Rights Reserved" are retained in Python alone or in any derivative version +prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the Internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the Internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +--- third_party/v8/codegen/LICENSE --- +Source: https://raw.githubusercontent.com/v8/v8/b0a55a7dad7f536cce1f9aaddba89894c8533946/third_party/v8/codegen/LICENSE +SHA-256: 17e4f539024be2749ee729d1e2f01d24cef12ece8c9bf18e91a4349be29c80bf + +The MIT License (MIT) + +Copyright (c) 2017 Facebook Inc. +Copyright (c) 2017 Georgia Institute of Technology +Copyright 2019 Google LLC + +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. + +--- third_party/fp16/LICENSE --- +Source: https://raw.githubusercontent.com/v8/v8/b0a55a7dad7f536cce1f9aaddba89894c8533946/third_party/fp16/LICENSE +SHA-256: c91377adafa2f498211a4fe017788e20c38892124673860f068f2919c289193d + +The MIT License (MIT) + +Copyright (c) 2017 Facebook Inc. +Copyright (c) 2017 Georgia Institute of Technology +Copyright 2019 Google LLC + +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. + +--- abseil --- +Source: https://chromium.googlesource.com/chromium/src/third_party/abseil-cpp/+/3fbb10e80d80e3430224b75add53c47c7a711612/LICENSE?format=TEXT +SHA-256: c79a7fea0e3cac04cd43f20e7b648e5a0ff8fa5344e644b0ee09ca1162b62747 + + + Apache License + Version 2.0, January 2004 + https://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 [yyyy] [name of copyright owner] + + 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 + + https://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. + +--- icu --- +Source: https://chromium.googlesource.com/chromium/deps/icu/+/c9fb4b3a6fb54aa8c20a03bbcaa0a4a985ffd34b/LICENSE?format=TEXT +SHA-256: 17510cf7a58b4879b887ec05a45d72cf1b73544dd9ec7e72f20110ed104229ee + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2016-2023 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE 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 OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +---------------------------------------------------------------------- + +Third-Party Software Licenses + +This section contains third-party software notices and/or additional +terms for licensed third-party software components included within ICU +libraries. + +---------------------------------------------------------------------- + +ICU License - ICU 1.8.1 to ICU 57.1 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1995-2016 International Business Machines Corporation and others +All rights reserved. + +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, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, provided that the above +copyright notice(s) and this permission notice appear in all copies of +the Software and that both the above copyright notice(s) and this +permission notice appear in supporting documentation. + +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 +OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY +SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER +RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, use +or other dealings in this Software without prior written authorization +of the copyright holder. + +All trademarks and registered trademarks mentioned herein are the +property of their respective owners. + +---------------------------------------------------------------------- + +Chinese/Japanese Word Break Dictionary Data (cjdict.txt) + + # The Google Chrome software developed by Google is licensed under + # the BSD license. Other software included in this distribution is + # provided under other licenses, as set forth below. + # + # The BSD License + # http://opensource.org/licenses/bsd-license.php + # Copyright (C) 2006-2008, Google 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: + # + # Redistributions of source code must retain the above copyright notice, + # this list of conditions and the following disclaimer. + # 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. + # Neither the name of Google Inc. nor the names of its + # contributors may be used to endorse or promote products derived from + # this software without specific prior written permission. + # + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + # 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 THE COPYRIGHT OWNER 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. + # + # + # The word list in cjdict.txt are generated by combining three word lists + # listed below with further processing for compound word breaking. The + # frequency is generated with an iterative training against Google web + # corpora. + # + # * Libtabe (Chinese) + # - https://sourceforge.net/project/?group_id=1519 + # - Its license terms and conditions are shown below. + # + # * IPADIC (Japanese) + # - http://chasen.aist-nara.ac.jp/chasen/distribution.html + # - Its license terms and conditions are shown below. + # + # ---------COPYING.libtabe ---- BEGIN-------------------- + # + # /* + # * Copyright (c) 1999 TaBE Project. + # * Copyright (c) 1999 Pai-Hsiang Hsiao. + # * All rights reserved. + # * + # * Redistribution and use in source and binary forms, with or without + # * modification, are permitted provided that the following conditions + # * are met: + # * + # * . Redistributions of source code must retain the above copyright + # * notice, this list of conditions and the following disclaimer. + # * . 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. + # * . Neither the name of the TaBE Project nor the names of its + # * contributors may be used to endorse or promote products derived + # * from this software without specific prior written permission. + # * + # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE + # * REGENTS 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. + # */ + # + # /* + # * Copyright (c) 1999 Computer Systems and Communication Lab, + # * Institute of Information Science, Academia + # * Sinica. All rights reserved. + # * + # * Redistribution and use in source and binary forms, with or without + # * modification, are permitted provided that the following conditions + # * are met: + # * + # * . Redistributions of source code must retain the above copyright + # * notice, this list of conditions and the following disclaimer. + # * . 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. + # * . Neither the name of the Computer Systems and Communication Lab + # * nor the names of its contributors may be used to endorse or + # * promote products derived from this software without specific + # * prior written permission. + # * + # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE + # * REGENTS 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. + # */ + # + # Copyright 1996 Chih-Hao Tsai @ Beckman Institute, + # University of Illinois + # c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4 + # + # ---------------COPYING.libtabe-----END-------------------------------- + # + # + # ---------------COPYING.ipadic-----BEGIN------------------------------- + # + # Copyright 2000, 2001, 2002, 2003 Nara Institute of Science + # and Technology. All Rights Reserved. + # + # Use, reproduction, and distribution of this software is permitted. + # Any copy of this software, whether in its original form or modified, + # must include both the above copyright notice and the following + # paragraphs. + # + # Nara Institute of Science and Technology (NAIST), + # the copyright holders, disclaims all warranties with regard to this + # software, including all implied warranties of merchantability and + # fitness, in no event shall NAIST be liable for + # any special, indirect or consequential damages or any damages + # whatsoever resulting from loss of use, data or profits, whether in an + # action of contract, negligence or other tortuous action, arising out + # of or in connection with the use or performance of this software. + # + # A large portion of the dictionary entries + # originate from ICOT Free Software. The following conditions for ICOT + # Free Software applies to the current dictionary as well. + # + # Each User may also freely distribute the Program, whether in its + # original form or modified, to any third party or parties, PROVIDED + # that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear + # on, or be attached to, the Program, which is distributed substantially + # in the same form as set out herein and that such intended + # distribution, if actually made, will neither violate or otherwise + # contravene any of the laws and regulations of the countries having + # jurisdiction over the User or the intended distribution itself. + # + # NO WARRANTY + # + # The program was produced on an experimental basis in the course of the + # research and development conducted during the project and is provided + # to users as so produced on an experimental basis. Accordingly, the + # program is provided without any warranty whatsoever, whether express, + # implied, statutory or otherwise. The term "warranty" used herein + # includes, but is not limited to, any warranty of the quality, + # performance, merchantability and fitness for a particular purpose of + # the program and the nonexistence of any infringement or violation of + # any right of any third party. + # + # Each user of the program will agree and understand, and be deemed to + # have agreed and understood, that there is no warranty whatsoever for + # the program and, accordingly, the entire risk arising from or + # otherwise connected with the program is assumed by the user. + # + # Therefore, neither ICOT, the copyright holder, or any other + # organization that participated in or was otherwise related to the + # development of the program and their respective officials, directors, + # officers and other employees shall be held liable for any and all + # damages, including, without limitation, general, special, incidental + # and consequential damages, arising out of or otherwise in connection + # with the use or inability to use the program or any product, material + # or result produced or otherwise obtained by using the program, + # regardless of whether they have been advised of, or otherwise had + # knowledge of, the possibility of such damages at any time during the + # project or thereafter. Each user will be deemed to have agreed to the + # foregoing by his or her commencement of use of the program. The term + # "use" as used herein includes, but is not limited to, the use, + # modification, copying and distribution of the program and the + # production of secondary products from the program. + # + # In the case where the program, whether in its original form or + # modified, was distributed or delivered to or received by a user from + # any person, organization or entity other than ICOT, unless it makes or + # grants independently of ICOT any specific warranty to the user in + # writing, such person, organization or entity, will also be exempted + # from and not be held liable to the user for any such damages as noted + # above as far as the program is concerned. + # + # ---------------COPYING.ipadic-----END---------------------------------- + +---------------------------------------------------------------------- + +Lao Word Break Dictionary Data (laodict.txt) + + # Copyright (C) 2016 and later: Unicode, Inc. and others. + # License & terms of use: http://www.unicode.org/copyright.html + # Copyright (c) 2015 International Business Machines Corporation + # and others. All Rights Reserved. + # + # Project: https://github.com/rober42539/lao-dictionary + # Dictionary: https://github.com/rober42539/lao-dictionary/laodict.txt + # License: https://github.com/rober42539/lao-dictionary/LICENSE.txt + # (copied below) + # + # This file is derived from the above dictionary version of Nov 22, 2020 + # ---------------------------------------------------------------------- + # Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell. + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions are met: + # + # Redistributions of source code must retain the above copyright notice, this + # list of conditions and the following disclaimer. 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 THE COPYRIGHT HOLDERS AND 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 THE + # COPYRIGHT HOLDER 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. + # -------------------------------------------------------------------------- + +---------------------------------------------------------------------- + +Burmese Word Break Dictionary Data (burmesedict.txt) + + # Copyright (c) 2014 International Business Machines Corporation + # and others. All Rights Reserved. + # + # This list is part of a project hosted at: + # github.com/kanyawtech/myanmar-karen-word-lists + # + # -------------------------------------------------------------------------- + # Copyright (c) 2013, LeRoy Benjamin Sharon + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: Redistributions of source code must retain the above + # copyright notice, this list of conditions and the following + # disclaimer. 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. + # + # Neither the name Myanmar Karen Word Lists, nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + # 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 THE COPYRIGHT HOLDER 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. + # -------------------------------------------------------------------------- + +---------------------------------------------------------------------- + +Time Zone Database + + ICU uses the public domain data and code derived from Time Zone +Database for its time zone support. The ownership of the TZ database +is explained in BCP 175: Procedure for Maintaining the Time Zone +Database section 7. + + # 7. Database Ownership + # + # The TZ database itself is not an IETF Contribution or an IETF + # document. Rather it is a pre-existing and regularly updated work + # that is in the public domain, and is intended to remain in the + # public domain. Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do + # not apply to the TZ Database or contributions that individuals make + # to it. Should any claims be made and substantiated against the TZ + # Database, the organization that is providing the IANA + # Considerations defined in this RFC, under the memorandum of + # understanding with the IETF, currently ICANN, may act in accordance + # with all competent court orders. No ownership claims will be made + # by ICANN or the IETF Trust on the database or the code. Any person + # making a contribution to the database or code waives all rights to + # future claims in that contribution or in the TZ Database. + +---------------------------------------------------------------------- + +Google double-conversion + +Copyright 2006-2011, the V8 project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT +OWNER 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. + +---------------------------------------------------------------------- + +File: aclocal.m4 (only for ICU4C) +Section: pkg.m4 - Macros to locate and utilise pkg-config. + + +Copyright © 2004 Scott James Remnant . +Copyright © 2012-2015 Dan Nicholson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. + +As a special exception to the GNU General Public License, if you +distribute this file as part of a program that contains a +configuration script generated by Autoconf, you may include it under +the same distribution terms that you use for the rest of that +program. + + +(The condition for the exception is fulfilled because +ICU4C includes a configuration script generated by Autoconf, +namely the `configure` script.) + +---------------------------------------------------------------------- + +File: config.guess (only for ICU4C) + + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . + +As a special exception to the GNU General Public License, if you +distribute this file as part of a program that contains a +configuration script generated by Autoconf, you may include it under +the same distribution terms that you use for the rest of that +program. This Exception is an additional permission under section 7 +of the GNU General Public License, version 3 ("GPLv3"). + + +(The condition for the exception is fulfilled because +ICU4C includes a configuration script generated by Autoconf, +namely the `configure` script.) + +---------------------------------------------------------------------- + +File: install-sh (only for ICU4C) + + +Copyright 1991 by the Massachusetts Institute of Technology + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of M.I.T. not be used in advertising or +publicity pertaining to distribution of the software without specific, +written prior permission. M.I.T. makes no representations about the +suitability of this software for any purpose. It is provided "as is" +without express or implied warranty. + +--- zlib --- +Source: https://chromium.googlesource.com/chromium/src/third_party/zlib/+/788cb3c270e8700b425c7bdca1f9ce6b0c1400a9/LICENSE?format=TEXT +SHA-256: e1cfcc55c325b3f78cf55df9664abaa066e2271dffe8213347d9fccdfbac8f2c + +version 1.2.12, March 27th, 2022 + +Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +--- simdutf --- +Source: https://chromium.googlesource.com/chromium/src/third_party/simdutf/+/40d1fa26cd5ca221605c974e22c001ca2fb12fde/LICENSE?format=TEXT +SHA-256: fc8dbc04e03ad4efc08a647ffe7f995b811a95bc04c0e85a56d5277c6593fa5f + +Copyright 2021 The simdutf authors + +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. + +--- highway --- +Source: https://chromium.googlesource.com/external/github.com/google/highway/+/00fe003dac355b979f36157f9407c7c46448958e/LICENSE?format=TEXT +SHA-256: 43070e2d4e532684de521b885f385d0841030efa2b1a20bafb76133a5e1379c1 + + 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 [yyyy] [name of copyright owner] + + 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. + +--- fast-float --- +Source: https://chromium.googlesource.com/external/github.com/fastfloat/fast_float/+/cb1d42aaa1e14b09e1452cfdef373d051b8c02a4/LICENSE-MIT?format=TEXT +SHA-256: e562f3f974ced7e69dd1db77b820b36bcf8f30377f1aa105723fba449c53c4e6 + +MIT License + +Copyright (c) 2021 The fast_float authors + +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. + +--- cpu-features --- +Source: https://chromium.googlesource.com/external/github.com/google/cpu_features/+/936b9ab5515dead115606559502e3864958f7f6e/LICENSE?format=TEXT +SHA-256: af272c1b4dd08dd61d43e001d3134119d03ca7a2b057a64f7355fdf27fbea3dd + + + 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 [yyyy] [name of copyright owner] + + 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. + +-------------------------------------------------------------------------------- +For files in the `ndk_compat` folder: +-------------------------------------------------------------------------------- + +Copyright (C) 2010 The Android Open Source Project +All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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 THE COPYRIGHT HOLDERS AND 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 THE +COPYRIGHT OWNER 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. + +--- fp16 --- +Source: https://chromium.googlesource.com/external/github.com/Maratyszcza/FP16/+/0a92994d729ff76a58f692d3028ca1b64b145d91/LICENSE?format=TEXT +SHA-256: 17e4f539024be2749ee729d1e2f01d24cef12ece8c9bf18e91a4349be29c80bf + +The MIT License (MIT) + +Copyright (c) 2017 Facebook Inc. +Copyright (c) 2017 Georgia Institute of Technology +Copyright 2019 Google LLC + +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/src/wasix/runtime/engine/licenses/Wasmer-LICENSE b/src/wasix/runtime/engine/licenses/Wasmer-LICENSE new file mode 100644 index 000000000..62bb543eb --- /dev/null +++ b/src/wasix/runtime/engine/licenses/Wasmer-LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019-present Wasmer, Inc. and its affiliates. + +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/src/wasix/runtime/engine/package.mts b/src/wasix/runtime/engine/package.mts new file mode 100644 index 000000000..3e9810abe --- /dev/null +++ b/src/wasix/runtime/engine/package.mts @@ -0,0 +1,193 @@ +import assert from 'node:assert/strict'; +import { cpSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { + fitCargoPayloadParts, + packageGeneratedCargoSource, + packagedCargoManifestText, +} from '../../../../tools/packaging/cargo-source-package.mts'; +import { canonicalGzipSync } from '../../../../tools/packaging/portable-archive.mts'; +import { releaseProfilePackageLicense } from '../../../../tools/packaging/release-notices.mts'; +import { RUST_BUILD_SCRIPT_SHA256 } from '../../../../tools/packaging/rust-build-script-sha256.mts'; +import { ROOT } from '../../../../tools/release/release-artifact-targets.mts'; +import { archiveTreeDigest, sha256File } from '../../../third-party/tools/source-fetch-core.mts'; +import { ENGINE_PAYLOAD_PACKAGE, ENGINE_SOURCE_CRATES } from './contract.mts'; + +const OWNER = path.join(ROOT, 'src/wasix/runtime/engine'); +const DLL = 'oliphaunt_wee8.dll'; + +export function isEnginePayloadPart(name: string): boolean { + return new RegExp(`^${ENGINE_PAYLOAD_PACKAGE}-part-[0-9]{3}$`, 'u').test(name); +} + +/** Freeze qualified sources and engine bytes; this step never builds the engine. */ +export function packageEngine({ + sourceRoot, + outputDir, + version, + engineDir = path.join(ROOT, 'target/oliphaunt-wasix/engine/windows-x64-msvc'), + cratesDir = path.join(OWNER, 'crates'), + expectedSourceSha = process.env.RELEASE_ARTIFACT_SHA ?? + Bun.spawnSync(['git', 'rev-parse', 'HEAD'], { cwd: ROOT }).stdout.toString().trim(), +}: { + sourceRoot: string; + outputDir: string; + version: string; + engineDir?: string; + cratesDir?: string; + expectedSourceSha?: string; +}) { + const receipt = JSON.parse(readFileSync(path.join(engineDir, 'source.json'), 'utf8')); + assert.equal( + receipt.sourceSha, + expectedSourceSha, + 'Windows engine must come from the qualified source commit', + ); + assert.deepEqual( + receipt.source, + Bun.TOML.parse(readFileSync(path.join(OWNER, 'source.toml'), 'utf8')), + 'engine source pins', + ); + assert.equal( + sha256File(path.join(engineDir, DLL)), + receipt.dllSha256, + 'frozen engine DLL digest', + ); + assert.equal( + sha256File(path.join(cratesDir, 'wasmer/prebuilt/embedded_bindings.rs')), + receipt.bindingsSha256, + 'bindings must describe the frozen DLL', + ); + for (const name of ENGINE_SOURCE_CRATES) { + assert.equal( + archiveTreeDigest(path.join(cratesDir, name, 'upstream')), + receipt.engineSources[name], + `${name}: qualified patched source digest`, + ); + } + + const packages = []; + function freeze(name: string, profile: string) { + const manifestPath = path.join(sourceRoot, name, 'Cargo.toml'); + const cratePath = packageGeneratedCargoSource(manifestPath, outputDir, { + noticeProfile: profile, + }); + const row = { + name, + manifestPath, + cratePath, + target: name.startsWith(ENGINE_PAYLOAD_PACKAGE) ? 'windows-x64-msvc' : 'portable', + kind: 'wasix-engine', + size: statSync(cratePath).size, + sha256: sha256File(cratePath), + }; + packages.push(row); + return cratePath; + } + function manifest(name: string, profile: string, extra: string) { + const directory = path.join(sourceRoot, name); + mkdirSync(path.join(directory, 'src'), { recursive: true }); + writeFileSync( + path.join(directory, 'Cargo.toml'), + `[package]\nname = ${JSON.stringify(name)}\nversion = ${JSON.stringify(version)}\nedition = "2024"\nrust-version = "1.96"\ndescription = "Internal Oliphaunt Windows engine carrier"\nrepository = "https://github.com/f0rr0/oliphaunt"\nlicense = ${JSON.stringify(releaseProfilePackageLicense(profile).spdx)}\n\n${extra}`, + ); + return directory; + } + + const compressed = canonicalGzipSync(readFileSync(path.join(engineDir, DLL))); + const parts = fitCargoPayloadParts( + (budget) => { + const result = []; + for (let offset = 0; offset < compressed.length; offset += budget) { + const name = `${ENGINE_PAYLOAD_PACKAGE}-part-${String(result.length + 1).padStart(3, '0')}`; + assert(result.length < 999, 'engine payload exceeds supported part count'); + const directory = manifest(name, 'wasix-engine-windows', ''); + writeFileSync( + path.join(directory, 'payload.gz.part'), + compressed.subarray(offset, offset + budget), + ); + writeFileSync( + path.join(directory, 'src/lib.rs'), + 'pub const PAYLOAD: &[u8] = include_bytes!("../payload.gz.part");\n', + ); + result.push(name); + } + return result; + }, + (name) => + packageGeneratedCargoSource(path.join(sourceRoot, name, 'Cargo.toml'), outputDir, { + noticeProfile: 'wasix-engine-windows', + }), + 8 * 1024 * 1024, + ); + for (const name of parts) freeze(name, 'wasix-engine-windows'); + const directory = manifest( + ENGINE_PAYLOAD_PACKAGE, + 'wasix-engine-windows', + `[build-dependencies]\nflate2 = { version = "1", default-features = false, features = ["rust_backend"] }\nsha2 = "0.10"\n${parts.map((name) => `${name} = "=${version}"`).join('\n')}\n`, + ); + cpSync( + path.join(cratesDir, 'v8-windows-x64-msvc/src/lib.rs'), + path.join(directory, 'src/lib.rs'), + ); + writeFileSync( + path.join(directory, 'build.rs'), + `use std::{env, fs, io::{self, Read}, path::{Path, PathBuf}}; +${RUST_BUILD_SCRIPT_SHA256} +fn main() { + let parts: &[&[u8]] = &[${parts.map((name) => `${name.replaceAll('-', '_')}::PAYLOAD`).join(', ')}]; + let compressed: Vec = parts.iter().flat_map(|part| part.iter().copied()).collect(); + let mut decoder = flate2::read::GzDecoder::new(&compressed[..]).take(${statSync(path.join(engineDir, DLL)).size + 1}); + let output = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR")).join(${JSON.stringify(DLL)}); + let mut file = fs::File::create(&output).expect("create bundled engine"); + let size = io::copy(&mut decoder, &mut file).expect("decompress bundled engine"); + assert_eq!(size, ${statSync(path.join(engineDir, DLL)).size}, "bundled engine byte size"); + assert_eq!(sha256_file(&output).expect("hash bundled engine"), ${JSON.stringify(receipt.dllSha256)}, "bundled engine digest"); + println!("cargo::rerun-if-changed=build.rs"); +} +`, + ); + freeze(ENGINE_PAYLOAD_PACKAGE, 'wasix-engine-windows'); + + for (const upstream of ENGINE_SOURCE_CRATES) { + const name = `oliphaunt-${upstream}`; + const destination = path.join(sourceRoot, name); + rmSync(destination, { recursive: true, force: true }); + mkdirSync(path.join(destination, 'upstream'), { recursive: true }); + cpSync(path.join(cratesDir, upstream, 'upstream/src'), path.join(destination, 'upstream/src'), { + recursive: true, + }); + const original = Bun.TOML.parse( + readFileSync(path.join(cratesDir, upstream, 'Cargo.toml'), 'utf8'), + ); + if (typeof original.package.readme === 'string') + cpSync( + path.join(cratesDir, upstream, original.package.readme), + path.join(destination, original.package.readme), + ); + cpSync(path.join(cratesDir, upstream, 'Cargo.toml'), path.join(destination, 'Cargo.toml')); + if (upstream === 'wasmer') { + cpSync(path.join(cratesDir, upstream, 'build.rs'), path.join(destination, 'build.rs')); + cpSync(path.join(cratesDir, upstream, 'prebuilt'), path.join(destination, 'prebuilt'), { + recursive: true, + }); + } + let text = packagedCargoManifestText( + readFileSync(path.join(destination, 'Cargo.toml'), 'utf8'), + ); + text = text + .replace(/^publish = false\n/gmu, '') + .replace(/^version = "[^"]+"$/mu, `version = "${version}"`); + text = text.replace( + /(package = "oliphaunt-wasmer[^"\n]*"\nversion = ")[^"]+/gu, + `$1=${version}`, + ); + text = text.replace( + /(\[build-dependencies\.oliphaunt-wasmer-v8-windows-x64-msvc\]\nversion = ")[^"]+/u, + `$1=${version}`, + ); + writeFileSync(path.join(destination, 'Cargo.toml'), text); + freeze(name, 'wasix-engine-source'); + } + return packages; +} diff --git a/src/wasix/runtime/engine/package.test.mts b/src/wasix/runtime/engine/package.test.mts new file mode 100644 index 000000000..2d5ac6618 --- /dev/null +++ b/src/wasix/runtime/engine/package.test.mts @@ -0,0 +1,136 @@ +import assert from 'node:assert/strict'; +import { randomBytes } from 'node:crypto'; +import { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import test from 'node:test'; +import { gunzipSync } from 'node:zlib'; +import { packagedCargoManifestText } from '../../../../tools/packaging/cargo-source-package.mts'; +import { preparePackagedCargoTestClosure } from '../../../../tools/packaging/cargo-package-test-closure.mts'; +import { readPortableArchiveEntries } from '../../../../tools/packaging/portable-archive.mts'; +import { assertReleaseNoticesInArchive } from '../../../../tools/packaging/release-notices.mts'; +import { archiveTreeDigest, sha256File } from '../../../third-party/tools/source-fetch-core.mts'; +import { + ENGINE_CARGO_PACKAGES, + ENGINE_PAYLOAD_PACKAGE, + ENGINE_SOURCE_CRATES, +} from './contract.mts'; +import { packageEngine } from './package.mts'; + +test('freezes a closed engine family, rejects mixed inputs, and reconstructs every DLL byte', (t) => { + const root = mkdtempSync(path.join(os.tmpdir(), 'oliphaunt-engine-package-')); + t.after(() => rmSync(root, { recursive: true, force: true })); + const cratesDir = path.join(root, 'crates'); + cpSync(path.join(import.meta.dir, 'crates'), cratesDir, { recursive: true }); + const engineDir = path.join(root, 'engine'); + mkdirSync(engineDir); + mkdirSync(path.join(cratesDir, 'wasmer/prebuilt'), { recursive: true }); + // Incompressible bytes exercise the registry size boundary and ordered parts. + const dll = randomBytes(10 * 1024 * 1024); + writeFileSync(path.join(engineDir, 'oliphaunt_wee8.dll'), dll); + writeFileSync( + path.join(cratesDir, 'wasmer/prebuilt/embedded_bindings.rs'), + '// fixture bindings\n', + ); + const sourceSha = 'b'.repeat(40); + const receipt = { + sourceSha, + source: Bun.TOML.parse(readFileSync(path.join(import.meta.dir, 'source.toml'), 'utf8')), + dllSha256: sha256File(path.join(engineDir, 'oliphaunt_wee8.dll')), + bindingsSha256: sha256File(path.join(cratesDir, 'wasmer/prebuilt/embedded_bindings.rs')), + engineSources: Object.fromEntries( + ENGINE_SOURCE_CRATES.map((name) => [ + name, + archiveTreeDigest(path.join(cratesDir, name, 'upstream')), + ]), + ), + }; + const receiptPath = path.join(engineDir, 'source.json'); + const save = () => writeFileSync(receiptPath, JSON.stringify(receipt)); + save(); + const options = { + engineDir, + cratesDir, + sourceRoot: path.join(root, 'source'), + outputDir: path.join(root, 'output'), + version: '9.8.7', + expectedSourceSha: sourceSha, + }; + assert.throws( + () => packageEngine({ ...options, expectedSourceSha: 'a'.repeat(40) }), + /qualified source commit/u, + ); + const digest = receipt.dllSha256; + receipt.dllSha256 = '0'.repeat(64); + save(); + assert.throws(() => packageEngine(options), /frozen engine DLL digest/u); + receipt.dllSha256 = digest; + save(); + const packages = packageEngine(options); + const partNames = [`${ENGINE_PAYLOAD_PACKAGE}-part-001`, `${ENGINE_PAYLOAD_PACKAGE}-part-002`]; + assert.deepEqual( + packages.map((row) => row.name).sort(), + [...ENGINE_CARGO_PACKAGES, ...partNames].sort(), + ); + const chunks = []; + for (const row of packages) { + assert.ok(row.size <= 9 * 1024 * 1024, row.name); + const prefix = `${row.name}-9.8.7`; + assertReleaseNoticesInArchive(row.cratePath, { + prefix, + profile: row.name.startsWith(ENGINE_PAYLOAD_PACKAGE) + ? 'wasix-engine-windows' + : 'wasix-engine-source', + }); + const entries = readPortableArchiveEntries(row.cratePath); + const text = entries.get(`${prefix}/Cargo.toml`).data().toString(); + const cargo = Bun.TOML.parse(text); + assert.equal(cargo.package.version, '9.8.7'); + assert.equal(text, packagedCargoManifestText(text)); + for (const table of [cargo.dependencies, cargo['build-dependencies']]) { + for (const [key, spec] of Object.entries(table ?? {})) { + assert.equal(spec.path, undefined, `${row.name}:${key} must use registry dependencies`); + if (ENGINE_CARGO_PACKAGES.includes(spec.package ?? key) || partNames.includes(key)) + assert.equal(spec.version ?? spec, '=9.8.7', `${row.name}:${key}`); + } + } + if (partNames.includes(row.name)) chunks.push(entries.get(`${prefix}/payload.gz.part`).data()); + assert.ok( + ![...entries.keys()].some((name) => /\.prepared|upstream\/tests\/|v8\.lib/u.test(name)), + ); + } + assert.deepEqual(gunzipSync(Buffer.concat(chunks)), dll); + const before = packages.map((row) => row.sha256); + assert.deepEqual( + packageEngine(options).map((row) => row.sha256), + before, + 'frozen carriers must be reproducible', + ); + + const consumer = path.join(root, 'consumer'); + const carrier = packages.find((row) => row.name === ENGINE_PAYLOAD_PACKAGE); + const manifest = preparePackagedCargoTestClosure({ + cratePath: carrier.cratePath, + scratch: consumer, + dependencyCrates: packages + .filter((row) => partNames.includes(row.name)) + .map((row) => row.cratePath), + }); + const result = Bun.spawnSync( + ['cargo', 'build', '--manifest-path', manifest, '--message-format=json'], + { cwd: consumer, env: { ...process.env, CARGO_TARGET_DIR: path.join(consumer, 'target') } }, + ); + assert.equal(result.exitCode, 0, result.stderr.toString()); + const build = result.stdout + .toString() + .trim() + .split('\n') + .map((line) => JSON.parse(line)) + .find( + (message) => + message.reason === 'build-script-executed' && + message.package_id.includes(`${ENGINE_PAYLOAD_PACKAGE}@`), + ); + assert.ok(build, 'the extracted carrier must reconstruct its own DLL'); + assert.deepEqual(readFileSync(path.join(build.out_dir, 'oliphaunt_wee8.dll')), dll); +}); diff --git a/src/wasix/runtime/engine/patches/wasmer-wasix/0001-cached-loading-and-terminal-shutdown.patch b/src/wasix/runtime/engine/patches/wasmer-wasix/0001-cached-loading-and-terminal-shutdown.patch new file mode 100644 index 000000000..2e2780fde --- /dev/null +++ b/src/wasix/runtime/engine/patches/wasmer-wasix/0001-cached-loading-and-terminal-shutdown.patch @@ -0,0 +1,187 @@ +--- a/src/os/task/process.rs ++++ b/src/os/task/process.rs +@@ -925,9 +925,9 @@ + }; + + if signal == Signal::Sigkill { +- // On kill, disable atomics to prevent threads from resuming. +- // NOTE: disable_atomics also wakes all current waiters. +- if let Err(err) = memory.disable_atomics() { ++ // Terminal cancellation: V8 interrupts whole execution contexts; ++ // Sys keeps its existing memory-specific atomics shutdown. ++ if let Err(err) = memory.terminate_execution_contexts() { + tracing::trace!( + pid=%process.pid, + error = &err as &dyn std::error::Error, +--- a/src/runners/wasi.rs ++++ b/src/runners/wasi.rs +@@ -13,10 +13,11 @@ + Runtime, WasiEnvBuilder, WasiError, WasiRuntimeError, + bin_factory::BinaryPackage, + capabilities::Capabilities, +- journal::{DynJournal, DynReadableJournal, SnapshotTrigger}, + runners::{MappedDirectory, MountedDirectory, wasi_common::CommonWasiOptions}, + runtime::task_manager::VirtualTaskManagerExt, + }; ++#[cfg(feature = "journal")] ++use crate::journal::{DynJournal, DynReadableJournal, SnapshotTrigger}; + + use super::wasi_common::{ + ExistingMountConflictBehavior, MAPPED_CURRENT_DIR_DEFAULT_PATH, MappedCommand, +@@ -364,13 +365,16 @@ + + let wasi = webc::metadata::annotations::Wasi::new(program_name); + +- let mut builder = runner.prepare_webc_env( ++ let builder = runner.prepare_webc_env( + program_name, + &wasi, + PackageOrHash::Hash(module_hash), + runtime_or_engine, + None, + )?; ++ ++ #[cfg(any(feature = "ctrlc", feature = "journal"))] ++ let mut builder = builder; + + #[cfg(feature = "ctrlc")] + { +--- a/src/runners/wasi_common.rs ++++ b/src/runners/wasi_common.rs +@@ -6,6 +6,7 @@ + }; + + use anyhow::{Context, Error}; ++#[cfg(feature = "host-fs")] + use tokio::runtime::Handle; + use virtual_fs::{ + ArcFileSystem, ExactMountConflictMode, FileSystem, MountFileSystem, OverlayFileSystem, +@@ -18,8 +19,9 @@ + bin_factory::{BinaryPackage, BinaryPackageMounts}, + capabilities::Capabilities, + fs::WasiFsRoot, +- journal::{DynJournal, DynReadableJournal, SnapshotTrigger}, + }; ++#[cfg(feature = "journal")] ++use crate::journal::{DynJournal, DynReadableJournal, SnapshotTrigger}; + + pub const MAPPED_CURRENT_DIR_DEFAULT_PATH: &str = "/home"; + +@@ -49,10 +51,15 @@ + pub(crate) is_home_mapped: bool, + pub(crate) injected_packages: Vec, + pub(crate) capabilities: Capabilities, ++ #[cfg(feature = "journal")] + pub(crate) read_only_journals: Vec>, ++ #[cfg(feature = "journal")] + pub(crate) writable_journals: Vec>, ++ #[cfg(feature = "journal")] + pub(crate) snapshot_on: Vec, ++ #[cfg(feature = "journal")] + pub(crate) snapshot_interval: Option, ++ #[cfg(feature = "journal")] + pub(crate) stop_running_after_snapshot: bool, + pub(crate) skip_stdio_during_bootstrap: bool, + pub(crate) current_dir: Option, +--- a/src/runtime/mod.rs ++++ b/src/runtime/mod.rs +@@ -353,6 +353,9 @@ + Arc::new(UnsupportedPackageLoader) + } + ++ /// Require cached modules instead of compiling on cache misses. ++ fn module_cache_only(&self) -> bool { false } ++ + /// A cache for compiled modules. + fn module_cache(&self) -> Arc { + // Return a cache that uses a thread-local variable. This isn't ideal +@@ -471,7 +474,19 @@ + + let module_cache = self.module_cache(); + +- let task = async move { load_module(&engine, &module_cache, input, on_progress).await }; ++ let cache_only = self.module_cache_only(); ++ let task = async move { ++ if cache_only { ++ let result = if let Some(progress) = on_progress { ++ module_cache.load_with_progress(input.hash(), &engine, progress).await ++ } else { ++ module_cache.load(input.hash(), &engine).await ++ }; ++ result.map_err(SpawnError::CacheError) ++ } else { ++ load_module(&engine, &module_cache, input, on_progress).await ++ } ++ }; + Box::pin(task) + } + +@@ -674,6 +689,7 @@ + pub source: Arc, + pub engine: Engine, + pub module_cache: Arc, ++ pub module_cache_only: bool, + pub tty: Option>, + #[cfg(feature = "journal")] + pub read_only_journals: Vec>, +@@ -715,6 +731,7 @@ + source: Arc::new(source), + package_loader: Arc::new(loader), + module_cache: Arc::new(module_cache::in_memory()), ++ module_cache_only: false, + #[cfg(feature = "journal")] + read_only_journals: Vec::new(), + #[cfg(feature = "journal")] +@@ -878,6 +895,8 @@ + } + + impl Runtime for PluggableRuntime { ++ fn module_cache_only(&self) -> bool { self.module_cache_only } ++ + fn networking(&self) -> &DynVirtualNetworking { + &self.networking + } +@@ -1111,6 +1130,8 @@ + } + + impl Runtime for OverriddenRuntime { ++ fn module_cache_only(&self) -> bool { self.inner.module_cache_only() } ++ + fn networking(&self) -> &DynVirtualNetworking { + if let Some(net) = self.networking.as_ref() { + net +--- a/src/runtime/task_manager/mod.rs ++++ b/src/runtime/task_manager/mod.rs +@@ -206,7 +206,8 @@ + })?; + Ok(Some(mem)) + } +- SpawnType::AttachMemory(mem) => Ok(Some(mem.attach(store))), ++ SpawnType::AttachMemory(mem) => mem.try_attach(store) ++ .map(Some).map_err(WasiThreadError::MemoryCreateFailed), + SpawnType::CreateMemory | SpawnType::NewLinkerInstanceGroup(..) => Ok(None), + } + } +--- a/src/runtime/task_manager/tokio.rs ++++ b/src/runtime/task_manager/tokio.rs +@@ -150,7 +150,8 @@ + SpawnType::CreateMemoryOfType(t) => (SpawnMemoryTypeOrStore::Type(t), None), + SpawnType::AttachMemory(mem) => { + let mut store = task.env.runtime().new_store(); +- let memory = mem.attach(&mut store); ++ let memory = mem.try_attach(&mut store) ++ .map_err(WasiThreadError::MemoryCreateFailed)?; + (SpawnMemoryTypeOrStore::StoreAndMemory(store, memory), None) + } + }; +--- a/src/state/linker/mod.rs ++++ b/src/state/linker/mod.rs +@@ -692,7 +692,7 @@ + + let mut imports = import_object_for_all_wasi_versions(&main_module, store, &func_env.env); + +- let memory = memory.attach(store); ++ let memory = memory.try_attach(store)?; + + let indirect_function_table = create_indirect_function_table( + store, diff --git a/src/wasix/runtime/engine/patches/wasmer-wasix/series b/src/wasix/runtime/engine/patches/wasmer-wasix/series new file mode 100644 index 000000000..02baccc2d --- /dev/null +++ b/src/wasix/runtime/engine/patches/wasmer-wasix/series @@ -0,0 +1 @@ +0001-cached-loading-and-terminal-shutdown.patch diff --git a/src/wasix/runtime/engine/patches/wasmer/0001-native-ownership-and-terminal-lifetimes.patch b/src/wasix/runtime/engine/patches/wasmer/0001-native-ownership-and-terminal-lifetimes.patch new file mode 100644 index 000000000..ba6d6a537 --- /dev/null +++ b/src/wasix/runtime/engine/patches/wasmer/0001-native-ownership-and-terminal-lifetimes.patch @@ -0,0 +1,1330 @@ +--- a/src/backend/v8/entities/engine.rs ++++ b/src/backend/v8/entities/engine.rs +@@ -1,4 +1,4 @@ +-//! Data types, functions and traits for `sys` runtime's `Engine` implementation. ++//! Data types, functions and traits for `v8` runtime's `Engine` implementation. + use crate::{ + BackendEngine, + backend::v8::bindings::{wasm_engine_delete, wasm_engine_new, wasm_engine_t}, +@@ -27,9 +27,15 @@ + + impl Default for CApiEngine { + fn default() -> Self { +- let engine = ENGINE +- .get_or_init(|| unsafe { std::sync::Mutex::new(EngineCapsule(wasm_engine_new())) }); +- let engine = unsafe { engine.lock().unwrap().0 }; ++ #[cfg(windows)] ++ crate::backend::v8::bindings::oliphaunt_v8_prepare() ++ .expect("prepare Windows V8 engine; use V8::try_new for delivery errors"); ++ let engine = ENGINE.get_or_init(|| unsafe { ++ #[cfg(windows)] ++ crate::backend::v8::bindings::oliphaunt_v8_configure(); ++ std::sync::Mutex::new(EngineCapsule(wasm_engine_new())) ++ }); ++ let engine = engine.lock().unwrap().0; + Self { engine } + } + } +@@ -48,6 +54,13 @@ + /// Create a new instance of the `V8` engine. + pub fn new() -> Self { + Self::default() ++ } ++ ++ /// Prepare the embedded native engine, returning retryable delivery errors. ++ pub fn try_new() -> std::io::Result { ++ #[cfg(windows)] ++ crate::backend::v8::bindings::oliphaunt_v8_prepare()?; ++ Ok(Self::default()) + } + + pub(crate) fn deterministic_id(&self) -> String { +--- a/src/backend/v8/entities/function/mod.rs ++++ b/src/backend/v8/entities/function/mod.rs +@@ -59,6 +59,12 @@ + .field("env_is_some", &self.env_handle.is_some()) + .finish() + } ++} ++ ++// Research only: each constructor transfers one unique Box to V8. ++// V8's existing host-info finalizer releases it when its isolate is disposed. ++unsafe extern "C" fn drop_callback_env(ptr: *mut c_void) { ++ unsafe { drop(Box::from_raw(ptr.cast::>())) }; + } + + impl Function { +@@ -145,8 +151,15 @@ + wasm_functype, + Some(callback), + callback_env as *mut _ as _, +- None, ++ Some(drop_callback_env::), + ) ++ }; ++ ++ // The callback has a borrowed StoreMut; retain only its unique C root. ++ unsafe { ++ let root = wasm_func_as_ref(wasm_function); ++ (*callback_env).store.inner.store.as_v8_mut().owned_refs.push(super::store::OwnedRef(root)); ++ wasm_functype_delete(wasm_functype); + }; + + if wasm_function.is_null() { +@@ -217,8 +230,15 @@ + wasm_functype, + Some(callback), + callback_env as _, +- None, ++ Some(drop_callback_env::), + ) ++ }; ++ ++ // The callback has a borrowed StoreMut; retain only its unique C root. ++ unsafe { ++ let root = wasm_func_as_ref(wasm_function); ++ (*callback_env).store.inner.store.as_v8_mut().owned_refs.push(super::store::OwnedRef(root)); ++ wasm_functype_delete(wasm_functype); + }; + + if wasm_function.is_null() { +@@ -297,8 +317,15 @@ + wasm_functype, + Some(callback), + callback_env as _, +- None, ++ Some(drop_callback_env::), + ) ++ }; ++ ++ // The callback has a borrowed StoreMut; retain only its unique C root. ++ unsafe { ++ let root = wasm_func_as_ref(wasm_function); ++ (*callback_env).store.inner.store.as_v8_mut().owned_refs.push(super::store::OwnedRef(root)); ++ wasm_functype_delete(wasm_functype); + }; + + if wasm_function.is_null() { +@@ -332,6 +359,7 @@ + res + }; + ++ unsafe { wasm_functype_delete(type_) }; + FunctionType::new(params, returns) + } + +@@ -375,31 +403,33 @@ + + // let wasm_func_param_arity(self.handle) + +- let mut args = unsafe { ++ let mut args = typed::ResearchValueVec(unsafe { + let mut wasm_params = params + .iter() +- .map(|v| IntoCApiValue::into_cv(v.clone())) ++ .map(|v| typed::own_call_parameter(IntoCApiValue::into_cv(v.clone()))) + .collect::>() + .into_boxed_slice(); + let mut vec = Default::default(); + wasm_val_vec_new(&mut vec, wasm_params.len(), wasm_params.as_ptr()); + vec +- }; ++ }); + + let size = unsafe { wasm_func_result_arity(self.handle) }; + +- let mut results = { ++ let mut results = typed::ResearchValueVec({ + unsafe { + let mut vec = Default::default(); + wasm_val_vec_new_uninitialized(&mut vec, size); + vec + } +- }; ++ }); + + let mut trap; + + loop { +- trap = unsafe { wasm_func_call(self.handle, &mut args as _, &mut results as *mut _) }; ++ store.as_store_ref().inner.store.as_v8().ensure_running()?; ++ let raw_trap = unsafe { wasm_func_call(self.handle, &mut *args as _, &mut *results as *mut _) }; ++ trap = (!raw_trap.is_null()).then(|| Trap::from(raw_trap)); + let store_mut = store.as_store_mut(); + if let Some(callback) = store_mut.inner.on_called.take() { + match callback(store_mut) { +@@ -418,15 +448,17 @@ + break; + } + +- if !trap.is_null() { +- return Err(Into::::into(trap).into()); ++ store.as_store_ref().inner.store.as_v8().ensure_running()?; ++ if let Some(trap) = trap { ++ return Err(trap.into()); + } + + let values = unsafe { + let results = std::ptr::slice_from_raw_parts(results.data, results.size); + (*results) + .iter() +- .map(|v| IntoWasmerValue::into_wv(*v)) ++ .zip(signature.results()) ++ .map(|(v, expected)| typed::into_call_value(*v, *expected, store)) + .collect::>() + .into_boxed_slice() + }; +@@ -526,8 +558,9 @@ + unsafe { trap.into_wasm_trap(&mut store) } + } + +- Err(e) => { +- unimplemented!("host function panicked"); ++ Err(_) => { ++ let trap = Trap::user(Box::new(RuntimeError::new("host function panicked"))); ++ unsafe { trap.into_wasm_trap(&mut store) } + } + } + } +@@ -634,8 +667,9 @@ + // unimplemented!("host function panicked"); + }, + +- Err(e) => { +- unimplemented!("host function panicked"); ++ Err(_) => { ++ let trap = Trap::user(Box::new(RuntimeError::new("host function panicked"))); ++ unsafe { trap.into_wasm_trap(store) } + } + } + }} +@@ -707,7 +741,10 @@ + let trap = crate::backend::v8::error::Trap::user(Box::new(e)); unsafe { trap.into_wasm_trap(store) } + }, + +- Err(e) => { unimplemented!("host function panicked"); } ++ Err(_) => { ++ let trap = Trap::user(Box::new(RuntimeError::new("host function panicked"))); ++ unsafe { trap.into_wasm_trap(store) } ++ } + } + }} + +--- a/src/backend/v8/entities/function/typed.rs ++++ b/src/backend/v8/entities/function/typed.rs +@@ -11,12 +11,47 @@ + use std::iter::FromIterator; + + use crate::{ +- AsStoreMut, FromToNativeWasmType, NativeWasmType, NativeWasmTypeInto, RuntimeError, ++ AsStoreMut, AsStoreRef, FromToNativeWasmType, NativeWasmType, NativeWasmTypeInto, RuntimeError, + TypedFunction, Value, WasmTypeList, + backend::v8::{bindings::*, error::Trap, function::Function, utils::convert::*}, + }; + use wasmer_types::RawValue; + ++// Disposable numeric-call ownership control. No entity alias deletes. ++pub(super) struct ResearchValueVec(pub(super) wasm_val_vec_t); ++impl std::ops::Deref for ResearchValueVec { ++ type Target = wasm_val_vec_t; ++ fn deref(&self) -> &Self::Target { &self.0 } ++} ++impl std::ops::DerefMut for ResearchValueVec { ++ fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } ++} ++impl Drop for ResearchValueVec { ++ fn drop(&mut self) { unsafe { wasm_val_vec_delete(&mut self.0) } } ++} ++// C vectors adopt reference wrappers; borrow a Function only through a copy. ++pub(super) fn own_call_parameter(value: wasm_val_t) -> wasm_val_t { ++ if value.kind == wasm_valkind_enum_WASM_FUNCREF as _ { ++ let mut owned = value; ++ unsafe { wasm_val_copy(&mut owned, &value) }; ++ owned ++ } else { value } ++} ++ ++// Returned Function aliases must outlive the temporary native result vector. ++pub(super) fn into_call_value(mut value: wasm_val_t, expected: wasmer_types::Type, store: &mut impl AsStoreMut) -> Value { ++ // The pinned C API reports returned function references as generic refs. ++ if expected == wasmer_types::Type::FuncRef { ++ value.kind = wasm_valkind_enum_WASM_FUNCREF as _; ++ let pointer = unsafe { value.of.ref_ }; ++ if pointer.is_null() { return Value::FuncRef(None); } ++ let owned = unsafe { wasm_ref_copy(pointer) }; ++ store.as_store_mut().inner.store.as_v8_mut().owned_refs ++ .push(super::super::store::OwnedRef(owned)); ++ value.of.ref_ = owned; ++ } ++ value.into_wv() ++} + macro_rules! impl_native_traits { + ( $( $x:ident ),* ) => { + #[allow(unused_parens, non_snake_case)] +@@ -37,28 +72,31 @@ + vec![ $( { + let raw = $x.to_native().into_raw(store); + let value = Value::from_raw(&mut store, <$x::Native as NativeWasmType>::WASM_TYPE, raw); +- value.into_cv() ++ own_call_parameter(value.into_cv()) + } ),* ] + }; + +- let mut results = { ++ let mut results = ResearchValueVec({ + unsafe { + let mut ret = std::mem::zeroed(); + wasm_val_vec_new_uninitialized(&mut ret, Rets::wasm_types().len()); + ret + } +- }; ++ }); + + + let func = unsafe { wasm_extern_as_func(self.func.to_vm_extern().unwrap_v_8()) }; + + let mut params = unsafe {std::mem::zeroed()}; + unsafe {wasm_val_vec_new(&mut params, params_list.len(), params_list.as_ptr())}; ++ let params = ResearchValueVec(params); + + let mut trap; + + loop { +- trap = unsafe { wasm_func_call(func, ¶ms, &mut results) }; ++ store.as_store_ref().inner.store.as_v8().ensure_running()?; ++ let raw_trap = unsafe { wasm_func_call(func, &*params, &mut *results) }; ++ trap = (!raw_trap.is_null()).then(|| Trap::from(raw_trap)); + + let store_mut = store.as_store_mut(); + if let Some(callback) = store_mut.inner.on_called.take() { +@@ -74,12 +112,10 @@ + + + +- if !trap.is_null() { +- unsafe { +- let trap: Trap = trap.into(); +- return Err(RuntimeError::from(trap)); +- } +- } ++ store.as_store_ref().inner.store.as_v8().ensure_running()?; ++ if let Some(trap) = trap { ++ return Err(RuntimeError::from(trap)); ++ } + + unsafe { + let mut rets_list_array = Rets::empty_array(); +@@ -89,13 +125,13 @@ + 0 => {}, + 1 => { + let val = (*results.data.wrapping_add(0)).clone(); +- let val = val.into_wv(); ++ let val = into_call_value(val, Rets::wasm_types()[0], &mut store); + *mut_rets = val.as_raw(&mut store); + } + _n => { + for (i, ret_type) in Rets::wasm_types().iter().enumerate() { + let val = (*results.data.wrapping_add(i)).clone(); +- let val = val.into_wv(); ++ let val = into_call_value(val, *ret_type, &mut store); + let slot = mut_rets.add(i); + *slot = val.as_raw(&mut store); + } +--- a/src/backend/v8/entities/global.rs ++++ b/src/backend/v8/entities/global.rs +@@ -60,9 +60,10 @@ + let v8_global_type = + unsafe { wasm_globaltype_new(wasm_valtype_new(v8_type), v8_mutability) }; + +- Ok(Self { +- handle: unsafe { wasm_global_new(v8_store.inner, v8_global_type, &v8_value) }, +- }) ++ let handle = unsafe { wasm_global_new(v8_store.inner, v8_global_type, &v8_value) }; ++ unsafe { bindings::wasm_globaltype_delete(v8_global_type) }; ++ store.inner.store.as_v8_mut().owned_refs.push(super::store::OwnedRef(unsafe { bindings::wasm_global_as_ref(handle) })); ++ Ok(Self { handle }) + } + + pub fn ty(&self, store: &impl AsStoreRef) -> GlobalType { +@@ -73,14 +74,16 @@ + let valtype = unsafe { wasm_globaltype_content(r#type) }; + let wasmer_type = valtype.into_wt(); + +- GlobalType::new( ++ let result = GlobalType::new( + wasmer_type, + if mutability == bindings::wasm_mutability_enum_WASM_VAR as u8 { + Mutability::Var + } else { + Mutability::Const + }, +- ) ++ ); ++ unsafe { bindings::wasm_globaltype_delete(r#type) }; ++ result + } + + pub fn get(&self, store: &mut impl AsStoreMut) -> Value { +--- a/src/backend/v8/entities/instance.rs ++++ b/src/backend/v8/entities/instance.rs +@@ -26,20 +26,15 @@ + let externs: Vec<_> = externs.into_iter().map(|v| v.unwrap_v_8()).collect(); + + let instance = unsafe { +- let mut imports = unsafe { +- let mut vec = Default::default(); +- wasm_extern_vec_new(&mut vec, externs.len(), externs.as_ptr()); +- vec +- }; +- +- std::mem::forget(externs); +- +- wasm_instance_new( +- store, +- wasm_module_obtain(store, module), +- &mut imports, +- &mut trap, +- ) ++ // The vector consumes C wrappers: copy borrowed reusable imports. ++ let copied: Vec<_> = externs.iter().map(|p| wasm_extern_copy(*p)).collect(); ++ let mut imports = wasm_extern_vec_t::default(); ++ wasm_extern_vec_new(&mut imports, copied.len(), copied.as_ptr()); ++ let local_module = wasm_module_obtain(store, module); ++ let instance = wasm_instance_new(store, local_module, &imports, &mut trap); ++ wasm_module_delete(local_module); ++ wasm_extern_vec_delete(&mut imports); ++ instance + }; + + if instance.is_null() { +@@ -65,7 +60,7 @@ + }; + + let exports_ty = module.exports().collect::>(); +- exports_ty ++ let result = exports_ty + .iter() + .zip(wasm_exports.iter()) + .map(|(export_type, wasm_export)| { +@@ -77,14 +72,14 @@ + let extern_ = Extern::from_vm_extern(&mut store, VMExtern::V8(*wasm_export)); + (name.to_string(), extern_) + }) +- .collect::() ++ .collect::(); ++ store.as_store_mut().inner.store.as_v8_mut().owned_exports ++ .push(super::store::OwnedExports(exports)); ++ result + } + } +-impl Drop for InstanceHandle { +- fn drop(&mut self) { +- unsafe { wasm_instance_delete(self.0) } +- } +-} ++// Store owns the C wrapper; a Rust alias may outlive its Store. ++ + + #[derive(Clone, PartialEq, Eq)] + /// A WebAssembly `instance` in the `v8` runtime. +@@ -117,6 +112,7 @@ + ) -> Result<(Self, Exports), InstantiationError> { + check_isolate(store); + let store_ref = store.as_store_ref(); ++ store_ref.inner.store.as_v8().ensure_running().map_err(InstantiationError::Start)?; + let externs: Vec = externs + .iter() + .map(|extern_| extern_.to_vm_extern()) +@@ -126,6 +122,10 @@ + module.as_v8().handle.v8_shared_module_handle, + externs, + )?; ++ // C instance wrappers belong to the Store, like their export aliases. ++ let owned = unsafe { wasm_instance_as_ref(instance.0) }; ++ store.as_store_mut().inner.store.as_v8_mut().owned_refs.push(super::store::OwnedRef(owned)); ++ store.as_store_ref().inner.store.as_v8().ensure_running().map_err(InstantiationError::Start)?; + let exports = instance.get_exports(store, module); + + Ok(( +--- a/src/backend/v8/entities/memory/mod.rs ++++ b/src/backend/v8/entities/memory/mod.rs +@@ -53,14 +53,12 @@ + }); + } + +- let limits = Box::into_raw(Box::new(wasm_limits_t { +- min, +- max, +- shared: ty.shared, +- })); +- +- let memorytype = unsafe { wasm_memorytype_new(limits) }; ++ let limits = wasm_limits_t { min, max, shared: ty.shared }; ++ ++ let memorytype = unsafe { wasm_memorytype_new(&limits) }; + let c_memory = unsafe { wasm_memory_new(v8_store.inner, memorytype) }; ++ unsafe { wasm_memorytype_delete(memorytype) }; ++ store_mut.inner.store.as_v8_mut().owned_refs.push(super::store::OwnedRef(unsafe { wasm_memory_as_ref(c_memory) })); + + Ok(Self { + handle: VMMemory(c_memory), +@@ -84,7 +82,7 @@ + let memory_type: *mut wasm_memorytype_t = unsafe { wasm_memory_type(self.handle.0) }; + let limits: *const wasm_limits_t = unsafe { wasm_memorytype_limits(memory_type) }; + +- MemoryType { ++ let result = MemoryType { + shared: unsafe { (*limits).shared }, + minimum: unsafe { wasmer_types::Pages((*limits).min) }, + maximum: unsafe { +@@ -94,7 +92,9 @@ + Some(wasmer_types::Pages((*limits).max)) + } + }, +- } ++ }; ++ unsafe { wasm_memorytype_delete(memory_type) }; ++ result + } + + pub fn size(&self, store: &impl AsStoreRef) -> Pages { +@@ -198,6 +198,8 @@ + )); + } + ++ // Own the temporary wrapper on success and every early-return path. ++ let _copied_owner = super::store::OwnedRef(unsafe { wasm_memory_as_ref(copied_memory) }); + let copied = Self { + handle: VMMemory(copied_memory), + }; +@@ -226,9 +228,9 @@ + .copy_to_memory(src_view.data_size(), &dst_view) + .map_err(|err| MemoryError::Generic(format!("failed to copy V8 memory: {err}")))?; + +- Ok(SharedMemory::new(crate::vm::VMSharedMemory::V8( +- copied.handle.as_shared()?, +- ))) ++ let shared = copied.handle.as_shared(super::store::interrupt::MemoryInterrupt::detached())?; ++ let ops = shared.1.clone(); ++ Ok(SharedMemory::new_with_ops(crate::vm::VMSharedMemory::V8(shared), ops)) + } + + pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { +@@ -238,9 +240,11 @@ + + pub fn as_shared(&self, store: &impl AsStoreRef) -> Result { + check_isolate(store); +- Ok(SharedMemory::new(crate::vm::VMSharedMemory::V8( +- self.handle.as_shared()?, +- ))) ++ let store = store.as_store_ref(); ++ let v8_store = store.inner.store.as_v8(); ++ let shared = self.handle.as_shared(v8_store.shared_memory_ops(self.handle.0))?; ++ let ops = shared.1.clone(); ++ Ok(SharedMemory::new_with_ops(crate::vm::VMSharedMemory::V8(shared), ops)) + } + } + +--- a/src/backend/v8/entities/module.rs ++++ b/src/backend/v8/entities/module.rs +@@ -15,22 +15,84 @@ + }; + use wasmparser::{Parser, Payload}; + ++#[cfg(all(test, windows))] ++mod oliphaunt_contract { ++ use super::*; ++ ++ const GUEST: &[u8] = b"\0asm\x01\0\0\0\x01\x05\x01\x60\0\x01\x7f\x03\x02\x01\0\x07\x0a\x01\x06answer\0\0\x0a\x06\x01\x04\0\x41\x2a\x0b"; ++ ++ fn native_offset(cache: &[u8]) -> usize { ++ assert!(cache[0] < 128, "the fixture has a one-byte wire length"); ++ 1 + usize::from(cache[0]) ++ } ++ ++ fn encoded(info: &V8ModuleInfo) -> Vec { ++ rkyv::to_bytes::(info).unwrap().to_vec() ++ } ++ ++ #[test] ++ fn rejects_wire_only_and_incompatible_caches_without_compilation() { ++ let engine: crate::Engine = crate::v8::V8::try_new().unwrap().into(); ++ let module = Module::from_binary(&engine, GUEST).unwrap(); ++ let mut info = module.info.clone(); ++ let offset = native_offset(&info.v8_module_bytes[0]); ++ info.v8_module_bytes[0].truncate(offset); ++ let error = unsafe { crate::Module::deserialize(&engine, encoded(&info)) }.unwrap_err(); ++ assert!(error.to_string().contains("guest compilation is disabled")); ++ ++ let mut info = module.info.clone(); ++ // Deliberately incompatible test bytes, never a producer workaround. ++ info.v8_module_bytes[0][offset + 8..offset + 12].copy_from_slice(&u32::MAX.to_le_bytes()); ++ assert!(unsafe { crate::Module::deserialize(&engine, encoded(&info)) }.is_err()); ++ } ++ ++ #[test] ++ fn skips_an_incompatible_cpu_profile_and_executes_the_next_native_cache() { ++ let engine: crate::Engine = crate::v8::V8::try_new().unwrap().into(); ++ let module = Module::from_binary(&engine, GUEST).unwrap(); ++ let mut info = module.info.clone(); ++ let original = info.v8_module_bytes[0].clone(); ++ let offset = native_offset(&original); ++ assert_eq!(u32::from_le_bytes(original[offset + 8..offset + 12].try_into().unwrap()), 0x1000e, "the native runner must qualify the CET profile"); ++ info.v8_module_bytes[0][offset + 8..offset + 12].copy_from_slice(&u32::MAX.to_le_bytes()); ++ info.v8_module_bytes.push(original); ++ let loaded = unsafe { crate::Module::deserialize(&engine, encoded(&info)) }.unwrap(); ++ let mut store = crate::Store::new(engine); ++ let instance = crate::Instance::new(&mut store, &loaded, &crate::imports! {}).unwrap(); ++ let answer = instance.exports.get_typed_function::<(), i32>(&store, "answer").unwrap(); ++ assert_eq!(answer.call(&mut store).unwrap(), 42); ++ } ++ ++ #[test] ++ fn rejects_variants_for_another_guest() { ++ let engine: crate::Engine = crate::v8::V8::try_new().unwrap().into(); ++ let module = Module::from_binary(&engine, GUEST).unwrap(); ++ let mut different = GUEST.to_vec(); ++ let position = different.len() - 2; ++ different[position] = 43; ++ let other = Module::from_binary(&engine, &different).unwrap(); ++ assert!(module.serialize_with_native_variants(&other.info.v8_module_bytes).is_err()); ++ } ++} ++ + #[derive(Debug)] + pub(crate) struct ModuleHandle { + pub(crate) v8_shared_module_handle: *mut wasm_shared_module_t, +- pub(crate) orig_store: Store, ++ pub(crate) engine: crate::Engine, + } + + impl PartialEq for ModuleHandle { + fn eq(&self, other: &Self) -> bool { ++ // Compare in one isolate; each obtained wrapper is unique. + unsafe { +- wasm_module_same( +- wasm_module_obtain(self.orig_store.as_v8().inner, self.v8_shared_module_handle), +- wasm_module_obtain( +- other.orig_store.as_v8().inner, +- other.v8_shared_module_handle, +- ), +- ) ++ let transient = Store::new(self.engine.clone()); ++ let store = transient.as_v8().inner; ++ let left = wasm_module_obtain(store, self.v8_shared_module_handle); ++ let right = wasm_module_obtain(store, other.v8_shared_module_handle); ++ let same = wasm_module_same(left, right); ++ wasm_module_delete(right); ++ wasm_module_delete(left); ++ same + } + } + } +@@ -46,7 +108,7 @@ + + let engine = engine.as_engine_ref().engine().clone(); + let store = Store::new(engine.clone()); +- let engine = engine.as_v8().inner.engine; ++ + + let inner = unsafe { wasm_module_new(store.as_v8().inner, &bytes as *const _) }; + +@@ -56,7 +118,11 @@ + )); + } + +- let inner = unsafe { wasm_module_share(inner) }; ++ let inner = unsafe { ++ let shared = wasm_module_share(inner); ++ wasm_module_delete(inner); ++ shared ++ }; + + if inner.is_null() { + return Err(CompileError::Validate( +@@ -66,11 +132,27 @@ + + Ok(Self { + v8_shared_module_handle: inner, +- orig_store: store, ++ engine, + }) + } + + fn deserialize(engine: &impl AsEngineRef, binary: &[u8]) -> Result { ++ // The pinned C API compiles wire-only serialized modules. Deserialization ++ // of an AOT artifact must reject that case before entering V8. ++ let mut wire_len = 0u64; ++ let mut prefix_len = None; ++ for (index, byte) in binary.iter().copied().take(10).enumerate() { ++ if index == 9 && byte > 1 { break; } ++ wire_len |= u64::from(byte & 0x7f) << (index * 7); ++ if byte & 0x80 == 0 { prefix_len = Some(index + 1); break; } ++ } ++ let native_start = prefix_len ++ .and_then(|n| usize::try_from(wire_len).ok().and_then(|wire| n.checked_add(wire))); ++ if native_start.and_then(|n| binary.len().checked_sub(n)).is_none_or(|n| n < 20) { ++ return Err(CompileError::Validate( ++ "V8 AOT artifact has no complete native payload; guest compilation is disabled".into(), ++ )); ++ } + let bytes = wasm_byte_vec_t { + size: binary.len(), + data: binary.as_ptr() as _, +@@ -86,7 +168,11 @@ + )); + } + +- let inner = unsafe { wasm_module_share(inner) }; ++ let inner = unsafe { ++ let shared = wasm_module_share(inner); ++ wasm_module_delete(inner); ++ shared ++ }; + + if inner.is_null() { + return Err(CompileError::Validate( +@@ -96,15 +182,16 @@ + + Ok(Self { + v8_shared_module_handle: inner, +- orig_store: store, ++ engine, + }) + } + + #[tracing::instrument(skip(self))] + pub fn serialize(&self) -> Result, SerializeError> { ++ let transient = Store::new(self.engine.clone()); + let handle = unsafe { + wasm_module_obtain( +- self.orig_store.as_v8().inner, ++ transient.as_v8().inner, + self.v8_shared_module_handle as *const _, + ) + }; +@@ -147,7 +234,7 @@ + // (store) than the one V8 associated with the module, which can crash. + imports: Vec, + exports: Vec, +- v8_module_bytes: Vec, ++ v8_module_bytes: Vec>, + // Copy of the custom sections, since the current API cannot retrieve them + // later from the handle. Use a BTreeMap to keep entries in deterministic order, + // producing stable serialized bytes. +@@ -205,7 +292,7 @@ + name: info.name, + imports, + exports, +- v8_module_bytes, ++ v8_module_bytes: vec![v8_module_bytes], + custom_sections, + }, + }) +@@ -242,6 +329,38 @@ + Ok(info.into()) + } + ++ /// Serialize this module with additional producer-built native CPU profiles. ++ /// Every profile must contain the same WebAssembly wire bytes. ++ pub fn serialize_with_native_variants(&self, variants: &[Vec]) -> Result { ++ fn wire(bytes: &[u8]) -> Option<&[u8]> { ++ let mut length = 0u64; ++ for (index, byte) in bytes.iter().copied().take(10).enumerate() { ++ if index == 9 && byte > 1 { return None; } ++ length |= u64::from(byte & 0x7f) << (index * 7); ++ if byte & 0x80 == 0 { ++ let end = (index + 1).checked_add(usize::try_from(length).ok()?)?; ++ if bytes.len().checked_sub(end)? < 20 { return None; } ++ return bytes.get(index + 1..end); ++ } ++ } ++ None ++ } ++ let original = self.info.v8_module_bytes.first() ++ .ok_or_else(|| SerializeError::Generic("module has no native cache".into()))?; ++ let expected = wire(original) ++ .ok_or_else(|| SerializeError::Generic("module has no complete native cache".into()))?; ++ let mut info = self.info.clone(); ++ for variant in variants { ++ if wire(variant) != Some(expected) { ++ return Err(SerializeError::Generic("native cache wire identity mismatch".into())); ++ } ++ info.v8_module_bytes.push(variant.clone()); ++ } ++ rkyv::to_bytes::(&info) ++ .map(|bytes| bytes.to_vec().into()) ++ .map_err(|error| SerializeError::Generic(format!("{error:?}"))) ++ } ++ + #[allow(clippy::arc_with_non_send_sync)] + pub unsafe fn deserialize_unchecked( + engine: &impl AsEngineRef, +@@ -252,7 +371,17 @@ + let info = rkyv::access::(&binary) + .and_then(rkyv::deserialize::) + .map_err(|err| DeserializeError::CorruptedBinary(format!("{err:?}")))?; +- let module = ModuleHandle::deserialize(engine, &info.v8_module_bytes)?; ++ // Native caches retain their original CPU/flag headers. Try the producer's ++ // profiles without compiling a guest when no cache is compatible. ++ let mut failures = Vec::new(); ++ let module = info.v8_module_bytes.iter().find_map(|cache| { ++ match ModuleHandle::deserialize(engine, cache) { ++ Ok(module) => Some(module), ++ Err(error) => { failures.push(error.to_string()); None } ++ } ++ }).ok_or_else(|| DeserializeError::CorruptedBinary(format!( ++ "no compatible V8 native cache: {}", failures.join("; ") ++ )))?; + + Ok(Self { + handle: Arc::new(module), +--- a/src/backend/v8/entities/store/interrupt.rs ++++ b/src/backend/v8/entities/store/interrupt.rs +@@ -0,0 +1,128 @@ ++use std::{cell::Cell, ffi::c_void, sync::{Arc, Mutex, Weak, atomic::{AtomicBool, Ordering}}}; ++ ++use crate::{error::AtomicsError, location::SharedMemoryOps}; ++use crate::backend::v8::bindings::*; ++ ++#[cfg(not(windows))] ++unsafe extern "C" { ++ #[link_name = "_ZN2v87Isolate10GetCurrentEv"] ++ fn current_isolate() -> *mut c_void; ++ #[link_name = "_ZN2v87Isolate18TerminateExecutionEv"] ++ fn terminate_execution(isolate: *mut c_void); ++} ++#[cfg(windows)] ++use crate::backend::v8::bindings::{ ++ oliphaunt_v8_current_isolate as current_isolate, ++ oliphaunt_v8_terminate_execution as terminate_execution, ++}; ++ ++thread_local! { static CAPTURED: Cell = const { Cell::new(0) }; } ++ ++unsafe extern "C" fn capture( ++ _args: *const wasm_val_vec_t, ++ _results: *mut wasm_val_vec_t, ++) -> *mut wasm_trap_t { ++ // The pinned C API enters the Store's isolate before a direct host call. ++ CAPTURED.with(|slot| slot.set(unsafe { current_isolate() } as usize)); ++ std::ptr::null_mut() ++} ++ ++pub(super) struct StoreInterrupt { isolate: Mutex>, terminated: AtomicBool } ++ ++impl StoreInterrupt { ++ pub(super) fn new() -> Arc { Arc::new(Self { isolate: Mutex::new(None), terminated: AtomicBool::new(false) }) } ++ ++ pub(super) fn capture(&self, store: *mut wasm_store_t) { ++ unsafe { ++ let mut params = std::mem::zeroed(); ++ let mut results = std::mem::zeroed(); ++ let ty = wasm_functype_new(&mut params, &mut results); ++ assert!(!ty.is_null(), "cannot create V8 interrupt capture type"); ++ let function = wasm_func_new(store, ty, Some(capture)); ++ wasm_functype_delete(ty); ++ assert!(!function.is_null(), "cannot create V8 interrupt capture function"); ++ let owner = super::OwnedRef(wasm_func_as_ref(function)); ++ let mut args = std::mem::zeroed(); ++ let mut results = std::mem::zeroed(); ++ CAPTURED.with(|slot| slot.set(0)); ++ let trap = wasm_func_call(function, &mut args, &mut results); ++ let pointer = CAPTURED.with(Cell::get); ++ if !trap.is_null() { wasm_trap_delete(trap); } ++ drop(owner); ++ assert!(trap.is_null() && pointer != 0, "cannot capture V8 Store interrupt"); ++ *self.isolate.lock().unwrap_or_else(|e| e.into_inner()) = Some(pointer); ++ } ++ } ++ ++ pub(super) fn is_terminated(&self) -> bool { self.terminated.load(Ordering::Acquire) } ++ ++ fn terminate(&self) { ++ self.terminated.store(true, Ordering::Release); ++ // Keep the lock through termination. Store teardown takes this same ++ // lock before retiring the pointer and deleting the native Store. ++ let pointer = self.isolate.lock().unwrap_or_else(|e| e.into_inner()); ++ if let Some(pointer) = *pointer { ++ unsafe { terminate_execution(pointer as *mut c_void) }; ++ } ++ } ++ ++ pub(super) fn retire(&self) { ++ *self.isolate.lock().unwrap_or_else(|e| e.into_inner()) = None; ++ } ++} ++ ++#[derive(Default)] ++struct MemoryState { ++ terminal: bool, ++ stores: Vec>, ++} ++ ++pub(crate) struct MemoryInterrupt(Mutex); ++ ++impl MemoryInterrupt { ++ pub(crate) fn detached() -> Arc { Arc::new(Self(Mutex::new(MemoryState::default()))) } ++ ++ pub(super) fn new(store: &Arc) -> Arc { ++ Arc::new(Self(Mutex::new(MemoryState { ++ terminal: false, stores: vec![Arc::downgrade(store)], ++ }))) ++ } ++ ++ pub(crate) fn attach( ++ self: &Arc, ++ store: &mut super::Store, ++ memory: *mut wasm_shared_memory_t, ++ ) -> Result<*mut wasm_memory_t, crate::MemoryError> { ++ let mut state = self.0.lock().unwrap_or_else(|e| e.into_inner()); ++ if state.terminal { ++ return Err(crate::MemoryError::Generic( ++ "cannot attach V8 shared memory after execution contexts are terminal".to_owned(), ++ )); ++ } ++ let handle = unsafe { wasm_memory_obtain(store.inner, memory) }; ++ if handle.is_null() { ++ return Err(crate::MemoryError::Generic("cannot obtain V8 shared memory".to_owned())); ++ } ++ state.stores.retain(|entry| entry.strong_count() != 0); ++ let interrupt = Arc::downgrade(&store.interrupt); ++ if !state.stores.iter().any(|entry| Weak::ptr_eq(entry, &interrupt)) { ++ state.stores.push(interrupt); ++ } ++ store.remember_shared_memory(handle, self.clone()); ++ Ok(handle) ++ } ++} ++ ++impl SharedMemoryOps for MemoryInterrupt { ++ fn terminate_execution_contexts(&self) -> Result<(), AtomicsError> { ++ let mut state = self.0.lock().unwrap_or_else(|e| e.into_inner()); ++ state.terminal = true; ++ state.stores.retain(|entry| { ++ if let Some(store) = entry.upgrade() { ++ store.terminate(); ++ true ++ } else { false } ++ }); ++ Ok(()) ++ } ++} +--- a/src/backend/v8/entities/store/mod.rs ++++ b/src/backend/v8/entities/store/mod.rs +@@ -1,5 +1,5 @@ + //! Data types, functions and traits for `v8` runtime's `Store` implementation. +-use std::thread::ThreadId; ++use std::{cell::RefCell, sync::Arc, thread::ThreadId}; + + use crate::{ + AsStoreRef, BackendStore, StoreRef, +@@ -7,14 +7,34 @@ + engine::{AsEngineRef, Engine, EngineRef}, + }; + ++pub(crate) mod interrupt; + mod obj; + pub use obj::*; ++ ++#[derive(Debug)] ++pub(crate) struct OwnedExports(pub(crate) crate::backend::v8::bindings::wasm_extern_vec_t); ++ ++impl Drop for OwnedExports { ++ fn drop(&mut self) { ++ unsafe { crate::backend::v8::bindings::wasm_extern_vec_delete(&mut self.0) }; ++ } ++} ++ ++#[derive(Debug)] ++pub(crate) struct OwnedRef(pub(crate) *mut crate::backend::v8::bindings::wasm_ref_t); ++impl Drop for OwnedRef { ++ fn drop(&mut self) { unsafe { crate::backend::v8::bindings::wasm_ref_delete(self.0) }; } ++} + + /// A WebAssembly `store` in the `v8` runtime. + pub struct Store { + pub(crate) engine: Engine, + pub(crate) inner: *mut wasm_store_t, + pub(crate) thread_id: ThreadId, ++ pub(crate) owned_exports: Vec, ++ pub(crate) owned_refs: Vec, ++ interrupt: Arc, ++ shared_memories: RefCell)>>, + } + + impl std::fmt::Debug for Store { +@@ -29,11 +49,46 @@ + pub(crate) fn new(engine: crate::engine::Engine) -> Self { + let inner: *mut wasm_store_t = unsafe { wasm_store_new(engine.as_v8().inner.engine) }; + let thread_id = std::thread::current().id(); +- Self { ++ let store = Self { + inner, + engine, + thread_id, ++ owned_exports: Vec::new(), ++ owned_refs: Vec::new(), ++ interrupt: interrupt::StoreInterrupt::new(), ++ shared_memories: RefCell::new(Vec::new()), ++ }; ++ store.interrupt.capture(inner); ++ store ++ } ++ ++ pub(crate) fn shared_memory_ops(&self, memory: *mut crate::backend::v8::bindings::wasm_memory_t) -> Arc { ++ use crate::backend::v8::bindings::*; ++ let mut memories = self.shared_memories.borrow_mut(); ++ if let Some((_, ops)) = memories.iter().find(|(entry, _)| unsafe { ++ wasm_memory_same(wasm_ref_as_memory(entry.0), memory) ++ }) { ++ return ops.clone(); + } ++ let ops = interrupt::MemoryInterrupt::new(&self.interrupt); ++ memories.push((OwnedRef(unsafe { wasm_memory_as_ref(wasm_memory_copy(memory)) }), ops.clone())); ++ ops ++ } ++ ++ pub(crate) fn remember_shared_memory(&self, memory: *mut crate::backend::v8::bindings::wasm_memory_t, ops: Arc) { ++ use crate::backend::v8::bindings::*; ++ let mut memories = self.shared_memories.borrow_mut(); ++ if !memories.iter().any(|(entry, _)| unsafe { ++ wasm_memory_same(wasm_ref_as_memory(entry.0), memory) ++ }) { ++ memories.push((OwnedRef(unsafe { wasm_memory_as_ref(wasm_memory_copy(memory)) }), ops)); ++ } ++ } ++ ++ pub(crate) fn ensure_running(&self) -> Result<(), crate::RuntimeError> { ++ if self.interrupt.is_terminated() { ++ Err(crate::RuntimeError::new("V8 execution context terminated")) ++ } else { Ok(()) } + } + + pub(crate) fn engine(&self) -> &Engine { +@@ -47,6 +102,12 @@ + + impl Drop for Store { + fn drop(&mut self) { ++ // These vectors uniquely own the export wrappers. Rust export clones ++ // borrow them through their Store, so retain them until Store teardown. ++ self.interrupt.retire(); ++ self.shared_memories.get_mut().clear(); ++ self.owned_exports.clear(); ++ self.owned_refs.clear(); + unsafe { wasm_store_delete(self.inner) } + } + } +--- a/src/backend/v8/entities/table.rs ++++ b/src/backend/v8/entities/table.rs +@@ -26,13 +26,12 @@ + pub(crate) fn type_to_v8(ty: TableType) -> *mut wasm_tabletype_t { + let valtype = unsafe { wasm_valtype_new(ty.ty.into_ct()) }; + +- let limits = Box::into_raw(Box::new(wasm_limits_t { ++ let limits = wasm_limits_t { + min: ty.minimum, + max: ty.maximum.unwrap_or(wasm_limits_max_default), + shared: false, +- })); +- +- unsafe { wasm_tabletype_new(valtype, limits) } ++ }; ++ unsafe { wasm_tabletype_new(valtype, &limits) } + } + + pub fn new( +@@ -49,9 +48,10 @@ + let wasm_tablety = Self::type_to_v8(ty); + let init: wasm_val_t = init.into_cv(); + +- Ok(Self { +- handle: unsafe { wasm_table_new(v8_store.inner, wasm_tablety, init.of.ref_) }, +- }) ++ let handle = unsafe { wasm_table_new(v8_store.inner, wasm_tablety, init.of.ref_) }; ++ unsafe { wasm_tabletype_delete(wasm_tablety) }; ++ store_mut.inner.store.as_v8_mut().owned_refs.push(super::store::OwnedRef(unsafe { wasm_table_as_ref(handle) })); ++ Ok(Self { handle }) + } + + pub fn to_vm_extern(&self) -> VMExtern { +@@ -65,10 +65,10 @@ + + let table_type: *mut wasm_tabletype_t = unsafe { wasm_table_type(self.handle) }; + let table_limits = unsafe { wasm_tabletype_limits(table_type) }; +- let table_type = unsafe { wasm_tabletype_element(table_type) }; +- +- TableType { +- ty: table_type.into_wt(), ++ let element_type = unsafe { wasm_tabletype_element(table_type) }; ++ ++ let result = TableType { ++ ty: element_type.into_wt(), + minimum: unsafe { (*table_limits).min }, + maximum: unsafe { + if (*table_limits).max == wasm_limits_max_default { +@@ -78,7 +78,9 @@ + } + }, + readonly: false, +- } ++ }; ++ unsafe { wasm_tabletype_delete(table_type) }; ++ result + } + + pub fn get(&self, store: &mut impl AsStoreMut, index: u32) -> Option { +--- a/src/backend/v8/entities/tag.rs ++++ b/src/backend/v8/entities/tag.rs +@@ -21,6 +21,9 @@ + + unsafe impl Send for Tag {} + unsafe impl Sync for Tag {} ++ ++// C bridge uses ExternType's virtual implementation destructor. ++use crate::backend::v8::bindings::oliphaunt_externtype_delete; + + impl Tag { + pub fn new>>(store: &mut impl AsStoreMut, params: P) -> Self { +@@ -49,6 +52,8 @@ + } + + let handle = unsafe { wasm_tag_new(v8_store.inner, tag_type) }; ++ unsafe { oliphaunt_externtype_delete(tag_type) }; ++ store_mut.inner.store.as_v8_mut().owned_refs.push(super::store::OwnedRef(unsafe { wasm_tag_as_ref(handle) })); + if handle.is_null() { + panic!("failed to create new tag: returned tag is null"); + } +@@ -69,6 +74,7 @@ + res + }; + ++ unsafe { oliphaunt_externtype_delete(type_) }; + TagType::new(wasmer_types::TagKind::Exception, params) + } + +--- a/src/backend/v8/error.rs ++++ b/src/backend/v8/error.rs +@@ -1,7 +1,4 @@ +-use std::{ +- error::Error, +- ffi::{CStr, c_char}, +-}; ++use std::error::Error; + + use crate::{ + AsStoreMut, +@@ -11,7 +8,7 @@ + #[derive(Debug)] + enum InnerTrap { + User(Box), +- CApi(*mut wasm_trap_t), ++ CApi(String), + } + + /// A struct representing a Trap +@@ -69,7 +66,14 @@ + #[allow(clippy::unnecessary_mut_passed)] + pub unsafe fn into_wasm_trap(self, store: &mut impl AsStoreMut) -> *mut wasm_trap_t { + match self.inner { +- InnerTrap::CApi(t) => t, ++ InnerTrap::CApi(message) => { ++ let mut data = wasm_byte_vec_t::default(); ++ unsafe { wasm_byte_vec_new(&mut data, message.len(), message.as_ptr() as _) }; ++ let store = store.as_store_mut(); ++ let trap = unsafe { wasm_trap_new(store.inner.store.as_v8().inner, &data) }; ++ unsafe { wasm_byte_vec_delete(&mut data) }; ++ trap ++ } + InnerTrap::User(err) => { + let err_ptr = Box::leak(Box::new(err)); + // UNSAFE: The boxed error is propagated by passing the string +@@ -90,13 +94,16 @@ + + impl From<*mut wasm_trap_t> for Trap { + fn from(value: *mut wasm_trap_t) -> Self { ++ // Snapshot the message while the Store is live. RuntimeError can outlive ++ // its Store, so it must not retain an isolate-owned C API trap handle. + let message = unsafe { +- let mut message = unsafe { std::mem::zeroed() }; +- unsafe { wasm_trap_message(value, &mut message) }; +- +- CStr::from_ptr(message.data as *const c_char) +- .to_str() +- .unwrap() ++ let mut bytes = wasm_byte_vec_t::default(); ++ wasm_trap_message(value, &mut bytes); ++ let text = String::from_utf8_lossy(std::slice::from_raw_parts( ++ bytes.data.cast::(), bytes.size)).trim_end_matches('\0').to_owned(); ++ wasm_byte_vec_delete(&mut bytes); ++ wasm_trap_delete(value); ++ text + }; + + // User traps are encoded by `into_wasm_trap` as a leaked `Box` pointer +@@ -104,8 +111,8 @@ + if let Some((_, ptr_str)) = message.split_once("🐛") { + let ptr: Box = unsafe { + let r = ptr_str.trim_start_matches("0x"); +- std::ptr::read(usize::from_str_radix(r, 16).unwrap() +- as *const Box) ++ *Box::from_raw(usize::from_str_radix(r, 16).unwrap() ++ as *mut Box) + }; + + Self { +@@ -113,7 +120,7 @@ + } + } else { + Self { +- inner: InnerTrap::CApi(value), ++ inner: InnerTrap::CApi(message), + } + } + } +@@ -132,18 +139,7 @@ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.inner { + InnerTrap::User(e) => write!(f, "{e}"), +- InnerTrap::CApi(value) => { +- // let message: wasm_message_t; +- // wasm_trap_message(value, &mut message); +- let mut out = unsafe { +- let mut vec: wasm_byte_vec_t = Default::default(); +- wasm_byte_vec_new_empty(&mut vec); +- &mut vec as *mut _ +- }; +- unsafe { wasm_trap_message(*value, out) }; +- let cstr = unsafe { CStr::from_ptr((*out).data) }; +- write!(f, "wasm-c-api trap: {}", cstr.to_str().unwrap()) +- } ++ InnerTrap::CApi(message) => write!(f, "wasm-c-api trap: {message}"), + } + } + } +--- a/src/backend/v8/vm/mod.rs ++++ b/src/backend/v8/vm/mod.rs +@@ -154,7 +154,7 @@ + #[derive(Debug, Clone)] + pub struct VMMemory(pub(super) *mut wasm_memory_t); + struct SharedMemoryPointer(*mut wasm_shared_memory_t); +-pub struct VMSharedMemory(Arc); ++pub struct VMSharedMemory(Arc, pub(crate) Arc); + pub(crate) type VMExternMemory = *mut wasm_memory_t; + + impl SharedMemoryPointer { +@@ -182,7 +182,7 @@ + + impl VMMemory { + /// Created a shared detached memory that can be attached in another Store. +- pub(crate) fn as_shared(&self) -> Result { ++ pub(crate) fn as_shared(&self, ops: Arc) -> Result { + let memory_type = unsafe { wasm_memory_type(self.0) }; + let limits = unsafe { wasm_memorytype_limits(memory_type) }; + if !unsafe { (*limits).shared } { +@@ -202,23 +202,28 @@ + )); + } + +- Ok(VMSharedMemory(Arc::new(SharedMemoryPointer::new(shared)))) ++ Ok(VMSharedMemory(Arc::new(SharedMemoryPointer::new(shared)), ops)) + } + } + + impl VMSharedMemory { + /// We can clone a shared memory handle as we want. + pub(crate) fn clone(&self) -> Self { +- Self(self.0.clone()) ++ Self(self.0.clone(), self.1.clone()) + } + + /// Attach the shared memory handle to a new Store and convert it to VMMemory + pub(crate) fn into_vm_memory(self, store: &mut Store) -> VMMemory { +- let memory = unsafe { wasm_memory_obtain(store.inner, self.0.0) }; ++ self.try_into_vm_memory(store).expect("cannot attach V8 shared memory") ++ } ++ ++ pub(crate) fn try_into_vm_memory(self, store: &mut Store) -> Result { ++ let memory = self.1.attach(store, self.0.0)?; ++ store.owned_refs.push(crate::backend::v8::store::OwnedRef(unsafe { wasm_memory_as_ref(memory) })); + assert!( + !memory.is_null(), + "Failed to obtain V8 shared memory: wasm_memory_obtain returned null" + ); +- VMMemory(memory) +- } +-} ++ Ok(VMMemory(memory)) ++ } ++} +--- a/src/entities/memory/location.rs ++++ b/src/entities/memory/location.rs +@@ -24,6 +24,10 @@ + + /// See [`crate::SharedMemory`]. + pub(crate) trait SharedMemoryOps { ++ fn terminate_execution_contexts(&self) -> Result<(), AtomicsError> { ++ self.disable_atomics() ++ } ++ + /// See [`crate::SharedMemory::disable_atomics`]. + fn disable_atomics(&self) -> Result<(), AtomicsError> { + Err(AtomicsError::Unimplemented) +--- a/src/entities/memory/shared.rs ++++ b/src/entities/memory/shared.rs +@@ -68,6 +68,23 @@ + Memory::new_from_existing(store, memory) + } + ++ /// Attach shared memory, returning an error after V8 terminal cancellation. ++ /// ++ /// This permits task creation to race with terminal cancellation without ++ /// unwinding through the task or linker. Other backends retain their ++ /// existing attachment behavior. ++ pub fn try_attach(self, store: &mut impl AsStoreMut) -> Result { ++ let memory = match self.memory { ++ #[cfg(feature = "v8")] ++ VMSharedMemory::V8(memory) => { ++ let mut store = store.as_store_mut(); ++ VMMemory::V8(memory.try_into_vm_memory(store.inner.store.as_v8_mut())?) ++ } ++ memory => memory.into_vm_memory(store), ++ }; ++ Ok(Memory::new_from_existing(store, memory)) ++ } ++ + /// Create an operations handle that does not keep the underlying memory alive. + pub fn ops(&self) -> MemoryOps { + MemoryOps { +@@ -122,6 +139,16 @@ + } + + impl MemoryOps { ++ /// Terminally cancel execution contexts using this memory. ++ /// ++ /// V8 cancels whole Stores and rejects guest reentry; this is not a ++ /// memory-specific atomic operation. Hosts must retire these contexts. ++ /// Other backends retain their existing shutdown through atomics disabling. ++ #[doc(hidden)] ++ pub fn terminate_execution_contexts(&self) -> Result<(), AtomicsError> { ++ self.shared_ops()?.terminate_execution_contexts() ++ } ++ + #[inline] + fn shared_ops(&self) -> Result<&(dyn SharedMemoryOps + Send + Sync), AtomicsError> { + self.ops +--- a/src/vm/impls.rs ++++ b/src/vm/impls.rs +@@ -59,7 +59,11 @@ + #[cfg(feature = "sys")] + Self::Sys(s) => s.0.as_shared().map(VMSharedMemory::Sys), + #[cfg(feature = "v8")] +- Self::V8(s) => s.as_shared().map(VMSharedMemory::V8), ++ // V8 sharing needs its owning Store to preserve interruption state. ++ // The V8 Memory API supplies it; this store-free helper is Sys-only. ++ Self::V8(_) => Err(wasmer_types::MemoryError::Generic( ++ "V8 memory sharing requires its owning Store".to_owned(), ++ )), + #[cfg(feature = "js")] + Self::Js(s) => s.try_clone().map(VMSharedMemory::Js), + } diff --git a/src/wasix/runtime/engine/patches/wasmer/series b/src/wasix/runtime/engine/patches/wasmer/series new file mode 100644 index 000000000..3cc7774f1 --- /dev/null +++ b/src/wasix/runtime/engine/patches/wasmer/series @@ -0,0 +1 @@ +0001-native-ownership-and-terminal-lifetimes.patch diff --git a/src/wasix/runtime/engine/prepare-sources.mts b/src/wasix/runtime/engine/prepare-sources.mts new file mode 100644 index 000000000..b76340dcb --- /dev/null +++ b/src/wasix/runtime/engine/prepare-sources.mts @@ -0,0 +1,76 @@ +import assert from 'node:assert/strict'; +import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { ENGINE_SOURCE_CRATES } from './contract.mts'; +import { + archiveTreeDigest, + promotePathTransactional, + sha256File, +} from '../../../third-party/tools/source-fetch-core.mts'; + +const root = path.resolve(import.meta.dir, '../../../..'); +const owner = path.join(root, 'src/wasix/runtime/engine'); +const pins = Bun.TOML.parse(readFileSync(path.join(owner, 'source.toml'), 'utf8')); +const crates = ENGINE_SOURCE_CRATES; + +function inputs(name) { + const directory = path.join(owner, 'patches', name); + return existsSync(directory) + ? readFileSync(path.join(directory, 'series'), 'utf8') + .trim() + .split('\n') + .map((file) => path.join(directory, file)) + : []; +} + +function prepare(name) { + const source = path.join(root, 'target/oliphaunt-sources/checkouts', `sdk-${name}`); + const directory = path.join(owner, 'crates', name); + const output = path.join(directory, 'upstream'); + const signature = JSON.stringify([pins[name].sha256, ...inputs(name).map(sha256File)]); + const stamp = path.join(directory, '.prepared.json'); + if (existsSync(stamp) && existsSync(output)) { + const previous = JSON.parse(readFileSync(stamp, 'utf8')); + if (previous.signature === signature && previous.tree === archiveTreeDigest(output)) return; + } + const stage = path.join(directory, `.upstream-${process.pid}`); + rmSync(stage, { recursive: true, force: true }); + cpSync(source, stage, { recursive: true }); + try { + rmSync(path.join(stage, '.oliphaunt-source-pin')); + for (const patch of inputs(name)) { + const result = Bun.spawnSync(['git', 'apply', '--check', patch], { cwd: stage }); + assert.equal(result.exitCode, 0, result.stderr.toString()); + const applied = Bun.spawnSync(['git', 'apply', patch], { cwd: stage }); + assert.equal(applied.exitCode, 0, applied.stderr.toString()); + } + promotePathTransactional(stage, output); + writeFileSync(stamp, JSON.stringify({ signature, tree: archiveTreeDigest(output) }) + '\n'); + } finally { + rmSync(stage, { recursive: true, force: true }); + } +} + +const [operation, plan] = Bun.argv.slice(2); +if (operation === 'plan' && plan) { + mkdirSync(plan, { recursive: true }); + for (const name of crates) { + const pin = pins[name]; + writeFileSync( + path.join(plan, `${name}.json`), + JSON.stringify({ + name: `sdk-${name}`, + kind: 'archive', + url: pin.url, + branch: 'oliphaunt-pinned', + commit: pin.sha256, + sha256: pin.sha256, + stripPrefix: `${name}-${pin.version}`, + }), + ); + } +} else if (operation === 'prepare' && !plan) { + for (const name of crates) prepare(name); +} else { + throw new Error('usage: prepare-sources.mts plan DIRECTORY | prepare'); +} diff --git a/src/wasix/runtime/engine/prepare-sources.sh b/src/wasix/runtime/engine/prepare-sources.sh new file mode 100644 index 000000000..c8af518fb --- /dev/null +++ b/src/wasix/runtime/engine/prepare-sources.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(git rev-parse --show-toplevel)" +source src/third-party/tools/fetch-sources.sh +engine_dir=src/wasix/runtime/engine +plan=target/oliphaunt-wasix/engine/source-plan +mkdir -p "$plan" +bun "$engine_dir/prepare-sources.mts" plan "$plan" +for name in wasmer wasmer-wasix-types wasmer-journal wasmer-wasix; do + fetch_source "$plan/$name.json" target/oliphaunt-sources/checkouts target/oliphaunt-sources/archives fetch +done +bun "$engine_dir/prepare-sources.mts" prepare diff --git a/src/wasix/runtime/engine/source.toml b/src/wasix/runtime/engine/source.toml new file mode 100644 index 000000000..6f8b03ec7 --- /dev/null +++ b/src/wasix/runtime/engine/source.toml @@ -0,0 +1,48 @@ +[wasmer] +version = "7.5.0" +url = "https://static.crates.io/crates/wasmer/wasmer-7.5.0.crate" +sha256 = "ddfde3972691f1082774c71c42e21add1d1838cb6482a6921a0ba8a993e0046a" +bytes = 235496 + +[wasmer-wasix] +version = "0.705.0" +url = "https://static.crates.io/crates/wasmer-wasix/wasmer-wasix-0.705.0.crate" +sha256 = "1d5bbce1be073ab1e40f5ed2468653832c22aecfd73e2ba763c41a5d831a4475" +bytes = 510422 + +[wasmer-wasix-types] +version = "0.705.0" +url = "https://static.crates.io/crates/wasmer-wasix-types/wasmer-wasix-types-0.705.0.crate" +sha256 = "910259427f2e3af0f2238cf4d2d3dbf284ca471cf02965f4635de7f4b2eee66e" +bytes = 47482 + +[wasmer-journal] +version = "0.705.0" +url = "https://static.crates.io/crates/wasmer-journal/wasmer-journal-0.705.0.crate" +sha256 = "d1ba6aab53ebc49f540787f2daf9a8865b0933de8a8d081b22a78b19259d279c" +bytes = 73753 + +[wee8-windows] +version = "11.9.7" +v8-version = "13.6.233.17" +url = "https://github.com/wasmerio/wee8-custom-builds/releases/download/11.9.7/v8-windows-amd64.tar.xz" +sha256 = "2aee8b6c3e8cecae2ce0325ac01b9bcaea4bef49e8f2aac599e1729d60c17285" +bytes = 151695100 +member = "lib/v8.lib" +member-sha256 = "04061d2035659457dcedb2e695ab3ef50de97d3ab71579b4fe824523c47f582a" +member-bytes = 1871746786 + +[v8-header] +url = "https://raw.githubusercontent.com/v8/v8/b0a55a7dad7f536cce1f9aaddba89894c8533946/third_party/wasm-api/wasm.hh" +sha256 = "18f49f653f79a9485d6bda1d650a9c29f538987b7092457e2bb95e8fa68aae76" +bytes = 18392 + +[v8-license] +url = "https://raw.githubusercontent.com/v8/v8/b0a55a7dad7f536cce1f9aaddba89894c8533946/LICENSE" +sha256 = "6ab33af8774a0f396ee3aeeb761e3229057682d6f9fa7f572e390c2cb3a6e509" +bytes = 3093 + +[wasmer-license] +url = "https://raw.githubusercontent.com/wasmerio/wasmer/82ff099e082da586b38af79986abbb8b677f8baf/LICENSE" +sha256 = "76dc7d305458d07478bc62669fe53dbfd3b94b95c5e00fbb45af1f492cbd7284" +bytes = 1097 diff --git a/src/wasix/runtime/engine/windows/bindings.rs b/src/wasix/runtime/engine/windows/bindings.rs new file mode 100644 index 000000000..a3d07075e --- /dev/null +++ b/src/wasix/runtime/engine/windows/bindings.rs @@ -0,0 +1,2042 @@ +// Generated from Wasmer 7.5.0 third-party/wee8/wasm.h for Windows x64. +// Only the WebAssembly C ABI is retained; regenerate when the pinned header changes. +pub type byte_t = ::std::os::raw::c_char; +pub type float32_t = f32; +pub type float64_t = f64; +pub type wasm_byte_t = byte_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_byte_vec_t { + pub size: usize, + pub data: *mut wasm_byte_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of wasm_byte_vec_t"][::std::mem::size_of::() - 16usize]; + ["Alignment of wasm_byte_vec_t"][::std::mem::align_of::() - 8usize]; + ["Offset of field: wasm_byte_vec_t::size"] + [::std::mem::offset_of!(wasm_byte_vec_t, size) - 0usize]; + ["Offset of field: wasm_byte_vec_t::data"] + [::std::mem::offset_of!(wasm_byte_vec_t, data) - 8usize]; +}; +impl Default for wasm_byte_vec_t { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_byte_vec_new_empty"] + pub fn wasm_byte_vec_new_empty(out: *mut wasm_byte_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_byte_vec_new_uninitialized"] + pub fn wasm_byte_vec_new_uninitialized(out: *mut wasm_byte_vec_t, arg1: usize); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_byte_vec_new"] + pub fn wasm_byte_vec_new(out: *mut wasm_byte_vec_t, arg1: usize, arg2: *const wasm_byte_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_byte_vec_copy"] + pub fn wasm_byte_vec_copy(out: *mut wasm_byte_vec_t, arg1: *const wasm_byte_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_byte_vec_delete"] + pub fn wasm_byte_vec_delete(arg1: *mut wasm_byte_vec_t); +} +pub type wasm_name_t = wasm_byte_vec_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_config_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_config_delete"] + pub fn wasm_config_delete(arg1: *mut wasm_config_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_config_new"] + pub fn wasm_config_new() -> *mut wasm_config_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_engine_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_engine_delete"] + pub fn wasm_engine_delete(arg1: *mut wasm_engine_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_engine_new"] + pub fn wasm_engine_new() -> *mut wasm_engine_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_engine_new_with_config"] + pub fn wasm_engine_new_with_config(arg1: *mut wasm_config_t) -> *mut wasm_engine_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_store_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_store_delete"] + pub fn wasm_store_delete(arg1: *mut wasm_store_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_store_new"] + pub fn wasm_store_new(arg1: *mut wasm_engine_t) -> *mut wasm_store_t; +} +pub type wasm_mutability_t = u8; +pub const wasm_mutability_enum_WASM_CONST: wasm_mutability_enum = 0; +pub const wasm_mutability_enum_WASM_VAR: wasm_mutability_enum = 1; +pub type wasm_mutability_enum = ::std::os::raw::c_int; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct wasm_limits_t { + pub min: u32, + pub max: u32, + pub shared: bool, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of wasm_limits_t"][::std::mem::size_of::() - 12usize]; + ["Alignment of wasm_limits_t"][::std::mem::align_of::() - 4usize]; + ["Offset of field: wasm_limits_t::min"][::std::mem::offset_of!(wasm_limits_t, min) - 0usize]; + ["Offset of field: wasm_limits_t::max"][::std::mem::offset_of!(wasm_limits_t, max) - 4usize]; + ["Offset of field: wasm_limits_t::shared"] + [::std::mem::offset_of!(wasm_limits_t, shared) - 8usize]; +}; +pub const wasm_limits_max_default: u32 = 4294967295; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_valtype_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_valtype_delete"] + pub fn wasm_valtype_delete(arg1: *mut wasm_valtype_t); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_valtype_vec_t { + pub size: usize, + pub data: *mut *mut wasm_valtype_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of wasm_valtype_vec_t"][::std::mem::size_of::() - 16usize]; + ["Alignment of wasm_valtype_vec_t"][::std::mem::align_of::() - 8usize]; + ["Offset of field: wasm_valtype_vec_t::size"] + [::std::mem::offset_of!(wasm_valtype_vec_t, size) - 0usize]; + ["Offset of field: wasm_valtype_vec_t::data"] + [::std::mem::offset_of!(wasm_valtype_vec_t, data) - 8usize]; +}; +impl Default for wasm_valtype_vec_t { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_valtype_vec_new_empty"] + pub fn wasm_valtype_vec_new_empty(out: *mut wasm_valtype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_valtype_vec_new_uninitialized"] + pub fn wasm_valtype_vec_new_uninitialized(out: *mut wasm_valtype_vec_t, arg1: usize); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_valtype_vec_new"] + pub fn wasm_valtype_vec_new( + out: *mut wasm_valtype_vec_t, + arg1: usize, + arg2: *const *mut wasm_valtype_t, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_valtype_vec_copy"] + pub fn wasm_valtype_vec_copy(out: *mut wasm_valtype_vec_t, arg1: *const wasm_valtype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_valtype_vec_delete"] + pub fn wasm_valtype_vec_delete(arg1: *mut wasm_valtype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_valtype_copy"] + pub fn wasm_valtype_copy(arg1: *const wasm_valtype_t) -> *mut wasm_valtype_t; +} +pub type wasm_valkind_t = u8; +pub const wasm_valkind_enum_WASM_I32: wasm_valkind_enum = 0; +pub const wasm_valkind_enum_WASM_I64: wasm_valkind_enum = 1; +pub const wasm_valkind_enum_WASM_F32: wasm_valkind_enum = 2; +pub const wasm_valkind_enum_WASM_F64: wasm_valkind_enum = 3; +pub const wasm_valkind_enum_WASM_V128: wasm_valkind_enum = 4; +pub const wasm_valkind_enum_WASM_EXTERNREF: wasm_valkind_enum = 128; +pub const wasm_valkind_enum_WASM_FUNCREF: wasm_valkind_enum = 129; +pub type wasm_valkind_enum = ::std::os::raw::c_int; +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_valtype_new"] + pub fn wasm_valtype_new(arg1: wasm_valkind_t) -> *mut wasm_valtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_valtype_kind"] + pub fn wasm_valtype_kind(arg1: *const wasm_valtype_t) -> wasm_valkind_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_functype_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_functype_delete"] + pub fn wasm_functype_delete(arg1: *mut wasm_functype_t); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_functype_vec_t { + pub size: usize, + pub data: *mut *mut wasm_functype_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of wasm_functype_vec_t"][::std::mem::size_of::() - 16usize]; + ["Alignment of wasm_functype_vec_t"][::std::mem::align_of::() - 8usize]; + ["Offset of field: wasm_functype_vec_t::size"] + [::std::mem::offset_of!(wasm_functype_vec_t, size) - 0usize]; + ["Offset of field: wasm_functype_vec_t::data"] + [::std::mem::offset_of!(wasm_functype_vec_t, data) - 8usize]; +}; +impl Default for wasm_functype_vec_t { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_functype_vec_new_empty"] + pub fn wasm_functype_vec_new_empty(out: *mut wasm_functype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_functype_vec_new_uninitialized"] + pub fn wasm_functype_vec_new_uninitialized(out: *mut wasm_functype_vec_t, arg1: usize); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_functype_vec_new"] + pub fn wasm_functype_vec_new( + out: *mut wasm_functype_vec_t, + arg1: usize, + arg2: *const *mut wasm_functype_t, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_functype_vec_copy"] + pub fn wasm_functype_vec_copy(out: *mut wasm_functype_vec_t, arg1: *const wasm_functype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_functype_vec_delete"] + pub fn wasm_functype_vec_delete(arg1: *mut wasm_functype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_functype_copy"] + pub fn wasm_functype_copy(arg1: *const wasm_functype_t) -> *mut wasm_functype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_functype_new"] + pub fn wasm_functype_new( + params: *mut wasm_valtype_vec_t, + results: *mut wasm_valtype_vec_t, + ) -> *mut wasm_functype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_functype_params"] + pub fn wasm_functype_params(arg1: *const wasm_functype_t) -> *const wasm_valtype_vec_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_functype_results"] + pub fn wasm_functype_results(arg1: *const wasm_functype_t) -> *const wasm_valtype_vec_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_globaltype_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_globaltype_delete"] + pub fn wasm_globaltype_delete(arg1: *mut wasm_globaltype_t); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_globaltype_vec_t { + pub size: usize, + pub data: *mut *mut wasm_globaltype_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of wasm_globaltype_vec_t"][::std::mem::size_of::() - 16usize]; + ["Alignment of wasm_globaltype_vec_t"] + [::std::mem::align_of::() - 8usize]; + ["Offset of field: wasm_globaltype_vec_t::size"] + [::std::mem::offset_of!(wasm_globaltype_vec_t, size) - 0usize]; + ["Offset of field: wasm_globaltype_vec_t::data"] + [::std::mem::offset_of!(wasm_globaltype_vec_t, data) - 8usize]; +}; +impl Default for wasm_globaltype_vec_t { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_globaltype_vec_new_empty"] + pub fn wasm_globaltype_vec_new_empty(out: *mut wasm_globaltype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_globaltype_vec_new_uninitialized"] + pub fn wasm_globaltype_vec_new_uninitialized(out: *mut wasm_globaltype_vec_t, arg1: usize); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_globaltype_vec_new"] + pub fn wasm_globaltype_vec_new( + out: *mut wasm_globaltype_vec_t, + arg1: usize, + arg2: *const *mut wasm_globaltype_t, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_globaltype_vec_copy"] + pub fn wasm_globaltype_vec_copy( + out: *mut wasm_globaltype_vec_t, + arg1: *const wasm_globaltype_vec_t, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_globaltype_vec_delete"] + pub fn wasm_globaltype_vec_delete(arg1: *mut wasm_globaltype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_globaltype_copy"] + pub fn wasm_globaltype_copy(arg1: *const wasm_globaltype_t) -> *mut wasm_globaltype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_globaltype_new"] + pub fn wasm_globaltype_new( + arg1: *mut wasm_valtype_t, + arg2: wasm_mutability_t, + ) -> *mut wasm_globaltype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_globaltype_content"] + pub fn wasm_globaltype_content(arg1: *const wasm_globaltype_t) -> *const wasm_valtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_globaltype_mutability"] + pub fn wasm_globaltype_mutability(arg1: *const wasm_globaltype_t) -> wasm_mutability_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_tagtype_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tagtype_delete"] + pub fn wasm_tagtype_delete(arg1: *mut wasm_tagtype_t); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_tagtype_vec_t { + pub size: usize, + pub data: *mut *mut wasm_tagtype_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of wasm_tagtype_vec_t"][::std::mem::size_of::() - 16usize]; + ["Alignment of wasm_tagtype_vec_t"][::std::mem::align_of::() - 8usize]; + ["Offset of field: wasm_tagtype_vec_t::size"] + [::std::mem::offset_of!(wasm_tagtype_vec_t, size) - 0usize]; + ["Offset of field: wasm_tagtype_vec_t::data"] + [::std::mem::offset_of!(wasm_tagtype_vec_t, data) - 8usize]; +}; +impl Default for wasm_tagtype_vec_t { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tagtype_vec_new_empty"] + pub fn wasm_tagtype_vec_new_empty(out: *mut wasm_tagtype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tagtype_vec_new_uninitialized"] + pub fn wasm_tagtype_vec_new_uninitialized(out: *mut wasm_tagtype_vec_t, arg1: usize); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tagtype_vec_new"] + pub fn wasm_tagtype_vec_new( + out: *mut wasm_tagtype_vec_t, + arg1: usize, + arg2: *const *mut wasm_tagtype_t, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tagtype_vec_copy"] + pub fn wasm_tagtype_vec_copy(out: *mut wasm_tagtype_vec_t, arg1: *const wasm_tagtype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tagtype_vec_delete"] + pub fn wasm_tagtype_vec_delete(arg1: *mut wasm_tagtype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tagtype_copy"] + pub fn wasm_tagtype_copy(arg1: *const wasm_tagtype_t) -> *mut wasm_tagtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tagtype_new"] + pub fn wasm_tagtype_new(params: *mut wasm_valtype_vec_t) -> *mut wasm_tagtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tagtype_params"] + pub fn wasm_tagtype_params(arg1: *const wasm_tagtype_t) -> *const wasm_valtype_vec_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_tabletype_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tabletype_delete"] + pub fn wasm_tabletype_delete(arg1: *mut wasm_tabletype_t); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_tabletype_vec_t { + pub size: usize, + pub data: *mut *mut wasm_tabletype_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of wasm_tabletype_vec_t"][::std::mem::size_of::() - 16usize]; + ["Alignment of wasm_tabletype_vec_t"][::std::mem::align_of::() - 8usize]; + ["Offset of field: wasm_tabletype_vec_t::size"] + [::std::mem::offset_of!(wasm_tabletype_vec_t, size) - 0usize]; + ["Offset of field: wasm_tabletype_vec_t::data"] + [::std::mem::offset_of!(wasm_tabletype_vec_t, data) - 8usize]; +}; +impl Default for wasm_tabletype_vec_t { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tabletype_vec_new_empty"] + pub fn wasm_tabletype_vec_new_empty(out: *mut wasm_tabletype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tabletype_vec_new_uninitialized"] + pub fn wasm_tabletype_vec_new_uninitialized(out: *mut wasm_tabletype_vec_t, arg1: usize); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tabletype_vec_new"] + pub fn wasm_tabletype_vec_new( + out: *mut wasm_tabletype_vec_t, + arg1: usize, + arg2: *const *mut wasm_tabletype_t, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tabletype_vec_copy"] + pub fn wasm_tabletype_vec_copy( + out: *mut wasm_tabletype_vec_t, + arg1: *const wasm_tabletype_vec_t, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tabletype_vec_delete"] + pub fn wasm_tabletype_vec_delete(arg1: *mut wasm_tabletype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tabletype_copy"] + pub fn wasm_tabletype_copy(arg1: *const wasm_tabletype_t) -> *mut wasm_tabletype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tabletype_new"] + pub fn wasm_tabletype_new( + arg1: *mut wasm_valtype_t, + arg2: *const wasm_limits_t, + ) -> *mut wasm_tabletype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tabletype_element"] + pub fn wasm_tabletype_element(arg1: *const wasm_tabletype_t) -> *const wasm_valtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tabletype_limits"] + pub fn wasm_tabletype_limits(arg1: *const wasm_tabletype_t) -> *const wasm_limits_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_memorytype_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memorytype_delete"] + pub fn wasm_memorytype_delete(arg1: *mut wasm_memorytype_t); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_memorytype_vec_t { + pub size: usize, + pub data: *mut *mut wasm_memorytype_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of wasm_memorytype_vec_t"][::std::mem::size_of::() - 16usize]; + ["Alignment of wasm_memorytype_vec_t"] + [::std::mem::align_of::() - 8usize]; + ["Offset of field: wasm_memorytype_vec_t::size"] + [::std::mem::offset_of!(wasm_memorytype_vec_t, size) - 0usize]; + ["Offset of field: wasm_memorytype_vec_t::data"] + [::std::mem::offset_of!(wasm_memorytype_vec_t, data) - 8usize]; +}; +impl Default for wasm_memorytype_vec_t { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memorytype_vec_new_empty"] + pub fn wasm_memorytype_vec_new_empty(out: *mut wasm_memorytype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memorytype_vec_new_uninitialized"] + pub fn wasm_memorytype_vec_new_uninitialized(out: *mut wasm_memorytype_vec_t, arg1: usize); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memorytype_vec_new"] + pub fn wasm_memorytype_vec_new( + out: *mut wasm_memorytype_vec_t, + arg1: usize, + arg2: *const *mut wasm_memorytype_t, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memorytype_vec_copy"] + pub fn wasm_memorytype_vec_copy( + out: *mut wasm_memorytype_vec_t, + arg1: *const wasm_memorytype_vec_t, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memorytype_vec_delete"] + pub fn wasm_memorytype_vec_delete(arg1: *mut wasm_memorytype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memorytype_copy"] + pub fn wasm_memorytype_copy(arg1: *const wasm_memorytype_t) -> *mut wasm_memorytype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memorytype_new"] + pub fn wasm_memorytype_new(arg1: *const wasm_limits_t) -> *mut wasm_memorytype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memorytype_limits"] + pub fn wasm_memorytype_limits(arg1: *const wasm_memorytype_t) -> *const wasm_limits_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_externtype_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_delete"] + pub fn wasm_externtype_delete(arg1: *mut wasm_externtype_t); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_externtype_vec_t { + pub size: usize, + pub data: *mut *mut wasm_externtype_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of wasm_externtype_vec_t"][::std::mem::size_of::() - 16usize]; + ["Alignment of wasm_externtype_vec_t"] + [::std::mem::align_of::() - 8usize]; + ["Offset of field: wasm_externtype_vec_t::size"] + [::std::mem::offset_of!(wasm_externtype_vec_t, size) - 0usize]; + ["Offset of field: wasm_externtype_vec_t::data"] + [::std::mem::offset_of!(wasm_externtype_vec_t, data) - 8usize]; +}; +impl Default for wasm_externtype_vec_t { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_vec_new_empty"] + pub fn wasm_externtype_vec_new_empty(out: *mut wasm_externtype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_vec_new_uninitialized"] + pub fn wasm_externtype_vec_new_uninitialized(out: *mut wasm_externtype_vec_t, arg1: usize); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_vec_new"] + pub fn wasm_externtype_vec_new( + out: *mut wasm_externtype_vec_t, + arg1: usize, + arg2: *const *mut wasm_externtype_t, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_vec_copy"] + pub fn wasm_externtype_vec_copy( + out: *mut wasm_externtype_vec_t, + arg1: *const wasm_externtype_vec_t, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_vec_delete"] + pub fn wasm_externtype_vec_delete(arg1: *mut wasm_externtype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_copy"] + pub fn wasm_externtype_copy(arg1: *const wasm_externtype_t) -> *mut wasm_externtype_t; +} +pub type wasm_externkind_t = u8; +pub const wasm_externkind_enum_WASM_EXTERN_FUNC: wasm_externkind_enum = 0; +pub const wasm_externkind_enum_WASM_EXTERN_GLOBAL: wasm_externkind_enum = 1; +pub const wasm_externkind_enum_WASM_EXTERN_TABLE: wasm_externkind_enum = 2; +pub const wasm_externkind_enum_WASM_EXTERN_MEMORY: wasm_externkind_enum = 3; +pub const wasm_externkind_enum_WASM_EXTERN_TAG: wasm_externkind_enum = 4; +pub type wasm_externkind_enum = ::std::os::raw::c_int; +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_kind"] + pub fn wasm_externtype_kind(arg1: *const wasm_externtype_t) -> wasm_externkind_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_functype_as_externtype"] + pub fn wasm_functype_as_externtype(arg1: *mut wasm_functype_t) -> *mut wasm_externtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_globaltype_as_externtype"] + pub fn wasm_globaltype_as_externtype(arg1: *mut wasm_globaltype_t) -> *mut wasm_externtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tagtype_as_externtype"] + pub fn wasm_tagtype_as_externtype(arg1: *mut wasm_tagtype_t) -> *mut wasm_externtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tabletype_as_externtype"] + pub fn wasm_tabletype_as_externtype(arg1: *mut wasm_tabletype_t) -> *mut wasm_externtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memorytype_as_externtype"] + pub fn wasm_memorytype_as_externtype(arg1: *mut wasm_memorytype_t) -> *mut wasm_externtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_as_functype"] + pub fn wasm_externtype_as_functype(arg1: *mut wasm_externtype_t) -> *mut wasm_functype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_as_globaltype"] + pub fn wasm_externtype_as_globaltype(arg1: *mut wasm_externtype_t) -> *mut wasm_globaltype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_as_tagtype"] + pub fn wasm_externtype_as_tagtype(arg1: *mut wasm_externtype_t) -> *mut wasm_tagtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_as_tabletype"] + pub fn wasm_externtype_as_tabletype(arg1: *mut wasm_externtype_t) -> *mut wasm_tabletype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_as_memorytype"] + pub fn wasm_externtype_as_memorytype(arg1: *mut wasm_externtype_t) -> *mut wasm_memorytype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_functype_as_externtype_const"] + pub fn wasm_functype_as_externtype_const( + arg1: *const wasm_functype_t, + ) -> *const wasm_externtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_globaltype_as_externtype_const"] + pub fn wasm_globaltype_as_externtype_const( + arg1: *const wasm_globaltype_t, + ) -> *const wasm_externtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tagtype_as_externtype_const"] + pub fn wasm_tagtype_as_externtype_const( + arg1: *const wasm_tagtype_t, + ) -> *const wasm_externtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tabletype_as_externtype_const"] + pub fn wasm_tabletype_as_externtype_const( + arg1: *const wasm_tabletype_t, + ) -> *const wasm_externtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memorytype_as_externtype_const"] + pub fn wasm_memorytype_as_externtype_const( + arg1: *const wasm_memorytype_t, + ) -> *const wasm_externtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_as_functype_const"] + pub fn wasm_externtype_as_functype_const( + arg1: *const wasm_externtype_t, + ) -> *const wasm_functype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_as_globaltype_const"] + pub fn wasm_externtype_as_globaltype_const( + arg1: *const wasm_externtype_t, + ) -> *const wasm_globaltype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_as_tagtype_const"] + pub fn wasm_externtype_as_tagtype_const( + arg1: *const wasm_externtype_t, + ) -> *const wasm_tagtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_as_tabletype_const"] + pub fn wasm_externtype_as_tabletype_const( + arg1: *const wasm_externtype_t, + ) -> *const wasm_tabletype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_externtype_as_memorytype_const"] + pub fn wasm_externtype_as_memorytype_const( + arg1: *const wasm_externtype_t, + ) -> *const wasm_memorytype_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_importtype_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_importtype_delete"] + pub fn wasm_importtype_delete(arg1: *mut wasm_importtype_t); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_importtype_vec_t { + pub size: usize, + pub data: *mut *mut wasm_importtype_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of wasm_importtype_vec_t"][::std::mem::size_of::() - 16usize]; + ["Alignment of wasm_importtype_vec_t"] + [::std::mem::align_of::() - 8usize]; + ["Offset of field: wasm_importtype_vec_t::size"] + [::std::mem::offset_of!(wasm_importtype_vec_t, size) - 0usize]; + ["Offset of field: wasm_importtype_vec_t::data"] + [::std::mem::offset_of!(wasm_importtype_vec_t, data) - 8usize]; +}; +impl Default for wasm_importtype_vec_t { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_importtype_vec_new_empty"] + pub fn wasm_importtype_vec_new_empty(out: *mut wasm_importtype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_importtype_vec_new_uninitialized"] + pub fn wasm_importtype_vec_new_uninitialized(out: *mut wasm_importtype_vec_t, arg1: usize); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_importtype_vec_new"] + pub fn wasm_importtype_vec_new( + out: *mut wasm_importtype_vec_t, + arg1: usize, + arg2: *const *mut wasm_importtype_t, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_importtype_vec_copy"] + pub fn wasm_importtype_vec_copy( + out: *mut wasm_importtype_vec_t, + arg1: *const wasm_importtype_vec_t, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_importtype_vec_delete"] + pub fn wasm_importtype_vec_delete(arg1: *mut wasm_importtype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_importtype_copy"] + pub fn wasm_importtype_copy(arg1: *const wasm_importtype_t) -> *mut wasm_importtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_importtype_new"] + pub fn wasm_importtype_new( + module: *mut wasm_name_t, + name: *mut wasm_name_t, + arg1: *mut wasm_externtype_t, + ) -> *mut wasm_importtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_importtype_module"] + pub fn wasm_importtype_module(arg1: *const wasm_importtype_t) -> *const wasm_name_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_importtype_name"] + pub fn wasm_importtype_name(arg1: *const wasm_importtype_t) -> *const wasm_name_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_importtype_type"] + pub fn wasm_importtype_type(arg1: *const wasm_importtype_t) -> *const wasm_externtype_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_exporttype_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_exporttype_delete"] + pub fn wasm_exporttype_delete(arg1: *mut wasm_exporttype_t); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_exporttype_vec_t { + pub size: usize, + pub data: *mut *mut wasm_exporttype_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of wasm_exporttype_vec_t"][::std::mem::size_of::() - 16usize]; + ["Alignment of wasm_exporttype_vec_t"] + [::std::mem::align_of::() - 8usize]; + ["Offset of field: wasm_exporttype_vec_t::size"] + [::std::mem::offset_of!(wasm_exporttype_vec_t, size) - 0usize]; + ["Offset of field: wasm_exporttype_vec_t::data"] + [::std::mem::offset_of!(wasm_exporttype_vec_t, data) - 8usize]; +}; +impl Default for wasm_exporttype_vec_t { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_exporttype_vec_new_empty"] + pub fn wasm_exporttype_vec_new_empty(out: *mut wasm_exporttype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_exporttype_vec_new_uninitialized"] + pub fn wasm_exporttype_vec_new_uninitialized(out: *mut wasm_exporttype_vec_t, arg1: usize); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_exporttype_vec_new"] + pub fn wasm_exporttype_vec_new( + out: *mut wasm_exporttype_vec_t, + arg1: usize, + arg2: *const *mut wasm_exporttype_t, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_exporttype_vec_copy"] + pub fn wasm_exporttype_vec_copy( + out: *mut wasm_exporttype_vec_t, + arg1: *const wasm_exporttype_vec_t, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_exporttype_vec_delete"] + pub fn wasm_exporttype_vec_delete(arg1: *mut wasm_exporttype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_exporttype_copy"] + pub fn wasm_exporttype_copy(arg1: *const wasm_exporttype_t) -> *mut wasm_exporttype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_exporttype_new"] + pub fn wasm_exporttype_new( + arg1: *mut wasm_name_t, + arg2: *mut wasm_externtype_t, + ) -> *mut wasm_exporttype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_exporttype_name"] + pub fn wasm_exporttype_name(arg1: *const wasm_exporttype_t) -> *const wasm_name_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_exporttype_type"] + pub fn wasm_exporttype_type(arg1: *const wasm_exporttype_t) -> *const wasm_externtype_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_ref_t { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct wasm_v128_t { + pub bytes: [u8; 16usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of wasm_v128_t"][::std::mem::size_of::() - 16usize]; + ["Alignment of wasm_v128_t"][::std::mem::align_of::() - 1usize]; + ["Offset of field: wasm_v128_t::bytes"][::std::mem::offset_of!(wasm_v128_t, bytes) - 0usize]; +}; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct wasm_val_t { + pub kind: wasm_valkind_t, + pub of: wasm_val_t__bindgen_ty_1, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union wasm_val_t__bindgen_ty_1 { + pub i32_: i32, + pub i64_: i64, + pub f32_: float32_t, + pub f64_: float64_t, + pub v128: wasm_v128_t, + pub ref_: *mut wasm_ref_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of wasm_val_t__bindgen_ty_1"] + [::std::mem::size_of::() - 16usize]; + ["Alignment of wasm_val_t__bindgen_ty_1"] + [::std::mem::align_of::() - 8usize]; + ["Offset of field: wasm_val_t__bindgen_ty_1::i32_"] + [::std::mem::offset_of!(wasm_val_t__bindgen_ty_1, i32_) - 0usize]; + ["Offset of field: wasm_val_t__bindgen_ty_1::i64_"] + [::std::mem::offset_of!(wasm_val_t__bindgen_ty_1, i64_) - 0usize]; + ["Offset of field: wasm_val_t__bindgen_ty_1::f32_"] + [::std::mem::offset_of!(wasm_val_t__bindgen_ty_1, f32_) - 0usize]; + ["Offset of field: wasm_val_t__bindgen_ty_1::f64_"] + [::std::mem::offset_of!(wasm_val_t__bindgen_ty_1, f64_) - 0usize]; + ["Offset of field: wasm_val_t__bindgen_ty_1::v128"] + [::std::mem::offset_of!(wasm_val_t__bindgen_ty_1, v128) - 0usize]; + ["Offset of field: wasm_val_t__bindgen_ty_1::ref_"] + [::std::mem::offset_of!(wasm_val_t__bindgen_ty_1, ref_) - 0usize]; +}; +impl Default for wasm_val_t__bindgen_ty_1 { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of wasm_val_t"][::std::mem::size_of::() - 24usize]; + ["Alignment of wasm_val_t"][::std::mem::align_of::() - 8usize]; + ["Offset of field: wasm_val_t::kind"][::std::mem::offset_of!(wasm_val_t, kind) - 0usize]; + ["Offset of field: wasm_val_t::of"][::std::mem::offset_of!(wasm_val_t, of) - 8usize]; +}; +impl Default for wasm_val_t { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_val_delete"] + pub fn wasm_val_delete(v: *mut wasm_val_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_val_copy"] + pub fn wasm_val_copy(out: *mut wasm_val_t, arg1: *const wasm_val_t); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_val_vec_t { + pub size: usize, + pub data: *mut wasm_val_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of wasm_val_vec_t"][::std::mem::size_of::() - 16usize]; + ["Alignment of wasm_val_vec_t"][::std::mem::align_of::() - 8usize]; + ["Offset of field: wasm_val_vec_t::size"] + [::std::mem::offset_of!(wasm_val_vec_t, size) - 0usize]; + ["Offset of field: wasm_val_vec_t::data"] + [::std::mem::offset_of!(wasm_val_vec_t, data) - 8usize]; +}; +impl Default for wasm_val_vec_t { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_val_vec_new_empty"] + pub fn wasm_val_vec_new_empty(out: *mut wasm_val_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_val_vec_new_uninitialized"] + pub fn wasm_val_vec_new_uninitialized(out: *mut wasm_val_vec_t, arg1: usize); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_val_vec_new"] + pub fn wasm_val_vec_new(out: *mut wasm_val_vec_t, arg1: usize, arg2: *const wasm_val_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_val_vec_copy"] + pub fn wasm_val_vec_copy(out: *mut wasm_val_vec_t, arg1: *const wasm_val_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_val_vec_delete"] + pub fn wasm_val_vec_delete(arg1: *mut wasm_val_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_delete"] + pub fn wasm_ref_delete(arg1: *mut wasm_ref_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_copy"] + pub fn wasm_ref_copy(arg1: *const wasm_ref_t) -> *mut wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_same"] + pub fn wasm_ref_same(arg1: *const wasm_ref_t, arg2: *const wasm_ref_t) -> bool; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_get_host_info"] + pub fn wasm_ref_get_host_info(arg1: *const wasm_ref_t) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_set_host_info"] + pub fn wasm_ref_set_host_info(arg1: *mut wasm_ref_t, arg2: *mut ::std::os::raw::c_void); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_set_host_info_with_finalizer"] + pub fn wasm_ref_set_host_info_with_finalizer( + arg1: *mut wasm_ref_t, + arg2: *mut ::std::os::raw::c_void, + arg3: ::std::option::Option, + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_frame_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_frame_delete"] + pub fn wasm_frame_delete(arg1: *mut wasm_frame_t); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_frame_vec_t { + pub size: usize, + pub data: *mut *mut wasm_frame_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of wasm_frame_vec_t"][::std::mem::size_of::() - 16usize]; + ["Alignment of wasm_frame_vec_t"][::std::mem::align_of::() - 8usize]; + ["Offset of field: wasm_frame_vec_t::size"] + [::std::mem::offset_of!(wasm_frame_vec_t, size) - 0usize]; + ["Offset of field: wasm_frame_vec_t::data"] + [::std::mem::offset_of!(wasm_frame_vec_t, data) - 8usize]; +}; +impl Default for wasm_frame_vec_t { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_frame_vec_new_empty"] + pub fn wasm_frame_vec_new_empty(out: *mut wasm_frame_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_frame_vec_new_uninitialized"] + pub fn wasm_frame_vec_new_uninitialized(out: *mut wasm_frame_vec_t, arg1: usize); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_frame_vec_new"] + pub fn wasm_frame_vec_new( + out: *mut wasm_frame_vec_t, + arg1: usize, + arg2: *const *mut wasm_frame_t, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_frame_vec_copy"] + pub fn wasm_frame_vec_copy(out: *mut wasm_frame_vec_t, arg1: *const wasm_frame_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_frame_vec_delete"] + pub fn wasm_frame_vec_delete(arg1: *mut wasm_frame_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_frame_copy"] + pub fn wasm_frame_copy(arg1: *const wasm_frame_t) -> *mut wasm_frame_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_instance_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_frame_instance"] + pub fn wasm_frame_instance(arg1: *const wasm_frame_t) -> *mut wasm_instance_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_frame_func_index"] + pub fn wasm_frame_func_index(arg1: *const wasm_frame_t) -> u32; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_frame_func_offset"] + pub fn wasm_frame_func_offset(arg1: *const wasm_frame_t) -> usize; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_frame_module_offset"] + pub fn wasm_frame_module_offset(arg1: *const wasm_frame_t) -> usize; +} +pub type wasm_message_t = wasm_name_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_trap_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_trap_delete"] + pub fn wasm_trap_delete(arg1: *mut wasm_trap_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_trap_copy"] + pub fn wasm_trap_copy(arg1: *const wasm_trap_t) -> *mut wasm_trap_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_trap_same"] + pub fn wasm_trap_same(arg1: *const wasm_trap_t, arg2: *const wasm_trap_t) -> bool; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_trap_get_host_info"] + pub fn wasm_trap_get_host_info(arg1: *const wasm_trap_t) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_trap_set_host_info"] + pub fn wasm_trap_set_host_info(arg1: *mut wasm_trap_t, arg2: *mut ::std::os::raw::c_void); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_trap_set_host_info_with_finalizer"] + pub fn wasm_trap_set_host_info_with_finalizer( + arg1: *mut wasm_trap_t, + arg2: *mut ::std::os::raw::c_void, + arg3: ::std::option::Option, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_trap_as_ref"] + pub fn wasm_trap_as_ref(arg1: *mut wasm_trap_t) -> *mut wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_trap"] + pub fn wasm_ref_as_trap(arg1: *mut wasm_ref_t) -> *mut wasm_trap_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_trap_as_ref_const"] + pub fn wasm_trap_as_ref_const(arg1: *const wasm_trap_t) -> *const wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_trap_const"] + pub fn wasm_ref_as_trap_const(arg1: *const wasm_ref_t) -> *const wasm_trap_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_trap_new"] + pub fn wasm_trap_new(store: *mut wasm_store_t, arg1: *const wasm_message_t) + -> *mut wasm_trap_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_trap_message"] + pub fn wasm_trap_message(arg1: *const wasm_trap_t, out: *mut wasm_message_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_trap_origin"] + pub fn wasm_trap_origin(arg1: *const wasm_trap_t) -> *mut wasm_frame_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_trap_trace"] + pub fn wasm_trap_trace(arg1: *const wasm_trap_t, out: *mut wasm_frame_vec_t); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_foreign_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_foreign_delete"] + pub fn wasm_foreign_delete(arg1: *mut wasm_foreign_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_foreign_copy"] + pub fn wasm_foreign_copy(arg1: *const wasm_foreign_t) -> *mut wasm_foreign_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_foreign_same"] + pub fn wasm_foreign_same(arg1: *const wasm_foreign_t, arg2: *const wasm_foreign_t) -> bool; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_foreign_get_host_info"] + pub fn wasm_foreign_get_host_info(arg1: *const wasm_foreign_t) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_foreign_set_host_info"] + pub fn wasm_foreign_set_host_info(arg1: *mut wasm_foreign_t, arg2: *mut ::std::os::raw::c_void); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_foreign_set_host_info_with_finalizer"] + pub fn wasm_foreign_set_host_info_with_finalizer( + arg1: *mut wasm_foreign_t, + arg2: *mut ::std::os::raw::c_void, + arg3: ::std::option::Option, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_foreign_as_ref"] + pub fn wasm_foreign_as_ref(arg1: *mut wasm_foreign_t) -> *mut wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_foreign"] + pub fn wasm_ref_as_foreign(arg1: *mut wasm_ref_t) -> *mut wasm_foreign_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_foreign_as_ref_const"] + pub fn wasm_foreign_as_ref_const(arg1: *const wasm_foreign_t) -> *const wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_foreign_const"] + pub fn wasm_ref_as_foreign_const(arg1: *const wasm_ref_t) -> *const wasm_foreign_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_foreign_new"] + pub fn wasm_foreign_new(arg1: *mut wasm_store_t) -> *mut wasm_foreign_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_module_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_module_delete"] + pub fn wasm_module_delete(arg1: *mut wasm_module_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_module_copy"] + pub fn wasm_module_copy(arg1: *const wasm_module_t) -> *mut wasm_module_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_module_same"] + pub fn wasm_module_same(arg1: *const wasm_module_t, arg2: *const wasm_module_t) -> bool; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_module_get_host_info"] + pub fn wasm_module_get_host_info(arg1: *const wasm_module_t) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_module_set_host_info"] + pub fn wasm_module_set_host_info(arg1: *mut wasm_module_t, arg2: *mut ::std::os::raw::c_void); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_module_set_host_info_with_finalizer"] + pub fn wasm_module_set_host_info_with_finalizer( + arg1: *mut wasm_module_t, + arg2: *mut ::std::os::raw::c_void, + arg3: ::std::option::Option, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_module_as_ref"] + pub fn wasm_module_as_ref(arg1: *mut wasm_module_t) -> *mut wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_module"] + pub fn wasm_ref_as_module(arg1: *mut wasm_ref_t) -> *mut wasm_module_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_module_as_ref_const"] + pub fn wasm_module_as_ref_const(arg1: *const wasm_module_t) -> *const wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_module_const"] + pub fn wasm_ref_as_module_const(arg1: *const wasm_ref_t) -> *const wasm_module_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_shared_module_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_shared_module_delete"] + pub fn wasm_shared_module_delete(arg1: *mut wasm_shared_module_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_module_share"] + pub fn wasm_module_share(arg1: *const wasm_module_t) -> *mut wasm_shared_module_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_module_obtain"] + pub fn wasm_module_obtain( + arg1: *mut wasm_store_t, + arg2: *const wasm_shared_module_t, + ) -> *mut wasm_module_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_module_new"] + pub fn wasm_module_new( + arg1: *mut wasm_store_t, + binary: *const wasm_byte_vec_t, + ) -> *mut wasm_module_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_module_validate"] + pub fn wasm_module_validate(arg1: *mut wasm_store_t, binary: *const wasm_byte_vec_t) -> bool; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_module_imports"] + pub fn wasm_module_imports(arg1: *const wasm_module_t, out: *mut wasm_importtype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_module_exports"] + pub fn wasm_module_exports(arg1: *const wasm_module_t, out: *mut wasm_exporttype_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_module_serialize"] + pub fn wasm_module_serialize(arg1: *const wasm_module_t, out: *mut wasm_byte_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_module_deserialize"] + pub fn wasm_module_deserialize( + arg1: *mut wasm_store_t, + arg2: *const wasm_byte_vec_t, + ) -> *mut wasm_module_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_func_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_func_delete"] + pub fn wasm_func_delete(arg1: *mut wasm_func_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_func_copy"] + pub fn wasm_func_copy(arg1: *const wasm_func_t) -> *mut wasm_func_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_func_same"] + pub fn wasm_func_same(arg1: *const wasm_func_t, arg2: *const wasm_func_t) -> bool; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_func_get_host_info"] + pub fn wasm_func_get_host_info(arg1: *const wasm_func_t) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_func_set_host_info"] + pub fn wasm_func_set_host_info(arg1: *mut wasm_func_t, arg2: *mut ::std::os::raw::c_void); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_func_set_host_info_with_finalizer"] + pub fn wasm_func_set_host_info_with_finalizer( + arg1: *mut wasm_func_t, + arg2: *mut ::std::os::raw::c_void, + arg3: ::std::option::Option, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_func_as_ref"] + pub fn wasm_func_as_ref(arg1: *mut wasm_func_t) -> *mut wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_func"] + pub fn wasm_ref_as_func(arg1: *mut wasm_ref_t) -> *mut wasm_func_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_func_as_ref_const"] + pub fn wasm_func_as_ref_const(arg1: *const wasm_func_t) -> *const wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_func_const"] + pub fn wasm_ref_as_func_const(arg1: *const wasm_ref_t) -> *const wasm_func_t; +} +pub type wasm_func_callback_t = ::std::option::Option< + unsafe extern "C" fn( + args: *const wasm_val_vec_t, + results: *mut wasm_val_vec_t, + ) -> *mut wasm_trap_t, +>; +pub type wasm_func_callback_with_env_t = ::std::option::Option< + unsafe extern "C" fn( + env: *mut ::std::os::raw::c_void, + args: *const wasm_val_vec_t, + results: *mut wasm_val_vec_t, + ) -> *mut wasm_trap_t, +>; +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_func_new"] + pub fn wasm_func_new( + arg1: *mut wasm_store_t, + arg2: *const wasm_functype_t, + arg3: wasm_func_callback_t, + ) -> *mut wasm_func_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_func_new_with_env"] + pub fn wasm_func_new_with_env( + arg1: *mut wasm_store_t, + type_: *const wasm_functype_t, + arg2: wasm_func_callback_with_env_t, + env: *mut ::std::os::raw::c_void, + finalizer: ::std::option::Option, + ) -> *mut wasm_func_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_func_type"] + pub fn wasm_func_type(arg1: *const wasm_func_t) -> *mut wasm_functype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_func_param_arity"] + pub fn wasm_func_param_arity(arg1: *const wasm_func_t) -> usize; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_func_result_arity"] + pub fn wasm_func_result_arity(arg1: *const wasm_func_t) -> usize; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_func_call"] + pub fn wasm_func_call( + arg1: *const wasm_func_t, + args: *const wasm_val_vec_t, + results: *mut wasm_val_vec_t, + ) -> *mut wasm_trap_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_global_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_global_delete"] + pub fn wasm_global_delete(arg1: *mut wasm_global_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_global_copy"] + pub fn wasm_global_copy(arg1: *const wasm_global_t) -> *mut wasm_global_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_global_same"] + pub fn wasm_global_same(arg1: *const wasm_global_t, arg2: *const wasm_global_t) -> bool; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_global_get_host_info"] + pub fn wasm_global_get_host_info(arg1: *const wasm_global_t) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_global_set_host_info"] + pub fn wasm_global_set_host_info(arg1: *mut wasm_global_t, arg2: *mut ::std::os::raw::c_void); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_global_set_host_info_with_finalizer"] + pub fn wasm_global_set_host_info_with_finalizer( + arg1: *mut wasm_global_t, + arg2: *mut ::std::os::raw::c_void, + arg3: ::std::option::Option, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_global_as_ref"] + pub fn wasm_global_as_ref(arg1: *mut wasm_global_t) -> *mut wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_global"] + pub fn wasm_ref_as_global(arg1: *mut wasm_ref_t) -> *mut wasm_global_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_global_as_ref_const"] + pub fn wasm_global_as_ref_const(arg1: *const wasm_global_t) -> *const wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_global_const"] + pub fn wasm_ref_as_global_const(arg1: *const wasm_ref_t) -> *const wasm_global_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_global_new"] + pub fn wasm_global_new( + arg1: *mut wasm_store_t, + arg2: *const wasm_globaltype_t, + arg3: *const wasm_val_t, + ) -> *mut wasm_global_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_global_type"] + pub fn wasm_global_type(arg1: *const wasm_global_t) -> *mut wasm_globaltype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_global_get"] + pub fn wasm_global_get(arg1: *const wasm_global_t, out: *mut wasm_val_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_global_set"] + pub fn wasm_global_set(arg1: *mut wasm_global_t, arg2: *const wasm_val_t); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_tag_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tag_delete"] + pub fn wasm_tag_delete(arg1: *mut wasm_tag_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tag_copy"] + pub fn wasm_tag_copy(arg1: *const wasm_tag_t) -> *mut wasm_tag_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tag_same"] + pub fn wasm_tag_same(arg1: *const wasm_tag_t, arg2: *const wasm_tag_t) -> bool; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tag_get_host_info"] + pub fn wasm_tag_get_host_info(arg1: *const wasm_tag_t) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tag_set_host_info"] + pub fn wasm_tag_set_host_info(arg1: *mut wasm_tag_t, arg2: *mut ::std::os::raw::c_void); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tag_set_host_info_with_finalizer"] + pub fn wasm_tag_set_host_info_with_finalizer( + arg1: *mut wasm_tag_t, + arg2: *mut ::std::os::raw::c_void, + arg3: ::std::option::Option, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tag_as_ref"] + pub fn wasm_tag_as_ref(arg1: *mut wasm_tag_t) -> *mut wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_tag"] + pub fn wasm_ref_as_tag(arg1: *mut wasm_ref_t) -> *mut wasm_tag_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tag_as_ref_const"] + pub fn wasm_tag_as_ref_const(arg1: *const wasm_tag_t) -> *const wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_tag_const"] + pub fn wasm_ref_as_tag_const(arg1: *const wasm_ref_t) -> *const wasm_tag_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tag_new"] + pub fn wasm_tag_new(arg1: *mut wasm_store_t, arg2: *const wasm_tagtype_t) -> *mut wasm_tag_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tag_type"] + pub fn wasm_tag_type(arg1: *const wasm_tag_t) -> *mut wasm_tagtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tag_get"] + pub fn wasm_tag_get(arg1: *const wasm_tag_t, out: *mut wasm_val_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tag_set"] + pub fn wasm_tag_set(arg1: *mut wasm_tag_t, arg2: *const wasm_val_t); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_table_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_table_delete"] + pub fn wasm_table_delete(arg1: *mut wasm_table_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_table_copy"] + pub fn wasm_table_copy(arg1: *const wasm_table_t) -> *mut wasm_table_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_table_same"] + pub fn wasm_table_same(arg1: *const wasm_table_t, arg2: *const wasm_table_t) -> bool; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_table_get_host_info"] + pub fn wasm_table_get_host_info(arg1: *const wasm_table_t) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_table_set_host_info"] + pub fn wasm_table_set_host_info(arg1: *mut wasm_table_t, arg2: *mut ::std::os::raw::c_void); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_table_set_host_info_with_finalizer"] + pub fn wasm_table_set_host_info_with_finalizer( + arg1: *mut wasm_table_t, + arg2: *mut ::std::os::raw::c_void, + arg3: ::std::option::Option, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_table_as_ref"] + pub fn wasm_table_as_ref(arg1: *mut wasm_table_t) -> *mut wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_table"] + pub fn wasm_ref_as_table(arg1: *mut wasm_ref_t) -> *mut wasm_table_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_table_as_ref_const"] + pub fn wasm_table_as_ref_const(arg1: *const wasm_table_t) -> *const wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_table_const"] + pub fn wasm_ref_as_table_const(arg1: *const wasm_ref_t) -> *const wasm_table_t; +} +pub type wasm_table_size_t = u32; +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_table_new"] + pub fn wasm_table_new( + arg1: *mut wasm_store_t, + arg2: *const wasm_tabletype_t, + init: *mut wasm_ref_t, + ) -> *mut wasm_table_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_table_type"] + pub fn wasm_table_type(arg1: *const wasm_table_t) -> *mut wasm_tabletype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_table_get"] + pub fn wasm_table_get(arg1: *const wasm_table_t, index: wasm_table_size_t) -> *mut wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_table_set"] + pub fn wasm_table_set( + arg1: *mut wasm_table_t, + index: wasm_table_size_t, + arg2: *mut wasm_ref_t, + ) -> bool; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_table_size"] + pub fn wasm_table_size(arg1: *const wasm_table_t) -> wasm_table_size_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_table_grow"] + pub fn wasm_table_grow( + arg1: *mut wasm_table_t, + delta: wasm_table_size_t, + init: *mut wasm_ref_t, + ) -> bool; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_memory_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_delete"] + pub fn wasm_memory_delete(arg1: *mut wasm_memory_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_copy"] + pub fn wasm_memory_copy(arg1: *const wasm_memory_t) -> *mut wasm_memory_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_same"] + pub fn wasm_memory_same(arg1: *const wasm_memory_t, arg2: *const wasm_memory_t) -> bool; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_get_host_info"] + pub fn wasm_memory_get_host_info(arg1: *const wasm_memory_t) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_set_host_info"] + pub fn wasm_memory_set_host_info(arg1: *mut wasm_memory_t, arg2: *mut ::std::os::raw::c_void); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_set_host_info_with_finalizer"] + pub fn wasm_memory_set_host_info_with_finalizer( + arg1: *mut wasm_memory_t, + arg2: *mut ::std::os::raw::c_void, + arg3: ::std::option::Option, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_as_ref"] + pub fn wasm_memory_as_ref(arg1: *mut wasm_memory_t) -> *mut wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_memory"] + pub fn wasm_ref_as_memory(arg1: *mut wasm_ref_t) -> *mut wasm_memory_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_as_ref_const"] + pub fn wasm_memory_as_ref_const(arg1: *const wasm_memory_t) -> *const wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_memory_const"] + pub fn wasm_ref_as_memory_const(arg1: *const wasm_ref_t) -> *const wasm_memory_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_shared_memory_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_shared_memory_delete"] + pub fn wasm_shared_memory_delete(arg1: *mut wasm_shared_memory_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_share"] + pub fn wasm_memory_share(arg1: *const wasm_memory_t) -> *mut wasm_shared_memory_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_obtain"] + pub fn wasm_memory_obtain( + arg1: *mut wasm_store_t, + arg2: *const wasm_shared_memory_t, + ) -> *mut wasm_memory_t; +} +pub type wasm_memory_pages_t = u32; +pub const MEMORY_PAGE_SIZE: usize = 65536; +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_new"] + pub fn wasm_memory_new( + arg1: *mut wasm_store_t, + arg2: *const wasm_memorytype_t, + ) -> *mut wasm_memory_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_type"] + pub fn wasm_memory_type(arg1: *const wasm_memory_t) -> *mut wasm_memorytype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_data"] + pub fn wasm_memory_data(arg1: *mut wasm_memory_t) -> *mut byte_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_data_size"] + pub fn wasm_memory_data_size(arg1: *const wasm_memory_t) -> usize; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_size"] + pub fn wasm_memory_size(arg1: *const wasm_memory_t) -> wasm_memory_pages_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_grow"] + pub fn wasm_memory_grow(arg1: *mut wasm_memory_t, delta: wasm_memory_pages_t) -> bool; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_extern_t { + _unused: [u8; 0], +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_delete"] + pub fn wasm_extern_delete(arg1: *mut wasm_extern_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_copy"] + pub fn wasm_extern_copy(arg1: *const wasm_extern_t) -> *mut wasm_extern_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_same"] + pub fn wasm_extern_same(arg1: *const wasm_extern_t, arg2: *const wasm_extern_t) -> bool; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_get_host_info"] + pub fn wasm_extern_get_host_info(arg1: *const wasm_extern_t) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_set_host_info"] + pub fn wasm_extern_set_host_info(arg1: *mut wasm_extern_t, arg2: *mut ::std::os::raw::c_void); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_set_host_info_with_finalizer"] + pub fn wasm_extern_set_host_info_with_finalizer( + arg1: *mut wasm_extern_t, + arg2: *mut ::std::os::raw::c_void, + arg3: ::std::option::Option, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_as_ref"] + pub fn wasm_extern_as_ref(arg1: *mut wasm_extern_t) -> *mut wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_extern"] + pub fn wasm_ref_as_extern(arg1: *mut wasm_ref_t) -> *mut wasm_extern_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_as_ref_const"] + pub fn wasm_extern_as_ref_const(arg1: *const wasm_extern_t) -> *const wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_extern_const"] + pub fn wasm_ref_as_extern_const(arg1: *const wasm_ref_t) -> *const wasm_extern_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wasm_extern_vec_t { + pub size: usize, + pub data: *mut *mut wasm_extern_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of wasm_extern_vec_t"][::std::mem::size_of::() - 16usize]; + ["Alignment of wasm_extern_vec_t"][::std::mem::align_of::() - 8usize]; + ["Offset of field: wasm_extern_vec_t::size"] + [::std::mem::offset_of!(wasm_extern_vec_t, size) - 0usize]; + ["Offset of field: wasm_extern_vec_t::data"] + [::std::mem::offset_of!(wasm_extern_vec_t, data) - 8usize]; +}; +impl Default for wasm_extern_vec_t { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_vec_new_empty"] + pub fn wasm_extern_vec_new_empty(out: *mut wasm_extern_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_vec_new_uninitialized"] + pub fn wasm_extern_vec_new_uninitialized(out: *mut wasm_extern_vec_t, arg1: usize); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_vec_new"] + pub fn wasm_extern_vec_new( + out: *mut wasm_extern_vec_t, + arg1: usize, + arg2: *const *mut wasm_extern_t, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_vec_copy"] + pub fn wasm_extern_vec_copy(out: *mut wasm_extern_vec_t, arg1: *const wasm_extern_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_vec_delete"] + pub fn wasm_extern_vec_delete(arg1: *mut wasm_extern_vec_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_kind"] + pub fn wasm_extern_kind(arg1: *const wasm_extern_t) -> wasm_externkind_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_type"] + pub fn wasm_extern_type(arg1: *const wasm_extern_t) -> *mut wasm_externtype_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_func_as_extern"] + pub fn wasm_func_as_extern(arg1: *mut wasm_func_t) -> *mut wasm_extern_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_global_as_extern"] + pub fn wasm_global_as_extern(arg1: *mut wasm_global_t) -> *mut wasm_extern_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tag_as_extern"] + pub fn wasm_tag_as_extern(arg1: *mut wasm_tag_t) -> *mut wasm_extern_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_table_as_extern"] + pub fn wasm_table_as_extern(arg1: *mut wasm_table_t) -> *mut wasm_extern_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_as_extern"] + pub fn wasm_memory_as_extern(arg1: *mut wasm_memory_t) -> *mut wasm_extern_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_as_func"] + pub fn wasm_extern_as_func(arg1: *mut wasm_extern_t) -> *mut wasm_func_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_as_global"] + pub fn wasm_extern_as_global(arg1: *mut wasm_extern_t) -> *mut wasm_global_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_as_tag"] + pub fn wasm_extern_as_tag(arg1: *mut wasm_extern_t) -> *mut wasm_tag_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_as_table"] + pub fn wasm_extern_as_table(arg1: *mut wasm_extern_t) -> *mut wasm_table_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_as_memory"] + pub fn wasm_extern_as_memory(arg1: *mut wasm_extern_t) -> *mut wasm_memory_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_func_as_extern_const"] + pub fn wasm_func_as_extern_const(arg1: *const wasm_func_t) -> *const wasm_extern_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_global_as_extern_const"] + pub fn wasm_global_as_extern_const(arg1: *const wasm_global_t) -> *const wasm_extern_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_tag_as_extern_const"] + pub fn wasm_tag_as_extern_const(arg1: *const wasm_tag_t) -> *const wasm_extern_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_table_as_extern_const"] + pub fn wasm_table_as_extern_const(arg1: *const wasm_table_t) -> *const wasm_extern_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_memory_as_extern_const"] + pub fn wasm_memory_as_extern_const(arg1: *const wasm_memory_t) -> *const wasm_extern_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_as_func_const"] + pub fn wasm_extern_as_func_const(arg1: *const wasm_extern_t) -> *const wasm_func_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_as_global_const"] + pub fn wasm_extern_as_global_const(arg1: *const wasm_extern_t) -> *const wasm_global_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_as_tag_const"] + pub fn wasm_extern_as_tag_const(arg1: *const wasm_extern_t) -> *const wasm_tag_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_as_table_const"] + pub fn wasm_extern_as_table_const(arg1: *const wasm_extern_t) -> *const wasm_table_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_extern_as_memory_const"] + pub fn wasm_extern_as_memory_const(arg1: *const wasm_extern_t) -> *const wasm_memory_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_instance_delete"] + pub fn wasm_instance_delete(arg1: *mut wasm_instance_t); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_instance_copy"] + pub fn wasm_instance_copy(arg1: *const wasm_instance_t) -> *mut wasm_instance_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_instance_same"] + pub fn wasm_instance_same(arg1: *const wasm_instance_t, arg2: *const wasm_instance_t) -> bool; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_instance_get_host_info"] + pub fn wasm_instance_get_host_info(arg1: *const wasm_instance_t) + -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_instance_set_host_info"] + pub fn wasm_instance_set_host_info( + arg1: *mut wasm_instance_t, + arg2: *mut ::std::os::raw::c_void, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_instance_set_host_info_with_finalizer"] + pub fn wasm_instance_set_host_info_with_finalizer( + arg1: *mut wasm_instance_t, + arg2: *mut ::std::os::raw::c_void, + arg3: ::std::option::Option, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_instance_as_ref"] + pub fn wasm_instance_as_ref(arg1: *mut wasm_instance_t) -> *mut wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_instance"] + pub fn wasm_ref_as_instance(arg1: *mut wasm_ref_t) -> *mut wasm_instance_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_instance_as_ref_const"] + pub fn wasm_instance_as_ref_const(arg1: *const wasm_instance_t) -> *const wasm_ref_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_ref_as_instance_const"] + pub fn wasm_ref_as_instance_const(arg1: *const wasm_ref_t) -> *const wasm_instance_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_instance_new"] + pub fn wasm_instance_new( + arg1: *mut wasm_store_t, + arg2: *const wasm_module_t, + imports: *const wasm_extern_vec_t, + arg3: *mut *mut wasm_trap_t, + ) -> *mut wasm_instance_t; +} +unsafe extern "C" { + #[link_name = "\u{1}wee8_wasm_instance_exports"] + pub fn wasm_instance_exports(arg1: *const wasm_instance_t, out: *mut wasm_extern_vec_t); +} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct __crt_locale_data { + pub _address: u8, +} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct __crt_multibyte_data { + pub _address: u8, +} diff --git a/src/wasix/runtime/engine/windows/bridge.cpp b/src/wasix/runtime/engine/windows/bridge.cpp new file mode 100644 index 000000000..0d4c11d30 --- /dev/null +++ b/src/wasix/runtime/engine/windows/bridge.cpp @@ -0,0 +1,65 @@ +#define LIBWASM_STATIC +#include "wasm.hh" +namespace wasm { +template <> class Shared { + friend class destroyer; + void destroy(); +protected: + Shared() = default; + ~Shared() = default; +}; +} +extern "C" void oliphaunt_store_delete(wasm::Store* p) { + if (p) wasm::destroyer{}(p); +} +extern "C" void oliphaunt_shared_memory_delete(wasm::Shared* p) { + if (p) wasm::destroyer{}(p); +} +extern "C" void oliphaunt_shared_module_delete(wasm::Shared* p) { + if (p) wasm::destroyer{}(p); +} +extern "C" void oliphaunt_functype_delete(wasm::FuncType* p) { + if (p) wasm::destroyer{}(p); +} +extern "C" void oliphaunt_module_delete(wasm::Module* p) { + if (p) wasm::destroyer{}(p); +} +extern "C" void oliphaunt_ref_delete(wasm::Ref* p) { + if (p) wasm::destroyer{}(p); +} +extern "C" void oliphaunt_extern_delete(wasm::Extern* p) { + if (p) wasm::destroyer{}(p); +} +extern "C" void oliphaunt_valtype_delete(wasm::ValType* p) { + if (p) wasm::destroyer{}(p); +} +extern "C" void oliphaunt_memorytype_delete(wasm::MemoryType* p) { + if (p) wasm::destroyer{}(p); +} +extern "C" void oliphaunt_globaltype_delete(wasm::GlobalType* p) { + if (p) wasm::destroyer{}(p); +} +extern "C" void oliphaunt_tabletype_delete(wasm::TableType* p) { + if (p) wasm::destroyer{}(p); +} +extern "C" void oliphaunt_trap_delete(wasm::Trap* p) { + if (p) wasm::destroyer{}(p); +} +extern "C" void oliphaunt_externtype_delete(wasm::ExternType* p) { + if (p) wasm::destroyer{}(p); +} +// Pinned V8's C wasm_func_call adopts and destroys its const argument array. +// Borrow it instead: Wasmer owns and reuses that array across WASIX reentry. +struct Values { size_t size; wasm::Val* data; }; +struct BorrowedValues { + wasm::vec values; + ~BorrowedValues() { values.release(); } +}; +extern "C" wasm::Trap* oliphaunt_func_call(const wasm::Func* func, + const Values* args, Values* results) { + BorrowedValues borrowed{wasm::vec::adopt(args->size, args->data)}; + auto values = wasm::vec::adopt(results->size, results->data); + auto trap = func->call(borrowed.values, values); + *results = {values.size(), values.release()}; + return trap.release(); +} diff --git a/src/wasix/runtime/engine/windows/build-caches.mts b/src/wasix/runtime/engine/windows/build-caches.mts new file mode 100644 index 000000000..88a3d2369 --- /dev/null +++ b/src/wasix/runtime/engine/windows/build-caches.mts @@ -0,0 +1,109 @@ +import assert from 'node:assert/strict'; +import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { sha256File } from '../../../../third-party/tools/source-fetch-core.mts'; + +const root = path.resolve(import.meta.dir, '../../../../..'); +const engine = path.join(root, 'target/oliphaunt-wasix/engine/windows-x64-msvc'); +const output = path.join(engine, 'cache/sse41'); +const work = path.join(root, 'target/oliphaunt-wasix/engine/cache-producer'); +const source = JSON.parse(readFileSync(path.join(engine, 'source.json'), 'utf8')); +assert.equal(sha256File(path.join(engine, 'oliphaunt_wee8.dll')), source.dllSha256); +mkdirSync(output, { recursive: true }); +mkdirSync(work, { recursive: true }); +for (const file of ['cache-producer.exe', 'oliphaunt_wee8.dll']) { + copyFileSync(path.join(engine, file), path.join(work, file)); +} + +const env = { ...process.env, WINEPREFIX: path.join(work, 'wine-prefix'), WINEDEBUG: '-all' }; +function run(command: string[], emulated = false) { + const result = Bun.spawnSync(command, { + cwd: work, + env: emulated ? { ...env, WINELOADERNOEXEC: '1' } : env, + timeout: 900_000, + }); + assert.equal(result.exitCode, 0, result.stdout.toString() + result.stderr.toString()); + return result.stdout.toString(); +} +const wine = '/usr/lib/wine/wine64'; +run([wine, 'cmd', '/c', 'exit', '0']); +const receipts = []; +const products = Bun.argv.slice(2); +assert( + products.length > 0 && + products.every((product) => ['runtime', 'tools', 'extensions'].includes(product)), +); +for (const product of products) { + const plan = Bun.spawnSync( + [ + 'cargo', + 'run', + '-p', + 'xtask', + '--locked', + '--', + 'assets', + 'prepare-aot', + '--target-triple', + 'x86_64-pc-windows-msvc', + '--product', + product, + ], + { cwd: root }, + ); + assert.equal(plan.exitCode, 0, plan.stderr.toString()); + for (const row of plan.stdout.toString().trim().split('\n')) { + const [input, destination] = row.split('\t'); + assert(input && destination, 'incomplete AOT producer input'); + const guest = readFileSync(input); + copyFileSync(input, path.join(work, 'guest.wasm')); + const log = run(['qemu-x86_64', '-cpu', 'Penryn', wine, 'cache-producer.exe', 'write'], true); + assert.match(log, /cpu_mask=0xe /, 'the baseline must come from a real emulated SSE4.1 CPU'); + assert.match(log, /native_deserialize=PASS/); + const cache = readFileSync(path.join(work, 'cache.bin')); + let wireLength = 0, + offset = 0, + shift = 0; + while (offset < 10) { + const byte = cache[offset++]; + wireLength += (byte & 127) * 2 ** shift; + if (!(byte & 128)) break; + shift += 7; + } + assert(Number.isSafeInteger(wireLength)); + assert(cache.length - offset - wireLength >= 20, 'missing native cache'); + assert.deepEqual( + cache.subarray(offset, offset + wireLength), + guest, + 'native cache guest identity', + ); + assert.equal(cache.readUInt32LE(offset + wireLength), 0xc0de0689, 'pinned native cache format'); + assert.equal(cache.readUInt32LE(offset + wireLength + 8), 0xe, 'unmodified SSE4.1 CPU profile'); + const digest = sha256File(input); + const file = path.join(output, `${digest}.bin`); + writeFileSync(file, cache); + const read = run(['qemu-x86_64', '-cpu', 'Penryn', wine, 'cache-producer.exe', 'read'], true); + assert.match(read, /native_deserialize=PASS/, 'fresh-process native cache reader'); + receipts.push({ + product, + moduleSha256: digest, + cacheSha256: sha256File(file), + bytes: cache.length, + }); + console.log(`SSE4.1 AOT ${product}: ${path.basename(input)}`); + } +} +writeFileSync( + path.join(output, 'source.json'), + JSON.stringify( + { + sourceSha: source.sourceSha, + dllSha256: source.dllSha256, + cpu: 'Penryn', + nativeCpuMask: '0xe', + modules: receipts, + }, + null, + 2, + ) + '\n', +); diff --git a/src/wasix/runtime/engine/windows/build.mts b/src/wasix/runtime/engine/windows/build.mts new file mode 100644 index 000000000..45bfabffa --- /dev/null +++ b/src/wasix/runtime/engine/windows/build.mts @@ -0,0 +1,173 @@ +import assert from 'node:assert/strict'; +import { + copyFileSync, + existsSync, + mkdirSync, + readFileSync, + statSync, + writeFileSync, +} from 'node:fs'; +import path from 'node:path'; +import { archiveTreeDigest, sha256File } from '../../../../third-party/tools/source-fetch-core.mts'; +import { renderDispatch, renderExports, V8_FLAGS } from './dispatch.mts'; +import { ENGINE_SOURCE_CRATES } from '../contract.mts'; + +const root = path.resolve(import.meta.dir, '../../../../..'); +const owner = path.join(root, 'src/wasix/runtime/engine'); +const pins = Bun.TOML.parse(readFileSync(path.join(owner, 'source.toml'), 'utf8')); +const inputs = path.join(root, 'target/oliphaunt-wasix/engine/inputs'); +const output = path.join(root, 'target/oliphaunt-wasix/engine/windows-x64-msvc'); +const inputNames = ['wee8-windows', 'v8-header', 'v8-license']; + +function verify(name: string, file: string) { + assert(inputNames.includes(name), `unknown engine input ${name}`); + assert.equal(statSync(file).size, pins[name].bytes, `${name}: incorrect byte size`); + assert.equal(sha256File(file), pins[name].sha256, `${name}: checksum mismatch`); +} + +function run(command: string[]) { + const result = Bun.spawnSync(command, { cwd: output, stdout: 'inherit', stderr: 'inherit' }); + assert.equal(result.exitCode, 0, `engine producer failed: ${command[0]}`); +} + +function build() { + assert.equal(process.platform, 'win32', 'the native engine producer requires Windows x64 MSVC'); + assert.equal(process.arch, 'x64'); + for (const name of inputNames) verify(name, path.join(inputs, name)); + const header = path.join(owner, 'crates/wasmer/upstream/third-party/wee8/wasm.h'); + assert.equal( + sha256File(header), + '97f49de5418f02c55633dbcdc96b72fb404659c9275e9a47a3b4d6bdce384b96', + 'regenerate C bindings for the pinned header', + ); + mkdirSync(output, { recursive: true }); + const archive = path.join(inputs, 'wee8-windows'); + const library = path.join(output, 'v8.lib'); + const pin = pins['wee8-windows']; + if ( + !existsSync(library) || + statSync(library).size !== pin['member-bytes'] || + sha256File(library) !== pin['member-sha256'] + ) { + const extracted = Bun.spawnSync(['tar', '-xOf', archive, '--', pin.member], { + stdout: Bun.file(library), + stderr: 'inherit', + }); + assert.equal(extracted.exitCode, 0, 'extract pinned V8 static archive'); + assert.equal(statSync(library).size, pin['member-bytes']); + assert.equal(sha256File(library), pin['member-sha256']); + } + copyFileSync(path.join(inputs, 'v8-header'), path.join(output, 'wasm.hh')); + copyFileSync(path.join(inputs, 'v8-license'), path.join(output, 'LICENSE.v8')); + const msvc = path.join(process.env.VCToolsInstallDir ?? '', 'bin/HostX64/x64'); + const compiler = path.join(msvc, 'cl.exe'), + linker = path.join(msvc, 'link.exe'); + assert(existsSync(compiler) && existsSync(linker), 'initialize the existing MSVC environment'); + const { symbols } = renderDispatch('0'.repeat(64)); + writeFileSync(path.join(output, 'engine.def'), renderExports(symbols)); + run([ + compiler, + '/nologo', + '/c', + '/O2', + '/MT', + '/EHsc', + '/std:c++20', + `/I${output}`, + path.join(import.meta.dir, 'bridge.cpp'), + `/Fo${path.join(output, 'bridge.obj')}`, + ]); + run([ + linker, + '/NOLOGO', + '/DLL', + '/Brepro', + `/OUT:${path.join(output, 'oliphaunt_wee8.dll')}`, + `/DEF:${path.join(output, 'engine.def')}`, + `/IMPLIB:${path.join(output, 'oliphaunt_wee8.lib')}`, + path.join(output, 'bridge.obj'), + library, + 'winmm.lib', + 'dbghelp.lib', + 'shlwapi.lib', + ]); + writeFileSync( + path.join(output, 'profile.h'), + `#define OLIPHAUNT_V8_FLAGS ${JSON.stringify(V8_FLAGS)}\n`, + ); + for (const [name, defines] of [ + ['cache-producer', ['/DOLIPHAUNT_EMULATED_PRODUCER']], + ['cache-reader', []], + ] as const) { + run([ + compiler, + '/nologo', + '/c', + '/O2', + '/GS-', + '/Zl', + `/I${output}`, + ...defines, + path.join(import.meta.dir, 'cache-producer.c'), + `/Fo${path.join(output, `${name}.obj`)}`, + ]); + run([ + linker, + '/nologo', + '/nodefaultlib', + '/machine:x64', + '/entry:mainCRTStartup', + '/subsystem:console', + '/Brepro', + path.join(output, `${name}.obj`), + 'kernel32.lib', + path.join(output, 'oliphaunt_wee8.lib'), + `/out:${path.join(output, `${name}.exe`)}`, + ]); + } + const dll = path.join(output, 'oliphaunt_wee8.dll'); + const digest = sha256File(dll); + const prebuilt = path.join(owner, 'crates/wasmer/prebuilt'); + mkdirSync(prebuilt, { recursive: true }); + const bindings = path.join(prebuilt, 'embedded_bindings.rs'); + writeFileSync(bindings, renderDispatch(digest).generated); + run(['rustfmt', '--edition', '2024', bindings]); + writeFileSync( + path.join(output, 'source.json'), + JSON.stringify( + { + source: pins, + engineSources: Object.fromEntries( + ENGINE_SOURCE_CRATES.map((name) => [ + name, + archiveTreeDigest(path.join(owner, 'crates', name, 'upstream')), + ]), + ), + sourceSha: Bun.spawnSync(['git', 'rev-parse', 'HEAD'], { cwd: root }) + .stdout.toString() + .trim(), + dllSha256: digest, + bindingsSha256: sha256File(bindings), + flags: V8_FLAGS, + entries: symbols.length, + }, + null, + 2, + ) + '\n', + ); +} + +if (import.meta.main) { + const [operation, name, file] = Bun.argv.slice(2); + if (operation === 'acquisition-plan' && !name) { + for (const input of inputNames) console.log(`${input}\t${pins[input].url}`); + } else if (operation === 'verify-input' && name && file) { + try { + verify(name, file); + } catch (error) { + console.error(error.message); + process.exitCode = 1; + } + } else if (operation === 'build' && !name) build(); + else throw new Error('usage: build.mts acquisition-plan | verify-input NAME FILE | build'); +} diff --git a/src/wasix/runtime/engine/windows/build.sh b/src/wasix/runtime/engine/windows/build.sh new file mode 100644 index 000000000..eed727e3d --- /dev/null +++ b/src/wasix/runtime/engine/windows/build.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail +case "$(uname -s)" in MINGW*|MSYS*|CYGWIN*) ;; *) exit 0 ;; esac +cd "$(git rev-parse --show-toplevel)" +source tools/dev/acquisition.sh +oliphaunt_acquisition_start 'Windows WASIX engine' 1800 +owner=src/wasix/runtime/engine/windows +inputs=target/oliphaunt-wasix/engine/inputs +mkdir -p "$inputs" +plan="$(bun "$owner/build.mts" acquisition-plan)" +while IFS=$'\t' read -r name url; do + destination="$inputs/$name" + if bun "$owner/build.mts" verify-input "$name" "$destination"; then continue; fi + temporary="$(mktemp "$inputs/.download.XXXXXX")" + trap 'rm -f "$temporary"' EXIT + oliphaunt_acquisition_curl 1200 4 2 curl --fail --silent --show-error --location \ + --proto '=https' --proto-redir '=https' --output "$temporary" "$url" + bun "$owner/build.mts" verify-input "$name" "$temporary" + mv "$temporary" "$destination" + trap - EXIT +done <<<"$plan" +bun "$owner/build.mts" build diff --git a/src/wasix/runtime/engine/windows/cache-producer.c b/src/wasix/runtime/engine/windows/cache-producer.c new file mode 100644 index 000000000..5c159bb95 --- /dev/null +++ b/src/wasix/runtime/engine/windows/cache-producer.c @@ -0,0 +1,112 @@ +#include "profile.h" +/* Producer-only Windows V8 cache control. No CRT or Windows SDK needed. */ +typedef unsigned long long usize; +typedef unsigned int DWORD; +typedef unsigned short WCHAR; +typedef void *HANDLE; +typedef struct { usize size; unsigned char *data; } Vec; +void __cpuidex(int[4], int, int); +#pragma intrinsic(__cpuidex) +#define IMP __declspec(dllimport) +IMP HANDLE __stdcall GetStdHandle(DWORD); +IMP HANDLE __stdcall CreateFileW(const WCHAR *, DWORD, DWORD, void *, DWORD, DWORD, HANDLE); +IMP int __stdcall ReadFile(HANDLE, void *, DWORD, DWORD *, void *); +IMP int __stdcall WriteFile(HANDLE, const void *, DWORD, DWORD *, void *); +IMP int __stdcall GetFileSizeEx(HANDLE, long long *); +IMP int __stdcall CloseHandle(HANDLE); +IMP WCHAR *__stdcall GetCommandLineW(void); +IMP void __stdcall ExitProcess(DWORD); +#ifdef OLIPHAUNT_EMULATED_PRODUCER +IMP HANDLE __stdcall GetCurrentProcess(void); +IMP int __stdcall TerminateProcess(HANDLE, DWORD); +#endif +IMP void oliphaunt_set_v8_flags(const char *, usize); +IMP void *wee8_wasm_engine_new(void); +IMP void *wee8_wasm_store_new(void *); +IMP void wee8_wasm_store_delete(void *); +IMP void wee8_wasm_engine_delete(void *); +IMP void wee8_wasm_byte_vec_new_uninitialized(Vec *, usize); +IMP void wee8_wasm_byte_vec_delete(Vec *); +IMP void *wee8_wasm_module_new(void *, const Vec *); +IMP void *wee8_wasm_module_deserialize(void *, const Vec *); +IMP void wee8_wasm_module_serialize(void *, Vec *); +IMP void wee8_wasm_module_delete(void *); + +static void print(const char *s) { + DWORD done; usize n=0; while(s[n]) n++; + WriteFile(GetStdHandle((DWORD)-11),s,(DWORD)n,&done,0); +} +static void number(usize value, int hex) { + char out[32]; unsigned n=0, base=hex?16:10; + do { unsigned d=value%base; out[n++]=(char)(d<10?'0'+d:'a'+d-10); value/=base; } while(value); + if(hex) print("0x"); + for(unsigned i=0;i0x7fffffff) fail("read open/size failed",10); + wee8_wasm_byte_vec_new_uninitialized(&v,(usize)size); + if(!ReadFile(f,v.data,(DWORD)size,&got,0) || got!=size) fail("read bytes failed",11); + CloseHandle(f); return v; +} +static void write(const WCHAR *path, const Vec *v) { + HANDLE f=CreateFileW(path,0x40000000,0,0,2,0,0); DWORD done=0; + if(f==(HANDLE)-1 || !WriteFile(f,v->data,(DWORD)v->size,&done,0) || done!=v->size) fail("write failed",12); + CloseHandle(f); +} +void mainCRTStartup(void) { + int cpu[4]; + __cpuidex(cpu,1,0); print("cpuid_leaf1_ecx="); number((DWORD)cpu[2],1); + __cpuidex(cpu,7,0); print(" cpuid_leaf7_ecx="); number((DWORD)cpu[2],1); print("\n"); + static const char flags[]=OLIPHAUNT_V8_FLAGS; + oliphaunt_set_v8_flags(flags,sizeof(flags)-1); + int reader=0; const WCHAR *cmd=GetCommandLineW(); + for(usize i=0;cmd[i];i++) if(cmd[i]==' ' && cmd[i+1]=='r' && cmd[i+2]=='e' && cmd[i+3]=='a' && cmd[i+4]=='d') reader=1; + print(reader?"operation=read\n":"operation=write\n"); + void *engine=wee8_wasm_engine_new(), *store=wee8_wasm_store_new(engine); + if(!engine || !store) fail("engine/store failed",20); + Vec data=read(reader?(const WCHAR*)L"cache.bin":(const WCHAR*)L"guest.wasm"); + if(!reader) { + void *m=wee8_wasm_module_new(store,&data); + if(!m) fail("compile failed",21); + Vec cache; wee8_wasm_module_serialize(m,&cache); + wee8_wasm_module_delete(m); wee8_wasm_byte_vec_delete(&data); + data=cache; write((const WCHAR*)L"cache.bin",&data); + } + usize p=0, wire=0; unsigned shift=0; + do { if(p>=data.size || shift>63) fail("invalid wire length",22); + unsigned c=data.data[p++]; wire|=(usize)(c&127)<data.size-p || data.size-p-wire<20) fail("missing native cache",23); + usize offset=p+wire; + print("native_bytes="); number(data.size-offset,0); + print(" cpu_mask="); number(*(DWORD*)(data.data+offset+8),1); + print(" flag_hash="); number(*(DWORD*)(data.data+offset+12),1); print("\n"); + void *module=wee8_wasm_module_deserialize(store,&data); + if(!module) fail("native_deserialize=REJECT",24); + print("native_deserialize=PASS (no compilation fallback)\n"); + wee8_wasm_module_delete(module); wee8_wasm_byte_vec_delete(&data); + print("stage=module_deleted\n"); + wee8_wasm_store_delete(store); print("stage=store_deleted\n"); + wee8_wasm_engine_delete(engine); print("stage=engine_deleted\n"); +#ifdef OLIPHAUNT_EMULATED_PRODUCER + /* Wine/QEMU crashes in process detach after all the above cleanup returns. + Only this internal producer avoids that detach path. Native readers and + SDK applications retain ordinary Windows process shutdown. */ + print("stage=emulated_producer_completed\n"); +#endif + finish(0); +} diff --git a/src/wasix/runtime/engine/windows/dispatch.mts b/src/wasix/runtime/engine/windows/dispatch.mts new file mode 100644 index 000000000..3cc52a982 --- /dev/null +++ b/src/wasix/runtime/engine/windows/dispatch.mts @@ -0,0 +1,140 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import path from 'node:path'; + +const DECLARATION = + /unsafe extern "C" \{\s*#\[link_name = "\\u\{1\}(wee8_[^"]+)"\]\s*pub fn (\w+)\(([^;]*)\)\s*(->[^;]+)?;\s*\}/gs; +const UNSUPPORTED = new Set([ + 'wee8_wasm_tag_get', + 'wee8_wasm_tag_set', + 'wee8_wasm_tagtype_as_externtype', + 'wee8_wasm_tagtype_as_externtype_const', +]); +const BRIDGES = [ + [ + '?GetCurrent@Isolate@v8@@SAPEAV12@XZ', + 'oliphaunt_v8_current_isolate', + '', + '-> *mut std::ffi::c_void', + ], + [ + '?TerminateExecution@Isolate@v8@@QEAAXXZ', + 'oliphaunt_v8_terminate_execution', + 'ptr: *mut std::ffi::c_void', + '', + ], + ['oliphaunt_externtype_delete', 'oliphaunt_externtype_delete', 'ptr: *mut wasm_tagtype_t', ''], + [ + 'oliphaunt_set_v8_flags', + 'oliphaunt_set_v8_flags', + 'flags: *const std::ffi::c_char, size: usize', + '', + ], +]; + +export const V8_FLAGS = + '--mcpu=generic --no-enable-sse4-2 --no-enable-sahf --no-enable-avx --no-enable-avx2 --no-enable-avx-vnni --no-enable-avx-vnni-int8 --no-enable-fma3 --no-enable-f16c --no-enable-bmi1 --no-enable-bmi2 --no-enable-lzcnt --no-enable-popcnt --no-intel-jcc-erratum-mitigation'; + +function argumentNames(args: string) { + let depth = 0, + start = 0; + const names: string[] = []; + for (const [offset, char] of [...`${args},`].entries()) { + if ('(<['.includes(char)) depth++; + else if (')>]'.includes(char) && !(char === '>' && args[offset - 1] === '-')) depth--; + else if (char === ',' && depth === 0) { + const argument = args.slice(start, offset).trim(); + if (argument) names.push(argument.split(':', 1)[0].trim()); + start = offset + 1; + } + } + assert.equal(depth, 0, 'unbalanced C declaration'); + return names; +} + +export function renderDispatch(digest: string) { + assert.match(digest, /^[0-9a-f]{64}$/); + const symbols: string[] = []; + const omitted = new Set(); + function wrap(symbol: string, name: string, args: string, ret = '', visibility = 'pub') { + if (UNSUPPORTED.has(symbol)) { + omitted.add(symbol); + return ''; // Absent upstream exports stay absent: future calls fail to compile. + } + const index = symbols.length; + symbols.push(symbol); + return `${visibility} unsafe fn ${name}(${args}) ${ret} { + type Function = unsafe extern "C" fn(${args}) ${ret}; + let function = unsafe { + std::mem::transmute::<*mut std::ffi::c_void, Function>(embedded_engine::symbol(${index})) + }; + unsafe { function(${argumentNames(args).join(', ')}) } +}`; + } + let count = 0; + let generated = readFileSync(path.join(import.meta.dir, 'bindings.rs'), 'utf8').replace( + DECLARATION, + (_, symbol, name, args, ret) => { + count++; + return wrap(symbol, name, args, ret); + }, + ); + assert.equal(count, 314, 'pinned Wasmer C ABI changed'); + assert.deepEqual(omitted, UNSUPPORTED); + assert(!generated.includes('link_name'), 'unhandled foreign declaration'); + for (const [symbol, name, args, ret] of BRIDGES) { + generated += `\n${wrap(symbol, name, args, ret, 'pub(crate)')}\n`; + } + const loader = readFileSync(path.join(import.meta.dir, 'loader.rs'), 'utf8').replace( + 'ENGINE_DIGEST', + digest, + ); + generated += `\nmod embedded_engine {\n${loader} +const REQUIRED_SYMBOLS: &[&[u8]] = &[ +${symbols.map((symbol) => ` b"${symbol}\\0",`).join('\n')} +]; +} +pub(crate) fn oliphaunt_v8_prepare() -> std::io::Result<()> { + embedded_engine::prepare() +} +pub(crate) unsafe fn oliphaunt_v8_configure() { + const FLAGS: &[u8] = b"${V8_FLAGS}"; + unsafe { oliphaunt_set_v8_flags(FLAGS.as_ptr().cast(), FLAGS.len()) }; +} +`; + return { generated, symbols }; +} + +export function renderExports(symbols: string[]) { + const owners = new Set( + [ + 'store', + 'shared_memory', + 'shared_module', + 'functype', + 'module', + 'ref', + 'extern', + 'valtype', + 'memorytype', + 'globaltype', + 'tabletype', + 'trap', + ].map((name) => `wasm_${name}_delete`), + ); + owners.add('wasm_func_call'); + return ( + 'LIBRARY oliphaunt_wee8\nEXPORTS\n' + + symbols + .filter((symbol) => symbol.startsWith('wee8_')) + .map((symbol) => { + const original = symbol.slice('wee8_'.length); + const implementation = owners.has(original) + ? original.replace('wasm_', 'oliphaunt_') + : original; + return ` ${symbol}=${implementation}`; + }) + .join('\n') + + '\n oliphaunt_externtype_delete\n oliphaunt_set_v8_flags=?SetFlagsFromString@V8@v8@@SAXPEBD_K@Z\n' + ); +} diff --git a/src/wasix/runtime/engine/windows/dispatch.test.mts b/src/wasix/runtime/engine/windows/dispatch.test.mts new file mode 100644 index 000000000..6569a70df --- /dev/null +++ b/src/wasix/runtime/engine/windows/dispatch.test.mts @@ -0,0 +1,21 @@ +import { expect, test } from 'bun:test'; +import { renderDispatch, renderExports, V8_FLAGS } from './dispatch.mts'; + +test('resolves the complete pinned C ABI before V8 initialization', () => { + const { generated, symbols } = renderDispatch('a'.repeat(64)); + expect(new Set(symbols).size).toBe(314); + expect(generated).not.toContain('link_name'); + expect(generated).not.toContain('pub unsafe fn wasm_tag_get('); + expect(generated).toContain(V8_FLAGS); + expect(generated).toContain('Oliphaunt/engines/' + 'a'.repeat(64)); + expect(generated).toContain('type Function = unsafe extern "C" fn('); + // Nested callback types must not become extra call arguments. + expect(generated.includes('function(arg1, type_, arg2, env, finalizer)')).toBe(true); + expect(renderExports(symbols)).toContain('wee8_wasm_func_call=oliphaunt_func_call'); + expect(renderExports(symbols)).toContain('wee8_wasm_store_delete=oliphaunt_store_delete'); + expect(symbols.includes('wee8_wasm_tag_get')).toBe(false); +}); + +test('requires a content-addressed engine identity', () => { + expect(() => renderDispatch('../engine')).toThrow(); +}); diff --git a/src/wasix/runtime/engine/windows/kernel32.def b/src/wasix/runtime/engine/windows/kernel32.def new file mode 100644 index 000000000..f75cc17ae --- /dev/null +++ b/src/wasix/runtime/engine/windows/kernel32.def @@ -0,0 +1,18 @@ +LIBRARY kernel32.dll +EXPORTS +GetStdHandle +CreateFileW +ReadFile +WriteFile +GetFileSizeEx +CloseHandle +GetCommandLineW +ExitProcess +GetCurrentProcess +TerminateProcess +Sleep +GetTickCount64 +GetModuleHandleW +GetProcAddress +CreateThread +WaitForSingleObject diff --git a/src/wasix/runtime/engine/windows/loader.rs b/src/wasix/runtime/engine/windows/loader.rs new file mode 100644 index 000000000..6d7f4d858 --- /dev/null +++ b/src/wasix/runtime/engine/windows/loader.rs @@ -0,0 +1,259 @@ +// Prepared native Windows delivery. Resolve every entry before initializing V8. +use std::{ + ffi::{OsString, c_void}, + io::{self, Write}, + os::windows::ffi::{OsStrExt, OsStringExt}, + path::{Path, PathBuf}, + sync::{Mutex, OnceLock}, +}; + +#[repr(C)] +struct Guid { + data1: u32, + data2: u16, + data3: u16, + data4: [u8; 8], +} +#[link(name = "shell32")] +unsafe extern "system" { + fn SHGetKnownFolderPath( + folder: *const Guid, + flags: u32, + token: *mut c_void, + path: *mut *mut u16, + ) -> i32; +} +#[link(name = "ole32")] +unsafe extern "system" { + fn CoTaskMemFree(ptr: *mut c_void); +} +#[link(name = "kernel32")] +unsafe extern "system" { + fn LoadLibraryExW(path: *const u16, file: *mut c_void, flags: u32) -> *mut c_void; + fn GetProcAddress(module: *mut c_void, name: *const u8) -> *mut c_void; + fn FreeLibrary(module: *mut c_void) -> i32; +} +const DLL: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/oliphaunt_wee8.dll")); +static PREPARED: OnceLock = OnceLock::new(); +static PREPARING: Mutex<()> = Mutex::new(()); + +struct Library(usize); +impl Drop for Library { + fn drop(&mut self) { + unsafe { FreeLibrary(self.0 as *mut c_void) }; + } +} +struct Prepared { + // OnceLock retains a successful DLL for process life. Failed resolution + // drops the library before V8 initializes any global state. + _library: Library, + symbols: Vec, +} + +#[derive(Debug)] +struct DeliveryError { + action: String, + path: PathBuf, + source: io::Error, +} +impl std::fmt::Display for DeliveryError { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + formatter, + "{} '{}': {}", + self.action, + self.path.display(), + self.source + ) + } +} +impl std::error::Error for DeliveryError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + Some(&self.source) + } +} +fn context(error: io::Error, action: &str, path: &Path) -> io::Error { + io::Error::new( + error.kind(), + DeliveryError { + action: action.to_owned(), + path: path.to_owned(), + source: error, + }, + ) +} + +fn cache_path() -> io::Result { + const LOCAL_APP_DATA: Guid = Guid { + data1: 0xf1b32785, + data2: 0x6fba, + data3: 0x4fcf, + data4: [0x9d, 0x55, 0x7b, 0x8e, 0x7f, 0x15, 0x70, 0x91], + }; + let mut folder = std::ptr::null_mut(); + let status = + unsafe { SHGetKnownFolderPath(&LOCAL_APP_DATA, 0x8000, std::ptr::null_mut(), &mut folder) }; + if status < 0 || folder.is_null() { + if !folder.is_null() { + unsafe { CoTaskMemFree(folder.cast()) }; + } + return Err(io::Error::other(format!( + "locate Windows local app data: HRESULT {status:#x}" + ))); + } + let mut len = 0; + unsafe { + while *folder.add(len) != 0 { + len += 1; + } + } + let root = PathBuf::from(OsString::from_wide(unsafe { + std::slice::from_raw_parts(folder, len) + })); + unsafe { CoTaskMemFree(folder.cast()) }; + Ok(root.join("Oliphaunt/engines/ENGINE_DIGEST/oliphaunt_wee8.dll")) +} + +fn valid_payload(path: &Path) -> io::Result { + match std::fs::read(path) { + Ok(bytes) => Ok(bytes == DLL), + Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(false), + Err(error) => Err(context(error, "read internal engine cache", path)), + } +} + +fn install(path: &Path) -> io::Result<()> { + // A valid warm cache also works without directory write permission. + if valid_payload(path)? { + return Ok(()); + } + let root = path.parent().expect("internal engine path has a parent"); + std::fs::create_dir_all(root) + .map_err(|error| context(error, "create internal engine cache", root))?; + let mut temporary = tempfile::NamedTempFile::new_in(root) + .map_err(|error| context(error, "create temporary engine", root))?; + temporary + .write_all(DLL) + .map_err(|error| context(error, "write temporary engine", temporary.path()))?; + temporary + .as_file() + .sync_all() + .map_err(|error| context(error, "sync temporary engine", temporary.path()))?; + if let Err(error) = temporary.persist(path) { + // Another process may have installed and loaded the same immutable DLL. + // Accept it only when every byte matches; otherwise leave it untouched. + if !valid_payload(path).unwrap_or(false) { + return Err(context(error.error, "replace internal engine cache", path)); + } + } + if !valid_payload(path)? { + return Err(io::Error::other(format!( + "internal engine cache identity differs: '{}'", + path.display() + ))); + } + Ok(()) +} + +fn load(path: &Path, names: &[&[u8]]) -> io::Result { + let wide: Vec<_> = path.as_os_str().encode_wide().chain(Some(0)).collect(); + let module = unsafe { LoadLibraryExW(wide.as_ptr(), std::ptr::null_mut(), 0x100 | 0x800) }; + if module.is_null() { + return Err(context( + io::Error::last_os_error(), + "load internal engine", + path, + )); + } + let library = Library(module as usize); + let mut symbols = Vec::with_capacity(names.len()); + for name in names { + let pointer = unsafe { GetProcAddress(module, name.as_ptr()) }; + if pointer.is_null() { + return Err(context( + io::Error::last_os_error(), + &format!( + "resolve internal engine entry {}", + String::from_utf8_lossy(&name[..name.len() - 1]) + ), + path, + )); + } + symbols.push(pointer as usize); + } + Ok(Prepared { + _library: library, + symbols, + }) +} + +pub(super) fn prepare() -> io::Result<()> { + if PREPARED.get().is_some() { + return Ok(()); + } + let _lock = PREPARING.lock().unwrap_or_else(|error| error.into_inner()); + if PREPARED.get().is_none() { + let path = cache_path()?; + install(&path)?; + let prepared = load(&path, REQUIRED_SYMBOLS)?; + PREPARED.get_or_init(|| prepared); + } + Ok(()) +} + +pub(super) fn symbol(index: usize) -> *mut c_void { + PREPARED + .get() + .expect("V8 engine must be prepared before dispatch") + .symbols[index] as *mut c_void +} + +#[cfg(test)] +mod oliphaunt_contract { + use super::*; + + #[test] + fn repairs_missing_truncated_and_wrong_engine_bytes() { + let root = tempfile::tempdir().unwrap(); + let path = root.path().join("engine.dll"); + for bytes in [None, Some(&DLL[..DLL.len() / 2]), Some(&b"wrong bytes"[..])] { + if let Some(bytes) = bytes { + std::fs::write(&path, bytes).unwrap(); + } + install(&path).unwrap(); + assert!(valid_payload(&path).unwrap()); + } + } + + #[test] + fn a_locked_bad_cache_reports_failure_and_can_be_retried() { + use std::os::windows::fs::OpenOptionsExt; + let root = tempfile::tempdir().unwrap(); + let path = root.path().join("engine.dll"); + std::fs::write(&path, b"wrong bytes").unwrap(); + let locked = std::fs::OpenOptions::new() + .read(true) + .share_mode(0) + .open(&path) + .unwrap(); + assert!(install(&path).is_err()); + drop(locked); + install(&path).unwrap(); + assert!(valid_payload(&path).unwrap()); + } + + #[test] + fn missing_exports_release_the_library_before_retry() { + let root = tempfile::tempdir().unwrap(); + let path = root.path().join("engine.dll"); + install(&path).unwrap(); + assert!(load(&path, &[b"oliphaunt_missing_entry\0"]).is_err()); + // Windows prevents removal of a DLL while its image is still loaded. + std::fs::remove_file(&path).unwrap(); + install(&path).unwrap(); + let prepared = load(&path, REQUIRED_SYMBOLS).unwrap(); + assert_eq!(prepared.symbols.len(), REQUIRED_SYMBOLS.len()); + drop(prepared); + std::fs::remove_file(&path).unwrap(); + } +} diff --git a/src/wasix/runtime/moon.yml b/src/wasix/runtime/moon.yml index d1b68546f..b6241e19a 100644 --- a/src/wasix/runtime/moon.yml +++ b/src/wasix/runtime/moon.yml @@ -47,6 +47,8 @@ owners: - "@oliphaunt/wasix" tools/**: - "@oliphaunt/wasix" + engine/**: + - "@oliphaunt/wasix" fileGroups: version: - VERSION @@ -58,6 +60,14 @@ fileGroups: cargo-carrier-sources: - crates/**/*.rs - crates/**/Cargo.toml + - engine/crates/**/*.rs + - engine/crates/**/Cargo.toml + - "!engine/crates/*/upstream/**" + engine-source: + - engine/**/* + - "!engine/crates/*/upstream/**" + - "!engine/crates/*/prebuilt/**" + - "!engine/crates/*/.prepared.json" extension-smoke-inputs: - /src/wasix/pgwire-server/Cargo.toml - /src/wasix/pgwire-server/tests/extensions.rs @@ -82,6 +92,50 @@ fileGroups: - /src/wasix/runtime/tools/wasix-cargo-toolchain-policy.mts - /tools/dev/bun.sh tasks: + engine-test: + tags: [runtime, requires-rust] + deps: ["liboliphaunt-wasix:engine-build"] + script: | + set -eu + case "$(uname -s)" in + MINGW*|MSYS*|CYGWIN*) + cargo nextest run -p oliphaunt-wasmer --locked --profile ci --no-default-features --features v8 --lib -E 'test(oliphaunt_contract)' --no-tests=fail --test-threads=1 + ;; + esac + inputs: ["@group(engine-source)", "@group(cargo-workspace)", "/.config/nextest.toml"] + options: + cache: false + runFromWorkspaceRoot: true + engine-build: + tags: [runtime, artifact, requires-rust] + command: bash src/wasix/runtime/engine/windows/build.sh + deps: + - liboliphaunt-wasix:engine-sources + inputs: + - "@group(engine-source)" + - /tools/dev/acquisition.sh + outputs: + - /target/oliphaunt-wasix/engine/windows-x64-msvc/oliphaunt_wee8.dll + - /target/oliphaunt-wasix/engine/windows-x64-msvc/cache-*.exe + - /target/oliphaunt-wasix/engine/windows-x64-msvc/source.json + - /target/oliphaunt-wasix/engine/windows-x64-msvc/LICENSE.v8 + - engine/crates/wasmer/prebuilt/embedded_bindings.rs + options: + cache: false + runFromWorkspaceRoot: true + engine-sources: + command: bash src/wasix/runtime/engine/prepare-sources.sh + inputs: + - "@group(engine-source)" + - /src/third-party/tools/fetch-sources.sh + - /src/third-party/tools/source-fetch-core.mts + - /tools/dev/acquisition.sh + outputs: + - engine/crates/*/upstream/**/* + - engine/crates/*/.prepared.json + options: + cache: false + runFromWorkspaceRoot: true build-orchestration-test: tags: - quality @@ -241,6 +295,7 @@ tasks: command: bash src/wasix/runtime/tools/build-aot-target.sh deps: - liboliphaunt-wasix:runtime-portable + - liboliphaunt-wasix:engine-test inputs: - "@group(legal-files)" - "@group(cargo-workspace)" @@ -373,6 +428,7 @@ tasks: options: runFromWorkspaceRoot: true packaging-unit: + deps: ["liboliphaunt-wasix:engine-sources"] tags: - quality - unit @@ -386,7 +442,7 @@ tasks: - /src/extensions/external/**/upstream-licenses/**/* - /src/database-resources/contracts/*.mts - "@group(upstream-licenses)" - - THIRD_PARTY_NOTICES.md + - /src/wasix/sdks/rust/THIRD_PARTY_NOTICES.md - "@group(legal-files)" - "@group(release-target-contract)" - "@group(package-test-metadata)" diff --git a/src/wasix/runtime/release.toml b/src/wasix/runtime/release.toml index 09d642241..577275239 100644 --- a/src/wasix/runtime/release.toml +++ b/src/wasix/runtime/release.toml @@ -3,6 +3,12 @@ owner = "@oliphaunt/wasix" kind = "wasm-runtime" publish_targets = ["github-release-assets", "crates-io", "npm"] registry_packages = [ + "crates:oliphaunt-wasmer", + "crates:oliphaunt-wasmer-wasix-types", + "crates:oliphaunt-wasmer-journal", + "crates:oliphaunt-wasmer-wasix", + "crates:oliphaunt-wasmer-v8-windows-x64-msvc", + "crates:liboliphaunt-wasix-portable", "crates:liboliphaunt-wasix-aot-aarch64-apple-darwin", "crates:liboliphaunt-wasix-aot-aarch64-unknown-linux-gnu", diff --git a/src/wasix/runtime/tools/check-release-assets.test.mts b/src/wasix/runtime/tools/check-release-assets.test.mts index 9018295ca..afe8b7282 100644 --- a/src/wasix/runtime/tools/check-release-assets.test.mts +++ b/src/wasix/runtime/tools/check-release-assets.test.mts @@ -38,7 +38,7 @@ function archiveStage(stage, archive, archiveRoot) { } function stageAotPayload(stage, target, profile = 'wasix-aot') { - const canonical = canonicalWasixAotMetadata(); + const canonical = canonicalWasixAotMetadata(undefined, target); const raw = Buffer.from(`aot-payload:${target}\n`); const compressed = zstdCompressSync(raw); const manifest = { diff --git a/src/wasix/runtime/tools/download-assets.sh b/src/wasix/runtime/tools/download-assets.sh index bece7954f..6aca75653 100755 --- a/src/wasix/runtime/tools/download-assets.sh +++ b/src/wasix/runtime/tools/download-assets.sh @@ -70,5 +70,8 @@ else bash .github/scripts/download-build-artifacts.sh CI "$sha" "$payload/target/oliphaunt-wasix/aot/$triple" "${download_args[@]}" --artifact "$artifact" install_args+=(--target-triple "$triple") done <<<"$rows" + if [[ "$rows" = *x86_64-pc-windows-msvc* ]]; then + bash .github/scripts/download-build-artifacts.sh CI "$sha" "$root" "${download_args[@]}" --artifact liboliphaunt-wasix-windows-engine + fi fi cargo run --quiet --locked -p xtask -- assets import-download "${install_args[@]}" diff --git a/src/wasix/runtime/tools/package-carriers.mts b/src/wasix/runtime/tools/package-carriers.mts index 1bc3b6535..530a58012 100644 --- a/src/wasix/runtime/tools/package-carriers.mts +++ b/src/wasix/runtime/tools/package-carriers.mts @@ -35,6 +35,9 @@ import { } from './wasix-extension-cargo-artifact-inventory.mts'; import { packWasixRuntimeNpmCarrier } from './wasix-runtime-npm-carrier.mts'; +import { ENGINE_PAYLOAD_PACKAGE } from '../engine/contract.mts'; +import { isEnginePayloadPart } from '../engine/package.mts'; + export const WASIX_PRODUCT = 'liboliphaunt-wasix'; function hasWasixReleaseArchive(assetDir) { @@ -109,6 +112,7 @@ export function validateWasixCargoArtifacts(outputDir) { } const packages = []; const allowedKinds = new Set([ + 'wasix-engine', 'wasix-runtime', 'wasix-aot', 'wasix-extension', @@ -138,6 +142,7 @@ export function validateWasixCargoArtifacts(outputDir) { } if ( !expectedBaseCrates.has(name) && + !(kind === 'wasix-engine' && isEnginePayloadPart(name)) && !isExpectedWasixExtensionPackage(name, kind, expectedExtensionInventory) ) { fail(`unexpected ${WASIX_PRODUCT} Cargo artifact crate ${name}`); @@ -166,6 +171,18 @@ export function validateWasixCargoArtifacts(outputDir) { `generated ${WASIX_PRODUCT} Cargo artifacts are missing configured runtime crates: ${missingBaseCrates.join(', ')}`, ); } + const engine = packages.find((row) => row.name === ENGINE_PAYLOAD_PACKAGE); + const engineManifest = Bun.TOML.parse(readFileSync(engine.manifestPath, 'utf8')); + const engineParts = Object.keys(engineManifest['build-dependencies'] ?? {}).filter( + isEnginePayloadPart, + ); + if (engineParts.length === 0) + fail('Windows engine carrier must declare its frozen payload parts'); + assertSameStringSet( + 'Windows engine payload parts must match the facade dependency closure', + new Set(packages.map((row) => row.name).filter(isEnginePayloadPart)), + new Set(engineParts), + ); const unexpected = readdirSync(outputDir) .filter((name) => name.endsWith('.crate')) .map((name) => path.join(outputDir, name)) diff --git a/src/wasix/runtime/tools/package-liboliphaunt-wasix-cargo-artifacts.test.mts b/src/wasix/runtime/tools/package-liboliphaunt-wasix-cargo-artifacts.test.mts index 1f5b6e0b3..696b30751 100644 --- a/src/wasix/runtime/tools/package-liboliphaunt-wasix-cargo-artifacts.test.mts +++ b/src/wasix/runtime/tools/package-liboliphaunt-wasix-cargo-artifacts.test.mts @@ -172,8 +172,8 @@ function aggregateFixture(root, { nestedOwner = false } = {}) { 2, )}\n`, ); - const canonicalAot = canonicalWasixAotMetadata(); for (const [targetId, targetTriple] of Object.entries(AOT_TARGET_TRIPLES)) { + const canonicalAot = canonicalWasixAotMetadata(undefined, targetTriple); for (const sqlName of ['cube', 'earthdistance']) { const directory = path.join(productRoot, 'wasix-aot', targetId, sqlName); const artifactName = `${sqlName}.bin.zst`; @@ -464,8 +464,8 @@ liboliphaunt-wasix-portable = { path = ${JSON.stringify(path.join(ROOT, 'src/was test('runtime Cargo packaging rejects an AOT carrier that cannot initialize fresh databases', () => { const root = mkdtempSync(path.join(scratch, 'runtime-aot-closure-')); - const canonical = canonicalWasixAotMetadata(); const target = supportedRustcHostTriple(); + const canonical = canonicalWasixAotMetadata(undefined, target); const raw = Buffer.from('core AOT fixture'); const bytes = zstdCompressSync(raw); const manifest = { diff --git a/src/wasix/runtime/tools/package_liboliphaunt_wasix_cargo_artifacts.mts b/src/wasix/runtime/tools/package_liboliphaunt_wasix_cargo_artifacts.mts index 1df7e1a00..3eb26bc45 100755 --- a/src/wasix/runtime/tools/package_liboliphaunt_wasix_cargo_artifacts.mts +++ b/src/wasix/runtime/tools/package_liboliphaunt_wasix_cargo_artifacts.mts @@ -66,6 +66,8 @@ import { wasixExtensionPackageName, } from './wasix-cargo-artifact-contract.mts'; +import { packageEngine } from '../engine/package.mts'; + const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../..'); const PRODUCT = 'liboliphaunt-wasix'; const PREFIX = 'package_liboliphaunt_wasix_cargo_artifacts.mts'; @@ -1766,6 +1768,7 @@ export function packageWasixCargoArtifacts(argv) { ); const specs = args.extensionsOnly ? [] : packageSpecs(assetDir, extractRoot, args.version); const packages = [ + ...(args.extensionsOnly ? [] : packageEngine({ sourceRoot, outputDir, version: args.version })), ...extensionSources.flatMap((source) => packageExtensionSource(source, { outputDir, cargoTargetDir }), ), diff --git a/src/wasix/runtime/tools/test-packaging.sh b/src/wasix/runtime/tools/test-packaging.sh index 719d33b2e..4254e15c0 100644 --- a/src/wasix/runtime/tools/test-packaging.sh +++ b/src/wasix/runtime/tools/test-packaging.sh @@ -17,6 +17,7 @@ if bun "$contract" unsupported > /dev/null 2>&1; then exit 1 fi bash tools/dev/bun.sh test ./src/wasix/runtime/tools +bash tools/dev/bun.sh test ./src/wasix/runtime/engine node src/wasix/runtime/tools/wasix-runtime-npm.test-consumer.mts for scenario in nested-owner aggregate independent-extension; do root="$OLIPHAUNT_WASIX_PACKAGING_TEST_ROOT/$scenario" diff --git a/src/wasix/runtime/tools/wasix-aot-manifest.mts b/src/wasix/runtime/tools/wasix-aot-manifest.mts index e81559ba5..01c0ae7aa 100644 --- a/src/wasix/runtime/tools/wasix-aot-manifest.mts +++ b/src/wasix/runtime/tools/wasix-aot-manifest.mts @@ -8,6 +8,10 @@ export { WASIX_TOOLCHAIN_PATH }; export const STABLE_WASIX_SOURCE_LANE = 'stable'; export const WASIX_AOT_ENGINE = 'llvm-opta'; +export function wasixAotEngine(target) { + return target === 'x86_64-pc-windows-msvc' ? 'v8' : WASIX_AOT_ENGINE; +} + function requiredString(value, context) { if (typeof value !== 'string' || value.length === 0) { throw new Error(`${context} must be a non-empty string`); @@ -15,11 +19,11 @@ function requiredString(value, context) { return value; } -export function canonicalWasixAotMetadata(root = ROOT) { +export function canonicalWasixAotMetadata(root = ROOT, target) { const toolchain = canonicalWasixCargoToolchainVersions(root); return { sourceLane: STABLE_WASIX_SOURCE_LANE, - engine: WASIX_AOT_ENGINE, + engine: wasixAotEngine(target), wasmerVersion: toolchain.wasmer, wasmerWasixVersion: toolchain.wasmerWasix, }; @@ -27,11 +31,12 @@ export function canonicalWasixAotMetadata(root = ROOT) { export function assertCanonicalWasixAotManifest( manifest, - { context = 'WASIX AOT manifest', expectedTarget, canonical = canonicalWasixAotMetadata() } = {}, + { context = 'WASIX AOT manifest', expectedTarget, canonical } = {}, ) { if (manifest === null || typeof manifest !== 'object' || Array.isArray(manifest)) { throw new Error(`${context} must be a JSON object`); } + canonical ??= canonicalWasixAotMetadata(undefined, expectedTarget ?? manifest['target-triple']); const expected = [ ['format-version', 1], ['source-lane', canonical.sourceLane], diff --git a/src/wasix/runtime/tools/wasix-cargo-artifact-contract.mts b/src/wasix/runtime/tools/wasix-cargo-artifact-contract.mts index 238a65614..4cd3696e2 100644 --- a/src/wasix/runtime/tools/wasix-cargo-artifact-contract.mts +++ b/src/wasix/runtime/tools/wasix-cargo-artifact-contract.mts @@ -1,3 +1,4 @@ +import { ENGINE_CARGO_PACKAGES } from '../engine/contract.mts'; import { compareText } from '../../../../tools/release/release-graph.mts'; export const WASIX_CARGO_ARTIFACT_SCHEMA = 'oliphaunt-liboliphaunt-wasix-cargo-artifacts-v2'; @@ -105,7 +106,9 @@ export const AOT_TARGET_CFGS = { }; export function publicCargoPackageNames() { - return [RUNTIME_PACKAGE, ...Object.values(AOT_PACKAGES)].sort(compareText); + return [RUNTIME_PACKAGE, ...Object.values(AOT_PACKAGES), ...ENGINE_CARGO_PACKAGES].sort( + compareText, + ); } export function publicAotCargoDependencies() { diff --git a/src/wasix/runtime/tools/wasix-cargo-toolchain-policy.mts b/src/wasix/runtime/tools/wasix-cargo-toolchain-policy.mts index b74886d30..062fd4eec 100644 --- a/src/wasix/runtime/tools/wasix-cargo-toolchain-policy.mts +++ b/src/wasix/runtime/tools/wasix-cargo-toolchain-policy.mts @@ -1,6 +1,7 @@ import { readFileSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; +import { ENGINE_SOURCE_CRATES } from '../engine/contract.mts'; const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../..'); const CARGO_DEPENDENCY_SOURCE_KEYS = Object.freeze([ @@ -34,10 +35,14 @@ const REQUIRED_WASIX_TOOLCHAIN_PACKAGES = new Map([ const REQUIRED_CONSUMER_PIN_POLICIES = new Map( [...REQUIRED_WASIX_TOOLCHAIN_PACKAGES] - .filter(([, versionKey]) => versionKey === 'wasmerWasix') + .filter(([name, versionKey]) => versionKey === 'wasmerWasix' || name === 'wasmer') .map(([name, versionKey]) => [ name, - Object.freeze({ versionKey, defaultFeaturesDisabled: true }), + Object.freeze({ + versionKey: ENGINE_SOURCE_CRATES.includes(name) ? 'engine' : versionKey, + packageName: ENGINE_SOURCE_CRATES.includes(name) ? `oliphaunt-${name}` : name, + defaultFeaturesDisabled: true, + }), ]), ); REQUIRED_CONSUMER_PIN_POLICIES.set( @@ -70,6 +75,7 @@ export function canonicalWasixCargoToolchainVersions(root = ROOT) { } const toolchain = objectTable(data.toolchain); return Object.freeze({ + engine: readFileSync(path.join(root, 'src/wasix/runtime/VERSION'), 'utf8').trim(), wasmer: requiredString(toolchain.wasmer, `${WASIX_TOOLCHAIN_PATH} toolchain.wasmer`), wasmerWasix: requiredString( toolchain['wasmer-wasix'], @@ -95,6 +101,7 @@ export function validateWasixConsumerDependencyPins( const failures = []; const dependencies = objectTable(manifest?.dependencies); for (const [name, policy] of REQUIRED_CONSUMER_PIN_POLICIES) { + const packageName = policy.packageName ?? name; const expectedVersion = toolchainVersions?.[policy.versionKey]; if (typeof expectedVersion !== 'string' || expectedVersion.length === 0) { failures.push( @@ -103,7 +110,7 @@ export function validateWasixConsumerDependencyPins( continue; } const matches = Object.entries(dependencies).filter( - ([key, spec]) => dependencyName(key, spec) === name, + ([key, spec]) => dependencyName(key, spec) === packageName, ); if (matches.length !== 1) { failures.push( diff --git a/src/wasix/runtime/tools/wasix-cargo-toolchain-policy.test.mts b/src/wasix/runtime/tools/wasix-cargo-toolchain-policy.test.mts index 541654a99..6e1a13485 100644 --- a/src/wasix/runtime/tools/wasix-cargo-toolchain-policy.test.mts +++ b/src/wasix/runtime/tools/wasix-cargo-toolchain-policy.test.mts @@ -3,15 +3,31 @@ import { REQUIRED_WASIX_CONSUMER_PINS, validateWasixConsumerDependencyPins, } from './wasix-cargo-toolchain-policy.mts'; -const toolchainVersions = { wasmer: '7.2.1', wasmerWasix: '0.702.1', webc: '12.0.0' }; +import { ENGINE_SOURCE_CRATES } from '../engine/contract.mts'; +const toolchainVersions = { + engine: '0.3.1', + wasmer: '7.2.1', + wasmerWasix: '0.702.1', + webc: '12.0.0', +}; -test('requires exact non-optional pins for the published WASIX family closure', () => { - const dependencies = Object.fromEntries( +function pinnedDependencies() { + return Object.fromEntries( REQUIRED_WASIX_CONSUMER_PINS.map((name) => [ name, - name === 'webc' ? '=12.0.0' : { version: '=0.702.1', 'default-features': false }, + name === 'webc' + ? '=12.0.0' + : { + version: ENGINE_SOURCE_CRATES.includes(name) ? '=0.3.1' : '=0.702.1', + 'default-features': false, + ...(ENGINE_SOURCE_CRATES.includes(name) ? { package: 'oliphaunt-' + name } : {}), + }, ]), ); +} + +test('requires exact non-optional pins for the published WASIX family closure', () => { + const dependencies = pinnedDependencies(); expect( validateWasixConsumerDependencyPins( { dependencies }, @@ -39,12 +55,7 @@ test('requires exact non-optional pins for the published WASIX family closure', }); test('rejects default-feature and source substitutions in published WASIX pins', () => { - const dependencies = Object.fromEntries( - REQUIRED_WASIX_CONSUMER_PINS.map((name) => [ - name, - name === 'webc' ? '=12.0.0' : { version: '=0.702.1', 'default-features': false }, - ]), - ); + const dependencies = pinnedDependencies(); delete dependencies['wasmer-config']['default-features']; dependencies['wasmer-journal']['default-features'] = true; dependencies['wasmer-package'].path = '../../substituted'; @@ -66,12 +77,7 @@ test('rejects default-feature and source substitutions in published WASIX pins', }); test('rejects missing, ranged, optional, and source-substituted WebC pins', () => { - const dependencies = Object.fromEntries( - REQUIRED_WASIX_CONSUMER_PINS.map((name) => [ - name, - name === 'webc' ? '=12.0.0' : { version: '=0.702.1', 'default-features': false }, - ]), - ); + const dependencies = pinnedDependencies(); delete dependencies.webc; expect( diff --git a/src/wasix/runtime/tools/xtask/Cargo.toml b/src/wasix/runtime/tools/xtask/Cargo.toml index dbdde65d1..46274cbd3 100644 --- a/src/wasix/runtime/tools/xtask/Cargo.toml +++ b/src/wasix/runtime/tools/xtask/Cargo.toml @@ -10,8 +10,6 @@ publish = false default = [] aot-serializer = [ "dep:wasmer", - "wasmer/llvm", - "wasmer/wasmer-artifact-create", "dep:wasmer-types", ] @@ -24,9 +22,15 @@ sha2 = "0.10" tar = "0.4" toml = "0.9" walkdir = "2" -wasmer = { version = "=7.5.0", default-features = false, features = [ - "sys", -], optional = true } +wasmer = { package = "oliphaunt-wasmer", version = "*", path = "../../engine/crates/wasmer", default-features = false, optional = true } wasmer-types = { version = "=7.5.0", optional = true } wasmparser = "0.261.0" zstd = "0.13" + +[target.'cfg(not(windows))'.dependencies] +wasmer = { package = "oliphaunt-wasmer", version = "*", path = "../../engine/crates/wasmer", default-features = false, optional = true, features = [ + "sys", "llvm", "wasmer-artifact-create", +] } + +[target.'cfg(windows)'.dependencies] +wasmer = { package = "oliphaunt-wasmer", version = "*", path = "../../engine/crates/wasmer", default-features = false, optional = true, features = ["v8"] } diff --git a/src/wasix/runtime/tools/xtask/moon.yml b/src/wasix/runtime/tools/xtask/moon.yml index 9192860da..81f31e663 100644 --- a/src/wasix/runtime/tools/xtask/moon.yml +++ b/src/wasix/runtime/tools/xtask/moon.yml @@ -34,6 +34,7 @@ tasks: aot-serializer-typecheck: tags: ["quality", "static", "requires-rust", "requires-wasmer-llvm"] command: "cargo check -p xtask --features aot-serializer --locked" + deps: ["liboliphaunt-wasix:engine-sources"] env: CARGO_TARGET_DIR: "target" inputs: diff --git a/src/wasix/runtime/tools/xtask/src/aot_serializer.rs b/src/wasix/runtime/tools/xtask/src/aot_serializer.rs index 5849e7e5e..5f2f83950 100644 --- a/src/wasix/runtime/tools/xtask/src/aot_serializer.rs +++ b/src/wasix/runtime/tools/xtask/src/aot_serializer.rs @@ -18,7 +18,18 @@ use crate::value_after; // This profile names the fixed codegen policy in our published manifests; // Wasmer's deterministic_id independently identifies the compiler settings. +#[cfg(not(windows))] pub(crate) const AOT_ENGINE_PROFILE: &str = "llvm-opta"; +#[cfg(windows)] +pub(crate) const AOT_ENGINE_PROFILE: &str = "v8"; + +pub(crate) fn aot_engine_profile(target: &str) -> &'static str { + if target == "x86_64-pc-windows-msvc" { + "v8" + } else { + "llvm-opta" + } +} pub(crate) fn check_aot_codegen_environment() -> Result<()> { for (name, expected) in [ @@ -86,14 +97,14 @@ fn probe_aot_serializer_in_process() -> Result<()> { #[cfg(feature = "aot-serializer")] fn probe_aot_serializer_in_process() -> Result<()> { - let engine = llvm_aot_engine(); + let engine = aot_engine()?; let store = wasmer::Store::new(engine.clone()); const EMPTY_WASM: &[u8] = b"\0asm\x01\0\0\0"; - let module = - wasmer::Module::new(&store, EMPTY_WASM).context("compile LLVM AOT probe module")?; + let module = wasmer::Module::new(&store, EMPTY_WASM) + .context("compile engine serialization probe module")?; let serialized = module .serialize() - .context("serialize LLVM AOT probe module")?; + .context("serialize engine probe module")?; print_aot_engine_config(&engine); println!("serialized-probe-bytes: {}", serialized.len()); Ok(()) @@ -101,7 +112,7 @@ fn probe_aot_serializer_in_process() -> Result<()> { #[cfg(feature = "aot-serializer")] fn serialize_aot_module(input: &Path, output: &Path) -> Result<()> { - let engine = llvm_aot_engine(); + let engine = aot_engine()?; print_aot_engine_config(&engine); println!("host-target: {}-{}", env::consts::OS, env::consts::ARCH); @@ -109,7 +120,35 @@ fn serialize_aot_module(input: &Path, output: &Path) -> Result<()> { let bytes = fs::read(input).with_context(|| format!("read {}", input.display()))?; let module = wasmer::Module::new(&store, &bytes) .with_context(|| format!("compile {}", input.display()))?; + #[cfg(not(windows))] let serialized = module.serialize().context("serialize module")?; + #[cfg(windows)] + let serialized = { + let digest = crate::fs_utils::sha256_bytes(&bytes); + let engine = PathBuf::from("target/oliphaunt-wasix/engine/windows-x64-msvc"); + let path = engine.join("cache/sse41").join(format!("{digest}.bin")); + let baseline = fs::read(&path) + .with_context(|| format!("read qualified SSE4.1 native cache {}", path.display()))?; + let source: serde_json::Value = + serde_json::from_slice(&fs::read(engine.join("source.json"))?)?; + let receipt: serde_json::Value = + serde_json::from_slice(&fs::read(engine.join("cache/sse41/source.json"))?)?; + let head = std::process::Command::new("git") + .args(["rev-parse", "HEAD"]) + .output()?; + anyhow::ensure!(head.status.success(), "read producer source commit"); + let source_sha = String::from_utf8(head.stdout)?; + anyhow::ensure!( + source["sourceSha"] == source_sha.trim(), + "stale Windows engine source" + ); + let dll_sha = crate::fs_utils::sha256_file(&engine.join("oliphaunt_wee8.dll"))?; + validate_windows_cache_receipt(&receipt, source_sha.trim(), &dll_sha, &digest, &baseline)?; + module + .as_v8() + .serialize_with_native_variants(&[baseline]) + .context("serialize Windows CPU cache profiles")? + }; if let Some(parent) = output.parent() { fs::create_dir_all(parent).with_context(|| format!("create {}", parent.display()))?; @@ -131,8 +170,51 @@ fn serialize_aot_module(input: &Path, output: &Path) -> Result<()> { Ok(()) } -#[cfg(feature = "aot-serializer")] -fn llvm_aot_engine() -> wasmer::Engine { +#[cfg(any(all(feature = "aot-serializer", windows), test))] +fn validate_windows_cache_receipt( + receipt: &serde_json::Value, + source_sha: &str, + dll_sha: &str, + guest_sha: &str, + cache: &[u8], +) -> Result<()> { + use anyhow::ensure; + ensure!( + receipt["sourceSha"] == source_sha, + "stale Windows baseline cache source" + ); + ensure!( + receipt["dllSha256"] == dll_sha, + "Windows baseline cache engine differs" + ); + ensure!( + receipt["nativeCpuMask"] == "0xe", + "Windows baseline CPU profile differs" + ); + let row = receipt["modules"] + .as_array() + .and_then(|rows| rows.iter().find(|row| row["moduleSha256"] == guest_sha)) + .ok_or_else(|| anyhow::anyhow!("Windows baseline cache has no guest receipt"))?; + ensure!( + row["bytes"] == cache.len(), + "Windows baseline cache byte size differs" + ); + ensure!( + row["cacheSha256"] == crate::fs_utils::sha256_bytes(cache), + "Windows baseline cache digest differs" + ); + Ok(()) +} + +#[cfg(all(feature = "aot-serializer", windows))] +fn aot_engine() -> Result { + Ok(wasmer::v8::V8::try_new() + .context("prepare Windows AOT producer engine")? + .into()) +} + +#[cfg(all(feature = "aot-serializer", not(windows)))] +fn aot_engine() -> Result { use wasmer::sys::{CompilerConfig, EngineBuilder, Features, LLVM}; let mut features = Features::new(); @@ -145,14 +227,14 @@ fn llvm_aot_engine() -> wasmer::Engine { // correctness/performance change, not part of patch consolidation. llvm.enable_non_volatile_memops(); llvm.enable_readonly_funcref_table(); - EngineBuilder::new(llvm) + Ok(EngineBuilder::new(llvm) .set_target(Some(portable_aot_target())) .set_features(Some(features)) .engine() - .into() + .into()) } -#[cfg(feature = "aot-serializer")] +#[cfg(all(feature = "aot-serializer", not(windows)))] fn portable_aot_target() -> wasmer_types::target::Target { use wasmer_types::target::{Architecture, CpuFeature, Target, Triple}; @@ -161,6 +243,9 @@ fn portable_aot_target() -> wasmer_types::target::Target { match triple.architecture { Architecture::X86_64 => { cpu_features.insert(CpuFeature::SSE2); + cpu_features.insert(CpuFeature::SSE3); + cpu_features.insert(CpuFeature::SSSE3); + cpu_features.insert(CpuFeature::SSE41); } Architecture::Aarch64(_) => { cpu_features.insert(CpuFeature::NEON); @@ -171,7 +256,17 @@ fn portable_aot_target() -> wasmer_types::target::Target { Target::new(triple, cpu_features) } -#[cfg(feature = "aot-serializer")] +#[cfg(all(feature = "aot-serializer", windows))] +fn print_aot_engine_config(_engine: &wasmer::Engine) { + println!("wasmer-engine: v8"); + println!("wasmer-engine-id: {AOT_ENGINE_PROFILE}"); + println!("wasmer-target-triple: x86_64-pc-windows-msvc"); + println!("wasmer-wee8-version: 11.9.7"); + println!("wasmer-v8-version: 13.6.233.17"); + println!("wasmer-feature-exceptions: enabled"); +} + +#[cfg(all(feature = "aot-serializer", not(windows)))] fn print_aot_engine_config(engine: &wasmer::Engine) { let target = portable_aot_target(); println!("wasmer-engine: llvm"); @@ -188,7 +283,7 @@ fn print_aot_engine_config(engine: &wasmer::Engine) { println!("wasmer-llvm-readonly-funcref-table: enabled"); } -#[cfg(feature = "aot-serializer")] +#[cfg(all(feature = "aot-serializer", not(windows)))] fn format_aot_cpu_features(target: &wasmer_types::target::Target) -> String { let mut features = target .cpu_features() @@ -203,7 +298,7 @@ fn format_aot_cpu_features(target: &wasmer_types::target::Target) -> String { } } -#[cfg(feature = "aot-serializer")] +#[cfg(all(feature = "aot-serializer", not(windows)))] fn env_flag(name: &str) -> bool { env::var(name) .map(|value| { @@ -221,9 +316,31 @@ fn env_flag(name: &str) -> bool { mod tests { use super::*; + #[test] + fn windows_cache_receipt_rejects_stale_engine_source_and_corrupt_bytes() { + let cache = b"native cache"; + let receipt = serde_json::json!({ + "sourceSha": "source", "dllSha256": "engine", "nativeCpuMask": "0xe", + "modules": [{ "moduleSha256": "guest", "bytes": cache.len(), + "cacheSha256": crate::fs_utils::sha256_bytes(cache) }] + }); + validate_windows_cache_receipt(&receipt, "source", "engine", "guest", cache).unwrap(); + for (source, engine, guest, bytes) in [ + ("other", "engine", "guest", &cache[..]), + ("source", "other", "guest", &cache[..]), + ("source", "engine", "other", &cache[..]), + ("source", "engine", "guest", &b"broken cache"[..]), + ] { + assert!( + validate_windows_cache_receipt(&receipt, source, engine, guest, bytes).is_err() + ); + } + } + #[test] fn fixed_codegen_profile_rejects_conflicting_and_ambiguous_overrides() { - assert_eq!(AOT_ENGINE_PROFILE, "llvm-opta"); + assert_eq!(aot_engine_profile("x86_64-pc-windows-msvc"), "v8"); + assert_eq!(aot_engine_profile("x86_64-unknown-linux-gnu"), "llvm-opta"); for expected in [false, true] { validate_profile_override("test", None, expected).unwrap(); validate_profile_override("test", Some(if expected { "1" } else { "0" }), expected) diff --git a/src/wasix/runtime/tools/xtask/src/asset_pipeline.rs b/src/wasix/runtime/tools/xtask/src/asset_pipeline.rs index a9da24168..f98b5b007 100644 --- a/src/wasix/runtime/tools/xtask/src/asset_pipeline.rs +++ b/src/wasix/runtime/tools/xtask/src/asset_pipeline.rs @@ -1279,6 +1279,16 @@ fn has_wasm_export(link: &WasmLinkMetadataOut, name: &str) -> bool { .any(|export| export.name == name || export.name == format!("_{name}")) } +fn aot_file(module: &BuildModuleOutput, target: &str) -> String { + module.aot_file.replace( + "-llvm-opta.bin.zst", + &format!( + "-{}.bin.zst", + crate::aot_serializer::aot_engine_profile(target) + ), + ) +} + fn aot_source_dir(product: AssetProduct, target: &str) -> PathBuf { // Old producer outputs used a different memory-codegen policy. A fresh // profile directory prevents packaging those bytes under the new identity. @@ -1286,7 +1296,7 @@ fn aot_source_dir(product: AssetProduct, target: &str) -> PathBuf { .root() .join("aot-source") .join(target) - .join(crate::aot_serializer::AOT_ENGINE_PROFILE) + .join(crate::aot_serializer::aot_engine_profile(target)) } pub(crate) fn prepare_aot_artifacts( @@ -1309,7 +1319,7 @@ pub(crate) fn prepare_aot_artifacts( .iter() .filter(|module| module.requires_aot && product.owns(&module.name)) { - let output = source_dir.join(&module.aot_file); + let output = source_dir.join(aot_file(module, target)); let input = fs::canonicalize(&module.path) .with_context(|| format!("canonicalize {}", module.path.display()))?; let input = input.to_str().context("AOT input path is not UTF-8")?; @@ -1960,7 +1970,8 @@ fn package_aot_artifacts( .filter(|module| module.requires_aot && outputs.product.owns(&module.name)) { let name = module.name.as_str(); - let file = module.aot_file.as_str(); + let file = aot_file(module, target); + let file = file.as_str(); let source = source_dir.join(file); if !source.exists() { bail!( @@ -2000,7 +2011,7 @@ fn package_aot_artifacts( source_fingerprint: outputs.source_fingerprint.clone(), postgres_version: Some(outputs.postgres_version.clone()), target_triple: target.to_owned(), - engine: crate::aot_serializer::AOT_ENGINE_PROFILE.to_owned(), + engine: crate::aot_serializer::aot_engine_profile(target).to_owned(), wasmer_version: sources.toolchain.wasmer.clone(), wasmer_wasix_version: sources.toolchain.wasmer_wasix.clone(), artifacts: manifest_artifacts, @@ -2053,7 +2064,7 @@ pub(crate) fn package_extension_aot_artifacts( let Some(sql_name) = extension_module_sql_name(&module.name) else { bail!("extension AOT module has invalid name {}", module.name); }; - let source = source_dir.join(&module.aot_file); + let source = source_dir.join(aot_file(module, target)); if !source.exists() { bail!( "missing extension AOT artifact {}; run AOT generation for target {target} before packaging", @@ -2063,7 +2074,7 @@ pub(crate) fn package_extension_aot_artifacts( let extension_dir = artifacts_root.join(sql_name); fs::create_dir_all(&extension_dir) .with_context(|| format!("create {}", extension_dir.display()))?; - let destination = extension_dir.join(&module.aot_file); + let destination = extension_dir.join(aot_file(module, target)); copy_file(&source, &destination)?; let raw_artifact = decode_zstd_file(&destination) .with_context(|| format!("decode extension AOT artifact {}", destination.display()))?; @@ -2072,7 +2083,7 @@ pub(crate) fn package_extension_aot_artifacts( .or_default() .push(AotManifestArtifact { name: module.name.clone(), - path: module.aot_file.clone(), + path: aot_file(module, target), sha256: sha256_file(&destination)?, raw_sha256: sha256_bytes(&raw_artifact), raw_size: raw_artifact.len() as u64, @@ -2094,7 +2105,7 @@ pub(crate) fn package_extension_aot_artifacts( source_fingerprint: outputs.source_fingerprint.clone(), postgres_version: Some(outputs.postgres_version.clone()), target_triple: target.to_owned(), - engine: crate::aot_serializer::AOT_ENGINE_PROFILE.to_owned(), + engine: crate::aot_serializer::aot_engine_profile(target).to_owned(), wasmer_version: sources.toolchain.wasmer.clone(), wasmer_wasix_version: sources.toolchain.wasmer_wasix.clone(), artifacts, @@ -2161,7 +2172,7 @@ pub(crate) fn check_aot_product_manifest( )?; ensure_eq( &manifest.engine, - crate::aot_serializer::AOT_ENGINE_PROFILE, + crate::aot_serializer::aot_engine_profile(target), "AOT manifest engine", )?; ensure_eq( @@ -2586,7 +2597,7 @@ mod tests { let legacy = product.root().join("aot-source").join(target); assert_eq!( aot_source_dir(product, target), - legacy.join(crate::aot_serializer::AOT_ENGINE_PROFILE) + legacy.join(crate::aot_serializer::aot_engine_profile(target)) ); assert_ne!(aot_source_dir(product, target), legacy); } @@ -2725,7 +2736,7 @@ mod tests { source_fingerprint: source_fingerprint.map(str::to_owned), postgres_version: postgres_version.map(str::to_owned), target_triple: "aarch64-apple-darwin".to_owned(), - engine: crate::aot_serializer::AOT_ENGINE_PROFILE.to_owned(), + engine: crate::aot_serializer::aot_engine_profile("aarch64-apple-darwin").to_owned(), wasmer_version: toolchain.wasmer, wasmer_wasix_version: toolchain.wasmer_wasix, artifacts: vec![AotManifestArtifact { diff --git a/src/wasix/sdks/rust/Cargo.toml b/src/wasix/sdks/rust/Cargo.toml index 214676143..e558e14f2 100644 --- a/src/wasix/sdks/rust/Cargo.toml +++ b/src/wasix/sdks/rust/Cargo.toml @@ -102,32 +102,38 @@ liboliphaunt-wasix-portable = { version = "*", path = "../../runtime/crates/asse oliphaunt-wasix-tools = { version = "*", path = "../../postgres-tools/crates/tools", optional = true } oliphaunt-icu = { version = "*", path = "../../../database-resources/icu/cargo", optional = true } tokio = { version = "1", features = ["io-util", "rt-multi-thread", "sync"] } -wasmer = { version = "=7.5.0", default-features = false, features = [ - "sys", - "headless", - "compiler", - "wasmer-artifact-load", -] } +wasmer = { package = "oliphaunt-wasmer", version = "*", path = "../../runtime/engine/crates/wasmer", default-features = false } # Wasmer-WASIX 0.x crates use compatible ranges for packages in their own # release family. Keep the complete family constrained here so a fresh # consumer cannot silently mix a later patch generation with the runtime and # AOT artifacts built against this exact toolchain. wasmer-config = { version = "=0.705.0", default-features = false } -wasmer-journal = { version = "=0.705.0", default-features = false } +wasmer-journal = { package = "oliphaunt-wasmer-journal", version = "*", path = "../../runtime/engine/crates/wasmer-journal", default-features = false } wasmer-package = { version = "=0.705.0", default-features = false } wasmer-types = "=7.5.0" -wasmer-wasix = { version = "=0.705.0", default-features = false, features = [ +wasmer-wasix = { package = "oliphaunt-wasmer-wasix", version = "*", path = "../../runtime/engine/crates/wasmer-wasix", default-features = false, features = [ "sys-minimal", "sys-poll", "host-vnet", "time", ] } -wasmer-wasix-types = { version = "=0.705.0", default-features = false } +wasmer-wasix-types = { package = "oliphaunt-wasmer-wasix-types", version = "*", path = "../../runtime/engine/crates/wasmer-wasix-types", default-features = false } virtual-fs = { version = "=0.705.0", default-features = false } virtual-mio = { version = "=0.705.0", default-features = false } virtual-net = { version = "=0.705.0", default-features = false } webc = "=12.0.1" +[target.'cfg(not(windows))'.dependencies] +wasmer = { package = "oliphaunt-wasmer", version = "*", path = "../../runtime/engine/crates/wasmer", default-features = false, features = [ + "sys", "headless", "compiler", "wasmer-artifact-load", +] } + +[target.'cfg(windows)'.dependencies] +# WASIX sys-minimal retains Sys types. Headless satisfies that dependency +# without enabling a Windows compiler; runtime stores explicitly use V8. +wasmer = { package = "oliphaunt-wasmer", version = "*", path = "../../runtime/engine/crates/wasmer", default-features = false, features = ["v8", "headless"] } +wasmer-wasix = { package = "oliphaunt-wasmer-wasix", version = "*", path = "../../runtime/engine/crates/wasmer-wasix", default-features = false, features = ["v8"] } + [target.'cfg(all(target_os = "macos", target_arch = "aarch64"))'.dependencies] liboliphaunt-wasix-aot-aarch64-apple-darwin = { version = "*", path = "../../runtime/crates/aot/aarch64-apple-darwin" } oliphaunt-wasix-tools-aot-aarch64-apple-darwin = { version = "*", path = "../../postgres-tools/crates/aot/aarch64-apple-darwin", optional = true } diff --git a/src/wasix/sdks/rust/THIRD_PARTY_NOTICES.md b/src/wasix/sdks/rust/THIRD_PARTY_NOTICES.md index 30c809e64..6bf80856e 100644 --- a/src/wasix/sdks/rust/THIRD_PARTY_NOTICES.md +++ b/src/wasix/sdks/rust/THIRD_PARTY_NOTICES.md @@ -16,9 +16,14 @@ bytes under `THIRD_PARTY_LICENSES/`: - `ICU-LICENSE` — ICU commit `8eca245c7484ac6cc179e3e5f7c1ea7680810f39`. - `OpenSSL-LICENSE.txt` — OpenSSL commit `286ddeaac037533bbdce65b3c689e3f7ffebf0f6`. +- `Wasmer-LICENSE` — Wasmer 7.5.0 and WASIX 0.705.0, with the compatibility + patches owned by `src/wasix/runtime/engine/`. +- `V8-LICENSES.txt` — Windows wee8 11.9.7 / V8 13.6.233.17, including the + licenses of the libraries bundled into that engine. Third-party source pins for optional external extensions are maintained in `src/third-party/`, and WASIX toolchain inputs are maintained in -`tools/dev/`. Exact SQL extension selection is modeled in -`extensions/`; generated WASM assets must include only the +`src/wasix/runtime/toolchain.toml` and `src/wasix/runtime/engine/source.toml`. +Exact SQL extension selection is modeled in +`src/extensions/`; generated WASM assets must include only the extension artifacts explicitly selected for the release payload. diff --git a/src/wasix/sdks/rust/moon.yml b/src/wasix/sdks/rust/moon.yml index 5cd5a3370..528d82ccb 100644 --- a/src/wasix/sdks/rust/moon.yml +++ b/src/wasix/sdks/rust/moon.yml @@ -38,6 +38,8 @@ fileGroups: tasks: cargo-sources: + deps: + - liboliphaunt-wasix:engine-build inputs: - project: "liboliphaunt-wasix" group: "cargo-carrier-sources" @@ -188,6 +190,7 @@ tasks: runFromWorkspaceRoot: true package: + deps: ["liboliphaunt-wasix:engine-sources"] tags: ["release", "artifact-package", "ci-wasix-rust-package"] script: | set -eu diff --git a/src/wasix/sdks/rust/src/oliphaunt/aot.rs b/src/wasix/sdks/rust/src/oliphaunt/aot.rs index 7296b9c09..c4a11eb4d 100644 --- a/src/wasix/sdks/rust/src/oliphaunt/aot.rs +++ b/src/wasix/sdks/rust/src/oliphaunt/aot.rs @@ -8,6 +8,7 @@ use anyhow::{Context, Result, bail, ensure}; use directories::ProjectDirs; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; +#[cfg(not(windows))] use wasmer::sys::{EngineBuilder, Features, NativeEngineExt}; use wasmer::{Engine, Module}; use zstd::stream::read::Decoder as ZstdDecoder; @@ -15,18 +16,17 @@ use zstd::stream::read::Decoder as ZstdDecoder; use super::assets; const RUNTIME_ARTIFACT: &str = "runtime:oliphaunt"; +#[cfg(not(windows))] const EXPECTED_AOT_ENGINE: &str = "llvm-opta"; +#[cfg(windows)] +const EXPECTED_AOT_ENGINE: &str = "v8"; const EXPECTED_WASMER_VERSION: &str = "7.5.0"; const EXPECTED_WASMER_WASIX_VERSION: &str = "0.705.0"; -const AOT_ENGINE_ID: &str = concat!( - "engine=", - "llvm-opta", - ";wasmer=", - "7.5.0", - ";wasmer-wasix=", - "0.705.0", - ";cpu=generic-baseline" -); +#[cfg(not(windows))] +const AOT_ENGINE_ID: &str = "engine=llvm-opta;wasmer=7.5.0;wasmer-wasix=0.705.0;cpu=sse41-or-neon"; +#[cfg(windows)] +const AOT_ENGINE_ID: &str = + "engine=v8;wasmer=7.5.0;wasmer-wasix=0.705.0;wee8=11.9.7;v8=13.6.233.17;cpu=sse41"; const ZSTD_MAGIC: &[u8] = &[0x28, 0xb5, 0x2f, 0xfd]; const CACHE_RECEIPT_FORMAT_VERSION: u32 = 1; const TOOL_AOT_ARTIFACTS: &[&str] = &["tool:pg_dump", "tool:psql"]; @@ -47,21 +47,29 @@ enum AotVerifyMode { Full, } -pub(crate) fn headless_engine() -> Engine { - HEADLESS_ENGINE - .get_or_init(|| { - let mut features = Features::new(); - features.exceptions(true); - EngineBuilder::headless() - .set_features(Some(features)) - .engine() - .into() - }) - .clone() +pub(crate) fn headless_engine() -> Result { + if let Some(engine) = HEADLESS_ENGINE.get() { + return Ok(engine.clone()); + } + #[cfg(windows)] + let engine: Engine = wasmer::v8::V8::try_new() + .context("prepare Windows runtime engine")? + .into(); + #[cfg(not(windows))] + let engine: Engine = { + let mut features = Features::new(); + features.exceptions(true); + EngineBuilder::headless() + .set_features(Some(features)) + .engine() + .into() + }; + // Cache successful construction only so delivery failures can be retried. + Ok(HEADLESS_ENGINE.get_or_init(|| engine).clone()) } pub(crate) fn load_runtime_module() -> Result<(Engine, Module)> { - let engine = headless_engine(); + let engine = headless_engine()?; let module = load_artifact_module(&engine, RUNTIME_ARTIFACT)?; Ok((engine, module)) } @@ -368,7 +376,7 @@ fn artifact_raw_bytes( bytes.to_vec() } else { bail!( - "no package-manager-resolved Wasmer LLVM AOT artifact named '{name}' is available for target {}; publish and stage the matching liboliphaunt-wasix AOT artifact crate with the application", + "no package-manager-resolved Wasmer runtime artifact named '{name}' is available for target {}; publish and stage the matching liboliphaunt-wasix AOT artifact crate with the application", target_triple() ) }; @@ -557,7 +565,7 @@ fn target_aot_manifest() -> Result { return Ok(manifest); } bail!( - "no package-manager-resolved Wasmer LLVM AOT manifest is available for target {}; publish and stage the matching liboliphaunt-wasix AOT artifact crate with the application", + "no package-manager-resolved Wasmer runtime manifest is available for target {}; publish and stage the matching liboliphaunt-wasix AOT artifact crate with the application", target_triple() ) } @@ -803,9 +811,20 @@ fn aot_verify_mode() -> Result { #[allow(unsafe_code)] fn deserialize_headless(engine: &Engine, path: &Path) -> Result { - deserialize_headless_mmap(engine, path) + #[cfg(windows)] + { + // SAFETY: these are the same trusted package bytes validated and + // materialized by the artifact installer. V8 has no Sys mmap loader. + unsafe { Module::deserialize_from_file(engine, path) } + .with_context(|| format!("deserialize Wasmer V8 artifact {}", path.display())) + } + #[cfg(not(windows))] + { + deserialize_headless_mmap(engine, path) + } } +#[cfg(not(windows))] #[allow(unsafe_code)] fn deserialize_headless_mmap(engine: &Engine, path: &Path) -> Result { // SAFETY: same artifact ownership and cache-key constraints as the file diff --git a/src/wasix/sdks/rust/src/oliphaunt/postgres_mod.rs b/src/wasix/sdks/rust/src/oliphaunt/postgres_mod.rs index 9b7c15620..e634df795 100644 --- a/src/wasix/sdks/rust/src/oliphaunt/postgres_mod.rs +++ b/src/wasix/sdks/rust/src/oliphaunt/postgres_mod.rs @@ -1207,6 +1207,7 @@ fn build_wasix_runtime( let task_manager = guest_wasm_tasks.apply(task_manager); let mut wasix_runtime = PluggableRuntime::new(task_manager); wasix_runtime.set_engine(engine.clone()); + wasix_runtime.module_cache_only = true; wasix_runtime.set_module_cache(module_cache); Arc::new(wasix_runtime) } diff --git a/src/wasix/sdks/rust/src/oliphaunt/tools.rs b/src/wasix/sdks/rust/src/oliphaunt/tools.rs index d509373af..4d6f4b81f 100644 --- a/src/wasix/sdks/rust/src/oliphaunt/tools.rs +++ b/src/wasix/sdks/rust/src/oliphaunt/tools.rs @@ -603,7 +603,7 @@ where stdin, args, } = invocation; - let engine = aot::headless_engine(); + let engine = aot::headless_engine()?; let module = match assets { // SAFETY: ToolAssets construction requires trusted compiler output. Some(assets) => unsafe { diff --git a/src/wasix/sdks/rust/tools/prepare-rust-release-source.mts b/src/wasix/sdks/rust/tools/prepare-rust-release-source.mts index e7c2cdba3..0d946ec4a 100644 --- a/src/wasix/sdks/rust/tools/prepare-rust-release-source.mts +++ b/src/wasix/sdks/rust/tools/prepare-rust-release-source.mts @@ -1,6 +1,7 @@ #!/usr/bin/env bun import fs from 'node:fs/promises'; import path from 'node:path'; +import { ENGINE_SOURCE_CRATES } from '../../../runtime/engine/contract.mts'; import { canonicalWasixCargoToolchainVersions, validateWasixConsumerDependencyPins, @@ -74,14 +75,26 @@ export function renderOliphauntWasixReleaseCargoToml( return `${before}=${version}${after}`; }); } + for (const key of ENGINE_SOURCE_CRATES) { + text = text.replace( + new RegExp( + '^(' + escapeRegExp(key) + '\\s*=\\s*\\{[^}\\n]*version\\s*=\\s*")[^"]+(".*)$', + 'gmu', + ), + (_, before, after) => before + '=' + runtimeVersion + after, + ); + } return text; } -function validateGeneratedOliphauntWasixReleaseArtifactCoverage(manifestText) { +function validateGeneratedOliphauntWasixReleaseArtifactCoverage(manifestText, runtimeVersion) { if (/=\s*\{[^}\n]*path\s*=/u.test(manifestText)) { fail('generated oliphaunt-wasix release source must not contain local path dependencies'); } - const toolchainVersions = canonicalWasixCargoToolchainVersions(root); + const toolchainVersions = { + ...canonicalWasixCargoToolchainVersions(root), + engine: runtimeVersion, + }; const toolchainFailures = validateWasixConsumerDependencyPins(Bun.TOML.parse(manifestText), { manifestPath: 'generated oliphaunt-wasix release source', toolchainVersions, @@ -108,7 +121,7 @@ export async function prepareOliphauntWasixReleaseSource(version) { if (generatedPackage.version !== version) { fail(`generated oliphaunt-wasix release source must keep SDK version ${version}`); } - validateGeneratedOliphauntWasixReleaseArtifactCoverage(rendered); + validateGeneratedOliphauntWasixReleaseArtifactCoverage(rendered, runtimeVersion); await fs.writeFile(cargoToml, rendered); stageReleaseNotices(stageDir, SOURCE_NOTICE_OPTIONS); assertReleaseNoticesInDirectory(stageDir, SOURCE_NOTICE_OPTIONS); diff --git a/tools/packaging/cargo-source-package.mts b/tools/packaging/cargo-source-package.mts index f8527149a..e365a4d84 100644 --- a/tools/packaging/cargo-source-package.mts +++ b/tools/packaging/cargo-source-package.mts @@ -78,6 +78,16 @@ export function packagedCargoManifestText(source) { text = text .replace(/(\{\s*)path\s*=\s*"[^"]+",\s*/gu, '$1') .replace(/,\s*path\s*=\s*"[^"]+"/gu, ''); + let dependencyTable = false; + text = text + .split('\n') + .filter((line) => { + const section = line.match(/^\s*\[{1,2}([^\]]+)\]{1,2}\s*$/u); + if (section) + dependencyTable = /(?:^|\.)(?:build-|dev-)?dependencies(?:\.|$)/u.test(section[1]); + return !(dependencyTable && /^\s*path\s*=/u.test(line)); + }) + .join('\n'); if (!text.includes('\n[workspace]')) { text = `${text.trimEnd()}\n\n[workspace]\n`; } diff --git a/tools/packaging/cargo-source-package.test.mts b/tools/packaging/cargo-source-package.test.mts index 078010e97..a610d617a 100644 --- a/tools/packaging/cargo-source-package.test.mts +++ b/tools/packaging/cargo-source-package.test.mts @@ -20,10 +20,23 @@ import { createDeterministicTar, fitCargoPayloadParts, packageGeneratedCargoSource, + packagedCargoManifestText, } from './cargo-source-package.mts'; import { readPortableArchiveEntries } from './portable-archive.mts'; import { assertReleaseNoticesInArchive } from './release-notices.mts'; +test('published manifests remove dependency paths while retaining library and build paths', () => { + const text = packagedCargoManifestText( + '[package]\nname = "example"\nversion = "1.0.0"\nbuild = "build.rs"\n[lib]\npath = "upstream/src/lib.rs"\n[dependencies.peer]\npackage = "private-peer"\npath = "../peer"\nversion = "=1.0.0"\n[target.\'cfg(windows)\'.build-dependencies]\nengine = { path = "../engine", version = "=1.0.0" }\n[[bin]]\nname = "example-tool"\npath = "src/tool.rs"\n', + ); + const cargo = Bun.TOML.parse(text); + assert.equal(cargo.lib.path, 'upstream/src/lib.rs'); + assert.equal(cargo.package.build, 'build.rs'); + assert.equal(cargo.bin[0].path, 'src/tool.rs'); + assert.equal(cargo.dependencies.peer.path, undefined); + assert.equal(cargo.target['cfg(windows)']['build-dependencies'].engine.path, undefined); +}); + function fixture(t, name) { const root = mkdtempSync(path.join(os.tmpdir(), `oliphaunt-${name}-`)); t.after(() => rmSync(root, { recursive: true, force: true })); diff --git a/tools/packaging/release-notices.mts b/tools/packaging/release-notices.mts index a59a9c33e..41de1df7a 100644 --- a/tools/packaging/release-notices.mts +++ b/tools/packaging/release-notices.mts @@ -21,7 +21,13 @@ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..') const PREFIX = 'release-notices.mts'; export const RELEASE_NOTICE_PRODUCTS = Object.freeze(['native', 'wasix']); -export const RELEASE_LICENSE_COMPONENTS = Object.freeze(['postgresql', 'icu', 'openssl']); +export const RELEASE_LICENSE_COMPONENTS = Object.freeze([ + 'postgresql', + 'icu', + 'openssl', + 'wasmer', + 'v8', +]); export const RELEASE_CARRIER_PROFILES = Object.freeze({ 'source-sdk': Object.freeze({ products: Object.freeze([]), components: Object.freeze([]) }), 'code-facade': Object.freeze({ products: Object.freeze([]), components: Object.freeze([]) }), @@ -31,7 +37,19 @@ export const RELEASE_CARRIER_PROFILES = Object.freeze({ }), 'wasix-napi-addon': Object.freeze({ products: Object.freeze(['wasix']), - components: Object.freeze(['postgresql', 'icu', 'openssl']), + components: Object.freeze(['postgresql', 'icu', 'openssl', 'wasmer']), + }), + 'wasix-engine-source': Object.freeze({ + products: Object.freeze([]), + components: Object.freeze(['wasmer']), + }), + 'wasix-engine-windows': Object.freeze({ + products: Object.freeze([]), + components: Object.freeze(['v8']), + }), + 'wasix-napi-addon-windows': Object.freeze({ + products: Object.freeze(['wasix']), + components: Object.freeze(['postgresql', 'icu', 'openssl', 'wasmer', 'v8']), }), broker: Object.freeze({ products: Object.freeze([]), components: Object.freeze([]) }), 'native-runtime': Object.freeze({ @@ -113,6 +131,36 @@ const PRODUCT_NOTICE_ROWS = Object.freeze({ }); const LICENSE_COMPONENT_ROWS = Object.freeze({ + wasmer: Object.freeze({ + id: 'wasmer', + spdx: 'MIT', + name: 'Wasmer / WASIX License', + member: 'THIRD_PARTY_LICENSES/Wasmer-LICENSE', + source: path.join(ROOT, 'src/wasix/runtime/engine/licenses/Wasmer-LICENSE'), + sourceManifest: path.join(ROOT, 'src/wasix/runtime/engine/source.toml'), + archiveSourceKey: 'wasmer', + sourceVersion: '7.5.0', + sha256: '76dc7d305458d07478bc62669fe53dbfd3b94b95c5e00fbb45af1f492cbd7284', + sourceUrl: 'https://static.crates.io/crates/wasmer/wasmer-7.5.0.crate', + sourceIdentity: 'sha256:ddfde3972691f1082774c71c42e21add1d1838cb6482a6921a0ba8a993e0046a', + licenseUrl: + 'https://github.com/wasmerio/wasmer/blob/82ff099e082da586b38af79986abbb8b677f8baf/LICENSE', + }), + v8: Object.freeze({ + id: 'v8', + spdx: 'BSD-3-Clause AND BSD-2-Clause AND Apache-2.0 AND Unicode-3.0 AND SunPro AND CC0-1.0 AND Zlib AND MIT AND Python-2.0', + name: 'V8 / wee8 Windows runtime licenses', + member: 'THIRD_PARTY_LICENSES/V8-LICENSES.txt', + source: path.join(ROOT, 'src/wasix/runtime/engine/licenses/V8-LICENSES.txt'), + sourceManifest: path.join(ROOT, 'src/wasix/runtime/engine/source.toml'), + archiveSourceKey: 'wee8-windows', + sourceVersion: '11.9.7', + sha256: '31bb1be82772ab89d94bbd99dbc6f4a804f6b94533f27903237cee5987da7829', + sourceUrl: + 'https://github.com/wasmerio/wee8-custom-builds/releases/download/11.9.7/v8-windows-amd64.tar.xz', + sourceIdentity: 'sha256:2aee8b6c3e8cecae2ce0325ac01b9bcaea4bef49e8f2aac599e1729d60c17285', + licenseUrl: 'https://github.com/v8/v8/blob/b0a55a7dad7f536cce1f9aaddba89894c8533946/LICENSE', + }), postgresql: Object.freeze({ id: 'postgresql', spdx: 'PostgreSQL', @@ -269,16 +317,14 @@ function validateRuntimeLicenseSource(row) { `runtime license source manifest ${row.sourceManifest} cannot be parsed: ${cause.message}`, ); } - if (row.id === 'postgresql') { - const source = manifest?.postgresql; + if (row.id === 'postgresql' || row.archiveSourceKey) { + const source = manifest?.[row.archiveSourceKey ?? 'postgresql']; if ( source?.version !== row.sourceVersion || source?.url !== row.sourceUrl || `sha256:${source?.sha256}` !== row.sourceIdentity ) { - throw new Error( - `PostgreSQL runtime license snapshot no longer matches ${row.sourceManifest}`, - ); + throw new Error(`${row.id} runtime license snapshot no longer matches ${row.sourceManifest}`); } } else { if ( @@ -471,7 +517,7 @@ export function releasePackageLicense({ components = [], includeOliphaunt = true } entries.push(...releaseLicenseComponents(components)); return Object.freeze({ - spdx: entries.map((entry) => entry.spdx).join(' AND '), + spdx: [...new Set(entries.flatMap((entry) => entry.spdx.split(' AND ')))].join(' AND '), entries: Object.freeze(entries), }); } diff --git a/tools/release/download-wasix-runtime-build-artifacts.test.mts b/tools/release/download-wasix-runtime-build-artifacts.test.mts index 6961aa6ab..d694aa2e4 100644 --- a/tools/release/download-wasix-runtime-build-artifacts.test.mts +++ b/tools/release/download-wasix-runtime-build-artifacts.test.mts @@ -15,10 +15,11 @@ if (mode === 'targets') { assert.match(commands, /import-download/); } else { const sha = 'b'.repeat(40); - assert.equal(commands.split(`CI\n${sha}\n`).length - 1, count); + assert.equal(commands.split(`CI\n${sha}\n`).length - 1, count + 1); assert.ok(!commands.includes('a'.repeat(40))); const run = mode === 'selected' ? '777' : '30358387218'; - assert.equal(commands.split(`--run-id\n${run}\n--job\nBuilds\n`).length - 1, count); + assert.equal(commands.split(`--run-id\n${run}\n--job\nBuilds\n`).length - 1, count + 1); + assert.ok(commands.includes('--artifact\nliboliphaunt-wasix-windows-engine\n')); if (mode === 'selected') assert.ok(commands.includes(`--commit\n${sha}\n--status\nsuccess`)); const install = commands.slice(commands.indexOf('import-download\n')); for (const [id, triple] of Object.entries(AOT_TARGET_TRIPLES)) { From 71cd603782bfe97f0b12d00697221abff1a40ca9 Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Tue, 6 Oct 2026 05:13:58 +0000 Subject: [PATCH 02/22] fix(release): plan embedded engine payloads before source acquisition --- .../prepare-release-candidate.test.mts | 44 ++++++++++++++++++- tools/release/release-graph.mts | 9 ++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/tools/release/prepare-release-candidate.test.mts b/tools/release/prepare-release-candidate.test.mts index 1f4b80459..7a0d33f29 100644 --- a/tools/release/prepare-release-candidate.test.mts +++ b/tools/release/prepare-release-candidate.test.mts @@ -156,7 +156,12 @@ test('Cargo owns transitive, inherited, renamed, build, target and custom source }); test('embedded generated payloads follow private producer ownership for source-pin changes', () => { - for (const file of ['src/third-party/postgres/source.toml', 'src/third-party/icu/source.toml']) { + for (const file of [ + 'src/third-party/postgres/source.toml', + 'src/third-party/icu/source.toml', + 'src/wasix/runtime/engine/source.toml', + 'src/wasix/runtime/engine/patches/wasmer/series', + ]) { expect(existsSync(path.join(ROOT, file))).toBe(true); const plan = buildPlan(graph, [file]); expect(plan.releaseProducts).toContain('liboliphaunt-wasix'); @@ -164,6 +169,43 @@ test('embedded generated payloads follow private producer ownership for source-p } }); +test('release planning does not require generated sources owned by an embedded payload', () => { + const scratch = mkdtempSync(path.join(ROOT, 'target/embedded-payload-')); + const relative = path.relative(ROOT, scratch).split(path.sep).join('/'); + try { + mkdirSync(path.join(scratch, 'consumer/src'), { recursive: true }); + mkdirSync(path.join(scratch, 'producer'), { recursive: true }); + writeFileSync( + path.join(scratch, 'Cargo.toml'), + '[workspace]\nmembers = ["consumer", "producer"]\n', + ); + writeFileSync(path.join(scratch, 'consumer/src/lib.rs'), 'pub fn consumer() {}\n'); + writeFileSync( + path.join(scratch, 'consumer/Cargo.toml'), + '[package]\nname = "consumer"\nversion = "1.0.0"\n[dependencies]\nproducer = { path = "../producer" }\n', + ); + writeFileSync( + path.join(scratch, 'producer/Cargo.toml'), + '[package]\nname = "producer"\nversion = "1.0.0"\n[lib]\npath = "upstream/src/lib.rs"\n', + ); + const impacts = declaredSharedSourceImpacts({ + 'oliphaunt-wasix-napi': { + embedded_cargo_manifests: [`${relative}/consumer/Cargo.toml`], + embedded_payload_products: ['liboliphaunt-wasix'], + }, + 'liboliphaunt-wasix': { path: `${relative}/producer` }, + }); + expect(impacts.some((row) => row.source_paths.includes(`${relative}/consumer/src`))).toBe(true); + expect(impacts.some((row) => row.source_paths.some((file) => file.includes('/upstream')))).toBe( + false, + ); + expect(existsSync(path.join(scratch, 'producer/upstream'))).toBe(false); + expect(existsSync(path.join(scratch, 'Cargo.lock'))).toBe(false); + } finally { + rmSync(scratch, { recursive: true, force: true }); + } +}); + async function generate(commits, baselines = { [native]: first, [wasix]: first }) { useSourceDate('2026-09-11'); const config = Object.fromEntries( diff --git a/tools/release/release-graph.mts b/tools/release/release-graph.mts index 9cabfaa15..ac9e1986b 100644 --- a/tools/release/release-graph.mts +++ b/tools/release/release-graph.mts @@ -426,6 +426,15 @@ export function declaredSharedSourceImpacts(products, prefix = 'release-graph') const visit = (manifestPath) => { if (visited.has(manifestPath)) return; visited.add(manifestPath); + // Independently owned payloads already propagate through + // embedded_payload_products. Plan from their checked-in producer inputs, + // rather than requiring generated Cargo sources before acquisition. + if ( + (config.embedded_payload_products ?? []).some((owner) => + manifestPath.startsWith(`${products[owner]?.path}/`), + ) + ) + return; const directory = path.posix.dirname(manifestPath); const pkg = cargoPackage(manifestPath); // Local runtime and build dependencies become part of the shipped binary. From 6566e2d74b2ff1e3de0f789e29d896b5432b4e74 Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Tue, 6 Oct 2026 05:18:49 +0000 Subject: [PATCH 03/22] fix(ci): qualify Windows engine contracts before cache production --- .github/workflows/wasix-host.yml | 6 +++-- tools/packaging/cargo-source-package.test.mts | 24 +++++++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/.github/workflows/wasix-host.yml b/.github/workflows/wasix-host.yml index 332e046a8..87f4a4153 100644 --- a/.github/workflows/wasix-host.yml +++ b/.github/workflows/wasix-host.yml @@ -44,9 +44,10 @@ jobs: - uses: ./.github/actions/setup-rust with: cache-save-if: ${{ inputs.cache-save-if }} + tools: cargo-nextest@0.9.137 - uses: ./.github/actions/setup-msvc - - name: Produce pinned Windows engine - run: moon run liboliphaunt-wasix:engine-build + - name: Produce and test pinned Windows engine + run: moon run liboliphaunt-wasix:engine-test - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with: name: liboliphaunt-wasix-windows-engine @@ -136,6 +137,7 @@ jobs: uses: ./.github/actions/setup-rust with: cache-save-if: ${{ inputs.cache-save-if }} + tools: ${{ runner.os == 'Windows' && 'cargo-nextest@0.9.137' || '' }} - name: Set up MSVC if: ${{ runner.os == 'Windows' }} diff --git a/tools/packaging/cargo-source-package.test.mts b/tools/packaging/cargo-source-package.test.mts index a610d617a..3ce75ef96 100644 --- a/tools/packaging/cargo-source-package.test.mts +++ b/tools/packaging/cargo-source-package.test.mts @@ -25,18 +25,6 @@ import { import { readPortableArchiveEntries } from './portable-archive.mts'; import { assertReleaseNoticesInArchive } from './release-notices.mts'; -test('published manifests remove dependency paths while retaining library and build paths', () => { - const text = packagedCargoManifestText( - '[package]\nname = "example"\nversion = "1.0.0"\nbuild = "build.rs"\n[lib]\npath = "upstream/src/lib.rs"\n[dependencies.peer]\npackage = "private-peer"\npath = "../peer"\nversion = "=1.0.0"\n[target.\'cfg(windows)\'.build-dependencies]\nengine = { path = "../engine", version = "=1.0.0" }\n[[bin]]\nname = "example-tool"\npath = "src/tool.rs"\n', - ); - const cargo = Bun.TOML.parse(text); - assert.equal(cargo.lib.path, 'upstream/src/lib.rs'); - assert.equal(cargo.package.build, 'build.rs'); - assert.equal(cargo.bin[0].path, 'src/tool.rs'); - assert.equal(cargo.dependencies.peer.path, undefined); - assert.equal(cargo.target['cfg(windows)']['build-dependencies'].engine.path, undefined); -}); - function fixture(t, name) { const root = mkdtempSync(path.join(os.tmpdir(), `oliphaunt-${name}-`)); t.after(() => rmSync(root, { recursive: true, force: true })); @@ -135,6 +123,18 @@ if (['prepare', 'verify'].includes(process.argv[2])) { process.exit(0); } +test('published manifests remove dependency paths while retaining library and build paths', () => { + const text = packagedCargoManifestText( + '[package]\nname = "example"\nversion = "1.0.0"\nbuild = "build.rs"\n[lib]\npath = "upstream/src/lib.rs"\n[dependencies.peer]\npackage = "private-peer"\npath = "../peer"\nversion = "=1.0.0"\n[target.\'cfg(windows)\'.build-dependencies]\nengine = { path = "../engine", version = "=1.0.0" }\n[[bin]]\nname = "example-tool"\npath = "src/tool.rs"\n', + ); + const cargo = Bun.TOML.parse(text); + assert.equal(cargo.lib.path, 'upstream/src/lib.rs'); + assert.equal(cargo.package.build, 'build.rs'); + assert.equal(cargo.bin[0].path, 'src/tool.rs'); + assert.equal(cargo.dependencies.peer.path, undefined); + assert.equal(cargo.target['cfg(windows)']['build-dependencies'].engine.path, undefined); +}); + test('payload splitting follows compressed crate size and preserves every byte', (t) => { const { root } = fixture(t, 'compressed-parts'); for (const payload of [Buffer.alloc(20 * 1024 * 1024, 42), randomBytes(20 * 1024 * 1024)]) { From eab3c424da804a86f4cd5592a10b1812664a275d Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Tue, 6 Oct 2026 05:27:52 +0000 Subject: [PATCH 04/22] test(wasix): qualify the frozen Windows engine package closure --- .../runtime/engine/licenses/V8-LICENSES.txt | 37 ++++++++ src/wasix/runtime/engine/package.test.mts | 89 ++++++++++++++++++- src/wasix/runtime/moon.yml | 1 + tools/packaging/release-notices.mts | 2 +- 4 files changed, 126 insertions(+), 3 deletions(-) diff --git a/src/wasix/runtime/engine/licenses/V8-LICENSES.txt b/src/wasix/runtime/engine/licenses/V8-LICENSES.txt index 0ff2be227..13095d2eb 100644 --- a/src/wasix/runtime/engine/licenses/V8-LICENSES.txt +++ b/src/wasix/runtime/engine/licenses/V8-LICENSES.txt @@ -2114,3 +2114,40 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of 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. + +--- zlib/google/compression_utils_portable.cc --- +Source: https://chromium.googlesource.com/chromium/src/third_party/zlib/+/788cb3c270e8700b425c7bdca1f9ce6b0c1400a9/google/compression_utils_portable.cc?format=TEXT +SHA-256: 5213dd083af3d7099251413e0fe8988ffe998b0dbee68722899910c480aea933 + +Copyright 2019 The Chromium Authors + +License source: https://raw.githubusercontent.com/chromium/chromium/8d621d9143e4526d70d688abf40651c149908708/LICENSE +License SHA-256: 368cca1106be99d39ecd32a38d8305585d802a475effb66380b91ffc9bcf709b + +// Copyright 2015 The Chromium Authors +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google LLC nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT +// OWNER 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. diff --git a/src/wasix/runtime/engine/package.test.mts b/src/wasix/runtime/engine/package.test.mts index 2d5ac6618..8bea8567c 100644 --- a/src/wasix/runtime/engine/package.test.mts +++ b/src/wasix/runtime/engine/package.test.mts @@ -116,9 +116,23 @@ test('freezes a closed engine family, rejects mixed inputs, and reconstructs eve .filter((row) => partNames.includes(row.name)) .map((row) => row.cratePath), }); + const env = { ...process.env, CARGO_TARGET_DIR: path.join(consumer, 'target') }; + const fetched = Bun.spawnSync( + ['cargo', '--config', 'net.offline=false', 'fetch', '--manifest-path', manifest], + { cwd: consumer, env, timeout: 300_000 }, + ); + assert.equal(fetched.exitCode, 0, fetched.stderr.toString()); const result = Bun.spawnSync( - ['cargo', 'build', '--manifest-path', manifest, '--message-format=json'], - { cwd: consumer, env: { ...process.env, CARGO_TARGET_DIR: path.join(consumer, 'target') } }, + [ + 'cargo', + 'build', + '--manifest-path', + manifest, + '--locked', + '--offline', + '--message-format=json', + ], + { cwd: consumer, env, timeout: 300_000 }, ); assert.equal(result.exitCode, 0, result.stderr.toString()); const build = result.stdout @@ -134,3 +148,74 @@ test('freezes a closed engine family, rejects mixed inputs, and reconstructs eve assert.ok(build, 'the extracted carrier must reconstruct its own DLL'); assert.deepEqual(readFileSync(path.join(build.out_dir, 'oliphaunt_wee8.dll')), dll); }); + +test('qualified Windows engine runs from the frozen Cargo package closure', { + skip: process.platform !== 'win32', + timeout: 1_800_000, +}, (t) => { + const root = mkdtempSync(path.join(os.tmpdir(), 'oliphaunt-installed-engine-')); + t.after(() => rmSync(root, { recursive: true, force: true })); + const version = Bun.TOML.parse( + readFileSync(path.join(import.meta.dir, 'crates/wasmer/Cargo.toml'), 'utf8'), + ).package.version; + const packages = packageEngine({ + sourceRoot: path.join(root, 'source'), + outputDir: path.join(root, 'packages'), + version, + }); + const consumer = path.join(root, 'consumer'); + const wasmer = packages.find((row) => row.name === 'oliphaunt-wasmer'); + const manifest = preparePackagedCargoTestClosure({ + cratePath: wasmer.cratePath, + scratch: consumer, + dependencyCrates: packages.filter((row) => row !== wasmer).map((row) => row.cratePath), + }); + function run(command: string[]) { + const result = Bun.spawnSync(command, { + cwd: consumer, + env: { ...process.env, CARGO_TARGET_DIR: path.resolve('target') }, + stdout: 'inherit', + stderr: 'inherit', + timeout: 1_200_000, + }); + assert.equal(result.exitCode, 0, `installed engine: ${command.join(' ')}`); + } + // Only the extracted archives supply private dependencies. No workspace + // producer, source archive, native library, or compiler build is consulted. + run(['cargo', '--config', 'net.offline=false', 'fetch', '--manifest-path', manifest]); + run([ + 'cargo', + 'nextest', + 'run', + '--manifest-path', + manifest, + '--locked', + '--offline', + '--no-default-features', + '--features', + 'v8', + '--lib', + '-E', + 'test(oliphaunt_contract)', + '--no-tests=fail', + '--test-threads=1', + ]); + const wasix = path.join( + consumer, + 'dependencies', + `oliphaunt-wasmer-wasix-${version}`, + 'Cargo.toml', + ); + run(['cargo', '--config', 'net.offline=false', 'fetch', '--manifest-path', wasix]); + run([ + 'cargo', + 'check', + '--manifest-path', + wasix, + '--locked', + '--offline', + '--no-default-features', + '--features', + 'sys-minimal,sys-poll,host-vnet,time,v8,wasmer/headless', + ]); +}); diff --git a/src/wasix/runtime/moon.yml b/src/wasix/runtime/moon.yml index b6241e19a..a51e7c16e 100644 --- a/src/wasix/runtime/moon.yml +++ b/src/wasix/runtime/moon.yml @@ -100,6 +100,7 @@ tasks: case "$(uname -s)" in MINGW*|MSYS*|CYGWIN*) cargo nextest run -p oliphaunt-wasmer --locked --profile ci --no-default-features --features v8 --lib -E 'test(oliphaunt_contract)' --no-tests=fail --test-threads=1 + bun test ./src/wasix/runtime/engine/package.test.mts -t 'qualified Windows engine' ;; esac inputs: ["@group(engine-source)", "@group(cargo-workspace)", "/.config/nextest.toml"] diff --git a/tools/packaging/release-notices.mts b/tools/packaging/release-notices.mts index 41de1df7a..a1f4a5399 100644 --- a/tools/packaging/release-notices.mts +++ b/tools/packaging/release-notices.mts @@ -155,7 +155,7 @@ const LICENSE_COMPONENT_ROWS = Object.freeze({ sourceManifest: path.join(ROOT, 'src/wasix/runtime/engine/source.toml'), archiveSourceKey: 'wee8-windows', sourceVersion: '11.9.7', - sha256: '31bb1be82772ab89d94bbd99dbc6f4a804f6b94533f27903237cee5987da7829', + sha256: '11b776696544c9ed9d11dc02b0f929cee703cfdb68d4895aaf2af1eb78e0116b', sourceUrl: 'https://github.com/wasmerio/wee8-custom-builds/releases/download/11.9.7/v8-windows-amd64.tar.xz', sourceIdentity: 'sha256:2aee8b6c3e8cecae2ce0325ac01b9bcaea4bef49e8f2aac599e1729d60c17285', From 489fc83235ca7317e7435547a7b6b3cb58a911bd Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Tue, 6 Oct 2026 05:31:40 +0000 Subject: [PATCH 05/22] fix(wasix): remove LLVM inputs from Windows runtime qualification --- tools/release/artifact-target-matrix.mts | 3 +++ tools/release/check_artifact_targets.mts | 7 +++++++ tools/release/release-artifact-targets.mts | 10 +++++++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/tools/release/artifact-target-matrix.mts b/tools/release/artifact-target-matrix.mts index 7e9dc33a7..ffda2f2d8 100644 --- a/tools/release/artifact-target-matrix.mts +++ b/tools/release/artifact-target-matrix.mts @@ -296,6 +296,9 @@ function wasixHostTargetMatrixRow(target) { if (!target.triple) { fail(PREFIX, `${target.id} must declare triple`); } + if (target.kind === 'wasix-aot-runtime' && target.target === 'windows-x64-msvc') { + return { os: target.runner, target: target.triple, target_id: target.target }; + } if (!target.llvmUrl) { fail(PREFIX, `${target.id} must declare llvm_url`); } diff --git a/tools/release/check_artifact_targets.mts b/tools/release/check_artifact_targets.mts index c3bb39123..95bfeb8ca 100644 --- a/tools/release/check_artifact_targets.mts +++ b/tools/release/check_artifact_targets.mts @@ -210,6 +210,13 @@ export function validateMatrixCoverage(targets, extensions, matrices) { for (const row of matrices.wasixAot.include) { const target = wasixAotTargets.get(row.target_id); invariant(target !== undefined, `WASIX AOT CI matrix has unknown target ${row.target_id}`); + if (row.target_id === 'windows-x64-msvc') { + invariant( + row.llvm_url === undefined && row.llvm_sha256 === undefined && row.llvm_bytes === undefined, + 'Windows WASIX AOT uses the owned V8 producer', + ); + continue; + } invariant( row.llvm_url === target.llvmUrl, `WASIX AOT CI matrix ${row.target_id} must bind its declared LLVM URL`, diff --git a/tools/release/release-artifact-targets.mts b/tools/release/release-artifact-targets.mts index ba26a83e0..083f281c7 100644 --- a/tools/release/release-artifact-targets.mts +++ b/tools/release/release-artifact-targets.mts @@ -541,9 +541,13 @@ function liboliphauntWasixRows(prefix) { target, triple: platform.triple, runner: platform.runner, - llvm_url: platform.wasixLlvmUrl, - llvm_sha256: platform.wasixLlvmSha256, - llvm_bytes: platform.wasixLlvmBytes, + ...(target === 'windows-x64-msvc' + ? {} + : { + llvm_url: platform.wasixLlvmUrl, + llvm_sha256: platform.wasixLlvmSha256, + llvm_bytes: platform.wasixLlvmBytes, + }), asset: `liboliphaunt-wasix-{version}-runtime-aot-${target}.tar.zst`, surfaces: ['github-release'], _source_file: 'Moon release metadata', From 706039004d100cb9276b2fe356bca7bc74092e14 Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Tue, 6 Oct 2026 05:56:36 +0000 Subject: [PATCH 06/22] fix(packaging): close transitive path dependency test sources --- .../packaging/cargo-package-test-closure.mts | 51 ++++++++++--- .../cargo-package-test-closure.test.mts | 76 ++++++++++++++++++- 2 files changed, 115 insertions(+), 12 deletions(-) diff --git a/tools/packaging/cargo-package-test-closure.mts b/tools/packaging/cargo-package-test-closure.mts index 67d1349b3..48bcdc051 100644 --- a/tools/packaging/cargo-package-test-closure.mts +++ b/tools/packaging/cargo-package-test-closure.mts @@ -13,6 +13,7 @@ import { import path from 'node:path'; import { extractPortableArchiveTree, readPortableArchiveEntries } from './portable-archive.mts'; +import { packagedCargoManifestText } from './cargo-source-package.mts'; const TOOL = 'cargo-package-test-closure.mts'; const CARGO_PACKAGE_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]*$/u; @@ -172,6 +173,44 @@ function pathDependencyPatches(manifests, scratch, packagedManifests, packagedNa const patches = new Map(); const sourceDirectories = new Map(); const packagedDependencies = packagedManifests.flatMap(dependencyRows); + function stageSource(directory, name) { + if (packagedNames.has(name)) return; + const realSource = realpathSync(directory); + const previousSource = sourceDirectories.get(name); + if (previousSource !== undefined) { + if (previousSource !== realSource) { + throw error( + `${name} has conflicting path-dependency sources: ${previousSource} and ${realSource}`, + ); + } + return; + } + const file = path.join(realSource, 'Cargo.toml'); + const manifest = parseManifest(file, `local patch for ${name}`); + const identity = packageIdentity(manifest, file); + if (identity.name !== name) { + throw error(`local patch ${file} does not declare ${name}`); + } + if ( + packagedDependencies.some( + (row) => row.name === name && exactVersion(row.version, name) !== identity.version, + ) + ) + return; + sourceDirectories.set(name, realSource); + const staged = path.join(scratch, 'path-dependency-sources', name); + copyCleanDependencySource(realSource, staged); + writeFileSync( + path.join(staged, 'Cargo.toml'), + packagedCargoManifestText(readFileSync(file, 'utf8')), + ); + addPatch(patches, name, staged, file); + // Cargo resolves the entire manifest closure, including inactive target and + // optional build dependencies. Preserve that closure without sibling paths. + for (const dependency of dependencyRows(manifest).filter((row) => row.path !== null)) { + stageSource(path.resolve(realSource, dependency.path), dependency.name); + } + } for (const manifestFile of manifests) { const resolvedManifest = path.resolve(manifestFile); const manifest = parseManifest(resolvedManifest, 'path-dependency source manifest'); @@ -200,17 +239,7 @@ function pathDependencyPatches(manifests, scratch, packagedManifests, packagedNa // source hint is usable only for that version; otherwise Cargo fetches // the declared published dependency instead of substituting workspace code. if (!packagedVersions.has(local.version)) continue; - const realSource = realpathSync(directory); - const previousSource = sourceDirectories.get(dependency.name); - if (previousSource !== undefined && previousSource !== realSource) { - throw error( - `${dependency.name} has conflicting path-dependency sources: ${previousSource} and ${realSource}`, - ); - } - sourceDirectories.set(dependency.name, realSource); - const staged = path.join(scratch, 'path-dependency-sources', dependency.name); - if (!patches.has(dependency.name)) copyCleanDependencySource(directory, staged); - addPatch(patches, dependency.name, staged, resolvedManifest); + stageSource(directory, dependency.name); } } return patches; diff --git a/tools/packaging/cargo-package-test-closure.test.mts b/tools/packaging/cargo-package-test-closure.test.mts index 5f91c5b9a..453d0f9cf 100644 --- a/tools/packaging/cargo-package-test-closure.test.mts +++ b/tools/packaging/cargo-package-test-closure.test.mts @@ -1,5 +1,12 @@ import assert from 'node:assert/strict'; -import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { + appendFileSync, + mkdtempSync, + mkdirSync, + readFileSync, + rmSync, + writeFileSync, +} from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import test from 'node:test'; @@ -140,6 +147,73 @@ test('package qualification retains exact published pins after a local dependenc } }); +test('stages sibling, optional build, and cyclic path dependencies as a closed Cargo graph', (t) => { + const root = fixture(t, 'cargo-closure-transitive'); + const cratePath = closureCrate(root); + const source = path.join(root, 'engine'); + for (const [directory, name, dependencies] of [ + [ + 'carrier', + 'carrier', + '[features]\nneeded = []\n[dependencies]\npeer = { package = "renamed-peer", version = "*", path = "../peer" }\n', + ], + [ + 'peer', + 'renamed-peer', + '[features]\nunused = ["dep:build-peer"]\n[build-dependencies]\nbuild-peer = { version = "*", path = "../build-peer", optional = true }\n', + ], + [ + 'build-peer', + 'build-peer', + '[dependencies]\ncarrier = { version = "*", path = "../carrier", optional = true }\n', + ], + ]) { + const directoryPath = path.join(source, directory); + writePackage(directoryPath, name); + appendFileSync(path.join(directoryPath, 'Cargo.toml'), dependencies); + } + const hint = path.join(root, 'Cargo.toml'); + writeFileSync(hint, '[dependencies]\ncarrier = { version = "*", path = "engine/carrier" }\n'); + const scratch = path.join(root, 'consumer'); + const manifest = preparePackagedCargoTestClosure({ + cratePath, + scratch, + pathDependencyManifests: [hint], + }); + rmSync(source, { recursive: true }); + const result = Bun.spawnSync( + [ + 'cargo', + 'metadata', + '--offline', + '--features', + 'carrier', + '--format-version', + '1', + '--manifest-path', + manifest, + ], + { cwd: scratch }, + ); + assert.equal(result.exitCode, 0, result.stderr.toString()); + const packages = JSON.parse(result.stdout.toString()).packages; + assert.deepEqual(packages.map((row) => row.name).sort(), [ + 'carrier', + 'closure-fixture', + 'renamed-peer', + ]); + for (const row of packages) { + assert.ok(row.manifest_path.startsWith(scratch)); + assert.ok(row.dependencies.every((dependency) => !dependency.path)); + } + const config = Bun.TOML.parse(readFileSync(path.join(scratch, '.cargo/config.toml'), 'utf8')); + assert.deepEqual(Object.keys(config.patch['crates-io']), [ + 'build-peer', + 'carrier', + 'renamed-peer', + ]); +}); + test('rejects unsafe packaged names', (t) => { const root = fixture(t, 'cargo-closure-unsafe'); const stage = path.join(root, 'stage'); From 9047556a4d4c4d71f3daa3f27cbb0dacb5a1d05b Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Tue, 6 Oct 2026 05:56:37 +0000 Subject: [PATCH 07/22] test(wasix): run installed Windows SDK against frozen AOT carriers --- src/wasix/runtime/engine/windows/kernel32.def | 18 --- ...age_liboliphaunt_wasix_cargo_artifacts.mts | 2 +- src/wasix/sdks/rust/moon.yml | 5 + src/wasix/sdks/rust/tools/test-aot.sh | 4 + .../sdks/rust/tools/test-packaged-aot.mts | 110 ++++++++++++++++++ 5 files changed, 120 insertions(+), 19 deletions(-) delete mode 100644 src/wasix/runtime/engine/windows/kernel32.def create mode 100644 src/wasix/sdks/rust/tools/test-packaged-aot.mts diff --git a/src/wasix/runtime/engine/windows/kernel32.def b/src/wasix/runtime/engine/windows/kernel32.def deleted file mode 100644 index f75cc17ae..000000000 --- a/src/wasix/runtime/engine/windows/kernel32.def +++ /dev/null @@ -1,18 +0,0 @@ -LIBRARY kernel32.dll -EXPORTS -GetStdHandle -CreateFileW -ReadFile -WriteFile -GetFileSizeEx -CloseHandle -GetCommandLineW -ExitProcess -GetCurrentProcess -TerminateProcess -Sleep -GetTickCount64 -GetModuleHandleW -GetProcAddress -CreateThread -WaitForSingleObject diff --git a/src/wasix/runtime/tools/package_liboliphaunt_wasix_cargo_artifacts.mts b/src/wasix/runtime/tools/package_liboliphaunt_wasix_cargo_artifacts.mts index 3eb26bc45..5ea1a4432 100755 --- a/src/wasix/runtime/tools/package_liboliphaunt_wasix_cargo_artifacts.mts +++ b/src/wasix/runtime/tools/package_liboliphaunt_wasix_cargo_artifacts.mts @@ -407,7 +407,7 @@ function rewriteRuntimeCoreManifest(root) { writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`); } -function runtimeCorePayload(runtimeRoot, extractRoot) { +export function runtimeCorePayload(runtimeRoot, extractRoot) { const coreRoot = path.join(extractRoot, 'runtime-core-payload'); rmSync(coreRoot, { recursive: true, force: true }); cpSync(runtimeRoot, coreRoot, { recursive: true }); diff --git a/src/wasix/sdks/rust/moon.yml b/src/wasix/sdks/rust/moon.yml index 528d82ccb..a898e4c79 100644 --- a/src/wasix/sdks/rust/moon.yml +++ b/src/wasix/sdks/rust/moon.yml @@ -84,6 +84,10 @@ tasks: deps: - target: cargo-sources cacheStrategy: hash + - target: "oliphaunt-wasix-rust:package" + cacheStrategy: ignored + - target: "oliphaunt-query:package" + cacheStrategy: ignored - "liboliphaunt-wasix:runtime-aot" - "extension-artifacts-wasix:build-aot" - "postgres-tools-wasix:build-aot" @@ -92,6 +96,7 @@ tasks: - "@group(cargo-workspace)" - "@group(rust-test-config)" - "@group(legal-files)" + - "@group(packaging-source)" - project: "liboliphaunt-wasix" group: "extension-smoke-inputs" - project: "liboliphaunt-wasix" diff --git a/src/wasix/sdks/rust/tools/test-aot.sh b/src/wasix/sdks/rust/tools/test-aot.sh index 8636d7184..0bd3809e0 100644 --- a/src/wasix/sdks/rust/tools/test-aot.sh +++ b/src/wasix/sdks/rust/tools/test-aot.sh @@ -28,6 +28,10 @@ fi # producer tasks only serialize/package/check payload bytes. bash src/wasix/runtime/tools/runtime-smoke.sh core-smoke +if [ "$host" = x86_64-pc-windows-msvc ]; then + tools/dev/bun.sh src/wasix/sdks/rust/tools/test-packaged-aot.mts +fi + # The portable/Linux regression exercises every catalogued extension. Each host # must also deserialize and execute machine code produced for that exact host, # including a side module and the split pg_dump/psql tool artifacts. Keep this diff --git a/src/wasix/sdks/rust/tools/test-packaged-aot.mts b/src/wasix/sdks/rust/tools/test-packaged-aot.mts new file mode 100644 index 000000000..52484c8be --- /dev/null +++ b/src/wasix/sdks/rust/tools/test-packaged-aot.mts @@ -0,0 +1,110 @@ +import assert from 'node:assert/strict'; +import { mkdirSync, mkdtempSync, rmSync } from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { preparePackagedCargoTestClosure } from '../../../../../tools/packaging/cargo-package-test-closure.mts'; +import { packageSpec } from '../../../../../tools/packaging/wasix-cargo-payload.mts'; +import { + currentProductVersionSync, + ROOT, +} from '../../../../../tools/release/release-artifact-targets.mts'; +import { packageEngine } from '../../../runtime/engine/package.mts'; +import { + postgresSourceFingerprint, + stageAotAssets, + stagePortableAssets, +} from '../../../runtime/tools/package-release-assets.mts'; +import { runtimeCorePayload } from '../../../runtime/tools/package_liboliphaunt_wasix_cargo_artifacts.mts'; +import { + AOT_PACKAGES, + AOT_TARGET_TRIPLES, + RUNTIME_PACKAGE, +} from '../../../runtime/tools/wasix-cargo-artifact-contract.mts'; + +assert.equal(process.platform, 'win32'); +const targetId = 'windows-x64-msvc'; +const target = AOT_TARGET_TRIPLES[targetId]; +const version = currentProductVersionSync('liboliphaunt-wasix'); +const scratch = mkdtempSync(path.join(os.tmpdir(), 'oliphaunt-wasix-installed-aot-')); +try { + const sourceRoot = path.join(scratch, 'source'); + const outputDir = path.join(scratch, 'packages'); + mkdirSync(outputDir); + const fingerprint = postgresSourceFingerprint(); + const portable = path.join(scratch, 'portable'); + const aot = path.join(scratch, 'aot'); + stagePortableAssets(path.join(ROOT, 'target/oliphaunt-wasix/assets'), portable, fingerprint); + stageAotAssets(path.join(ROOT, 'target/oliphaunt-wasix/aot', target), aot, target, fingerprint); + const packages = packageEngine({ sourceRoot, outputDir, version }); + for (const spec of [ + { + name: RUNTIME_PACKAGE, + kind: 'wasix-runtime', + target: 'portable', + templateDir: path.join(ROOT, 'src/wasix/runtime/crates/assets'), + payloadRoot: runtimeCorePayload(portable, scratch), + payloadDirName: 'payload', + }, + { + name: AOT_PACKAGES[targetId], + kind: 'wasix-aot', + target, + templateDir: path.join(ROOT, 'src/wasix/runtime/crates/aot', target), + payloadRoot: aot, + payloadDirName: 'artifacts', + }, + ]) { + packages.push(packageSpec(spec, { version, sourceRoot, outputDir, cargoTargetDir: scratch })); + } + const sdkVersion = currentProductVersionSync('oliphaunt-wasix-rust'); + const queryVersion = currentProductVersionSync('oliphaunt-query'); + const consumer = path.join(scratch, 'consumer'); + const manifest = preparePackagedCargoTestClosure({ + cratePath: path.join( + ROOT, + `target/sdk-artifacts/oliphaunt-wasix-rust/oliphaunt-wasix-${sdkVersion}.crate`, + ), + scratch: consumer, + dependencyCrates: [ + ...packages.map((row) => row.cratePath), + path.join(ROOT, `target/sdk-artifacts/oliphaunt-query/oliphaunt-query-${queryVersion}.crate`), + ], + // Inactive platform and optional tool/ICU declarations still participate in + // Cargo resolution; the selected host's runtime and engine are real archives. + stubDependencyPrefixes: ['liboliphaunt-wasix-aot-', 'oliphaunt-wasix-tools'], + stubDependencies: ['oliphaunt-icu'], + }); + const env = Object.fromEntries( + Object.entries(process.env).filter(([name]) => !name.startsWith('OLIPHAUNT_')), + ); + env.CARGO_TARGET_DIR = path.join(scratch, 'target'); + function run(command: string[]) { + const result = Bun.spawnSync(command, { + cwd: consumer, + env, + stdout: 'inherit', + stderr: 'inherit', + timeout: 1_200_000, + }); + assert.equal(result.exitCode, 0, `packaged SDK AOT: ${command.join(' ')}`); + } + run(['cargo', '--config', 'net.offline=false', 'fetch', '--manifest-path', manifest]); + run([ + 'cargo', + 'nextest', + 'run', + '--manifest-path', + manifest, + '--locked', + '--offline', + '--no-default-features', + '--test', + 'runtime_smoke', + '-E', + 'test(direct_api_) | test(async_api_)', + '--no-tests=fail', + '--test-threads=1', + ]); +} finally { + rmSync(scratch, { recursive: true, force: true }); +} From 1998462a8ab2225e804739e786c0005513949bfc Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Tue, 6 Oct 2026 06:06:01 +0000 Subject: [PATCH 08/22] test(wasix): qualify the full extension lifecycle on Windows V8 --- src/wasix/sdks/rust/tools/test-aot.sh | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/wasix/sdks/rust/tools/test-aot.sh b/src/wasix/sdks/rust/tools/test-aot.sh index 0bd3809e0..b792d0e6d 100644 --- a/src/wasix/sdks/rust/tools/test-aot.sh +++ b/src/wasix/sdks/rust/tools/test-aot.sh @@ -26,17 +26,23 @@ fi # Compiling and running the SDK consumes the host's actual AOT carrier crate; # producer tasks only serialize/package/check payload bytes. -bash src/wasix/runtime/tools/runtime-smoke.sh core-smoke +if [ "$host" = x86_64-pc-windows-msvc ]; then + # V8 has different import, memory, and terminal handling from the LLVM hosts. + # Qualify the same full catalog lifecycle against this backend as on Linux. + bash src/wasix/runtime/tools/runtime-smoke.sh regression +else + bash src/wasix/runtime/tools/runtime-smoke.sh core-smoke +fi if [ "$host" = x86_64-pc-windows-msvc ]; then tools/dev/bun.sh src/wasix/sdks/rust/tools/test-packaged-aot.mts fi -# The portable/Linux regression exercises every catalogued extension. Each host +# The Linux regression and Windows V8 lane exercise every catalogued extension. Each host # must also deserialize and execute machine code produced for that exact host, # including a side module and the split pg_dump/psql tool artifacts. Keep this -# bounded representative lane on all four AOT builders so cross-host coverage -# does not multiply the exhaustive 39-extension lifecycle suite by four. +# bounded representative lane on the LLVM builders so cross-host coverage +# does not repeat the exhaustive lifecycle suite on every LLVM host. proof_root="$root/target/wasix-target-aot-smoke" rm -rf "$proof_root" OLIPHAUNT_WASIX_GENERATED_ASSET_ROOT="$root/target/extensions/wasix/assets" \ From b88f7d87737727cfc5e68a0a9900d92e12d5eda1 Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Tue, 6 Oct 2026 06:15:54 +0000 Subject: [PATCH 09/22] refactor(wasix): keep one Windows cache producer helper --- .github/workflows/wasix-host.yml | 2 +- src/wasix/runtime/engine/windows/build.mts | 54 +++++++++---------- .../runtime/engine/windows/cache-producer.c | 50 ++++++----------- src/wasix/runtime/moon.yml | 2 +- 4 files changed, 43 insertions(+), 65 deletions(-) diff --git a/.github/workflows/wasix-host.yml b/.github/workflows/wasix-host.yml index 87f4a4153..1120b589c 100644 --- a/.github/workflows/wasix-host.yml +++ b/.github/workflows/wasix-host.yml @@ -54,7 +54,7 @@ jobs: include-hidden-files: true path: | target/oliphaunt-wasix/engine/windows-x64-msvc/oliphaunt_wee8.dll - target/oliphaunt-wasix/engine/windows-x64-msvc/cache-*.exe + target/oliphaunt-wasix/engine/windows-x64-msvc/cache-producer.exe target/oliphaunt-wasix/engine/windows-x64-msvc/source.json target/oliphaunt-wasix/engine/windows-x64-msvc/LICENSE.v8 src/wasix/runtime/engine/crates/*/upstream/** diff --git a/src/wasix/runtime/engine/windows/build.mts b/src/wasix/runtime/engine/windows/build.mts index 45bfabffa..b1de59643 100644 --- a/src/wasix/runtime/engine/windows/build.mts +++ b/src/wasix/runtime/engine/windows/build.mts @@ -95,36 +95,30 @@ function build() { path.join(output, 'profile.h'), `#define OLIPHAUNT_V8_FLAGS ${JSON.stringify(V8_FLAGS)}\n`, ); - for (const [name, defines] of [ - ['cache-producer', ['/DOLIPHAUNT_EMULATED_PRODUCER']], - ['cache-reader', []], - ] as const) { - run([ - compiler, - '/nologo', - '/c', - '/O2', - '/GS-', - '/Zl', - `/I${output}`, - ...defines, - path.join(import.meta.dir, 'cache-producer.c'), - `/Fo${path.join(output, `${name}.obj`)}`, - ]); - run([ - linker, - '/nologo', - '/nodefaultlib', - '/machine:x64', - '/entry:mainCRTStartup', - '/subsystem:console', - '/Brepro', - path.join(output, `${name}.obj`), - 'kernel32.lib', - path.join(output, 'oliphaunt_wee8.lib'), - `/out:${path.join(output, `${name}.exe`)}`, - ]); - } + run([ + compiler, + '/nologo', + '/c', + '/O2', + '/GS-', + '/Zl', + `/I${output}`, + path.join(import.meta.dir, 'cache-producer.c'), + `/Fo${path.join(output, 'cache-producer.obj')}`, + ]); + run([ + linker, + '/nologo', + '/nodefaultlib', + '/machine:x64', + '/entry:mainCRTStartup', + '/subsystem:console', + '/Brepro', + path.join(output, 'cache-producer.obj'), + 'kernel32.lib', + path.join(output, 'oliphaunt_wee8.lib'), + `/out:${path.join(output, 'cache-producer.exe')}`, + ]); const dll = path.join(output, 'oliphaunt_wee8.dll'); const digest = sha256File(dll); const prebuilt = path.join(owner, 'crates/wasmer/prebuilt'); diff --git a/src/wasix/runtime/engine/windows/cache-producer.c b/src/wasix/runtime/engine/windows/cache-producer.c index 5c159bb95..11b08c15d 100644 --- a/src/wasix/runtime/engine/windows/cache-producer.c +++ b/src/wasix/runtime/engine/windows/cache-producer.c @@ -1,25 +1,11 @@ #include "profile.h" -/* Producer-only Windows V8 cache control. No CRT or Windows SDK needed. */ -typedef unsigned long long usize; -typedef unsigned int DWORD; -typedef unsigned short WCHAR; -typedef void *HANDLE; +#define WIN32_LEAN_AND_MEAN +#include +#include +/* Producer-only Windows V8 cache control, linked without the CRT. */ +typedef SIZE_T usize; typedef struct { usize size; unsigned char *data; } Vec; -void __cpuidex(int[4], int, int); -#pragma intrinsic(__cpuidex) #define IMP __declspec(dllimport) -IMP HANDLE __stdcall GetStdHandle(DWORD); -IMP HANDLE __stdcall CreateFileW(const WCHAR *, DWORD, DWORD, void *, DWORD, DWORD, HANDLE); -IMP int __stdcall ReadFile(HANDLE, void *, DWORD, DWORD *, void *); -IMP int __stdcall WriteFile(HANDLE, const void *, DWORD, DWORD *, void *); -IMP int __stdcall GetFileSizeEx(HANDLE, long long *); -IMP int __stdcall CloseHandle(HANDLE); -IMP WCHAR *__stdcall GetCommandLineW(void); -IMP void __stdcall ExitProcess(DWORD); -#ifdef OLIPHAUNT_EMULATED_PRODUCER -IMP HANDLE __stdcall GetCurrentProcess(void); -IMP int __stdcall TerminateProcess(HANDLE, DWORD); -#endif IMP void oliphaunt_set_v8_flags(const char *, usize); IMP void *wee8_wasm_engine_new(void); IMP void *wee8_wasm_store_new(void *); @@ -43,23 +29,24 @@ static void number(usize value, int hex) { for(unsigned i=0;i0x7fffffff) fail("read open/size failed",10); - wee8_wasm_byte_vec_new_uninitialized(&v,(usize)size); - if(!ReadFile(f,v.data,(DWORD)size,&got,0) || got!=size) fail("read bytes failed",11); + LARGE_INTEGER size; DWORD got=0; Vec v; + if(f==INVALID_HANDLE_VALUE || !GetFileSizeEx(f,&size) || size.QuadPart<=0 || size.QuadPart>0x7fffffff) fail("read open/size failed",10); + wee8_wasm_byte_vec_new_uninitialized(&v,(usize)size.QuadPart); + if(!ReadFile(f,v.data,(DWORD)size.QuadPart,&got,0) || got!=size.QuadPart) fail("read bytes failed",11); CloseHandle(f); return v; } static void write(const WCHAR *path, const Vec *v) { @@ -93,8 +80,8 @@ void mainCRTStartup(void) { if(wire>data.size-p || data.size-p-wire<20) fail("missing native cache",23); usize offset=p+wire; print("native_bytes="); number(data.size-offset,0); - print(" cpu_mask="); number(*(DWORD*)(data.data+offset+8),1); - print(" flag_hash="); number(*(DWORD*)(data.data+offset+12),1); print("\n"); + print(" cpu_mask="); number(word(data.data+offset+8),1); + print(" flag_hash="); number(word(data.data+offset+12),1); print("\n"); void *module=wee8_wasm_module_deserialize(store,&data); if(!module) fail("native_deserialize=REJECT",24); print("native_deserialize=PASS (no compilation fallback)\n"); @@ -102,11 +89,8 @@ void mainCRTStartup(void) { print("stage=module_deleted\n"); wee8_wasm_store_delete(store); print("stage=store_deleted\n"); wee8_wasm_engine_delete(engine); print("stage=engine_deleted\n"); -#ifdef OLIPHAUNT_EMULATED_PRODUCER /* Wine/QEMU crashes in process detach after all the above cleanup returns. - Only this internal producer avoids that detach path. Native readers and SDK applications retain ordinary Windows process shutdown. */ print("stage=emulated_producer_completed\n"); -#endif finish(0); } diff --git a/src/wasix/runtime/moon.yml b/src/wasix/runtime/moon.yml index a51e7c16e..977401a53 100644 --- a/src/wasix/runtime/moon.yml +++ b/src/wasix/runtime/moon.yml @@ -117,7 +117,7 @@ tasks: - /tools/dev/acquisition.sh outputs: - /target/oliphaunt-wasix/engine/windows-x64-msvc/oliphaunt_wee8.dll - - /target/oliphaunt-wasix/engine/windows-x64-msvc/cache-*.exe + - /target/oliphaunt-wasix/engine/windows-x64-msvc/cache-producer.exe - /target/oliphaunt-wasix/engine/windows-x64-msvc/source.json - /target/oliphaunt-wasix/engine/windows-x64-msvc/LICENSE.v8 - engine/crates/wasmer/prebuilt/embedded_bindings.rs From 39ce90e80cd69e589ea70d329f78e26f27250b40 Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Tue, 6 Oct 2026 07:02:46 +0000 Subject: [PATCH 10/22] refactor(wasix): name native value ownership explicitly --- ...ive-ownership-and-terminal-lifetimes.patch | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/wasix/runtime/engine/patches/wasmer/0001-native-ownership-and-terminal-lifetimes.patch b/src/wasix/runtime/engine/patches/wasmer/0001-native-ownership-and-terminal-lifetimes.patch index ba6d6a537..1d8a0c727 100644 --- a/src/wasix/runtime/engine/patches/wasmer/0001-native-ownership-and-terminal-lifetimes.patch +++ b/src/wasix/runtime/engine/patches/wasmer/0001-native-ownership-and-terminal-lifetimes.patch @@ -47,7 +47,7 @@ } +} + -+// Research only: each constructor transfers one unique Box to V8. ++// Each constructor transfers one unique callback environment to V8. +// V8's existing host-info finalizer releases it when its isolate is disposed. +unsafe extern "C" fn drop_callback_env(ptr: *mut c_void) { + unsafe { drop(Box::from_raw(ptr.cast::>())) }; @@ -118,7 +118,7 @@ // let wasm_func_param_arity(self.handle) - let mut args = unsafe { -+ let mut args = typed::ResearchValueVec(unsafe { ++ let mut args = typed::OwnedValueVec(unsafe { let mut wasm_params = params .iter() - .map(|v| IntoCApiValue::into_cv(v.clone())) @@ -134,7 +134,7 @@ let size = unsafe { wasm_func_result_arity(self.handle) }; - let mut results = { -+ let mut results = typed::ResearchValueVec({ ++ let mut results = typed::OwnedValueVec({ unsafe { let mut vec = Default::default(); wasm_val_vec_new_uninitialized(&mut vec, size); @@ -223,16 +223,16 @@ }; use wasmer_types::RawValue; -+// Disposable numeric-call ownership control. No entity alias deletes. -+pub(super) struct ResearchValueVec(pub(super) wasm_val_vec_t); -+impl std::ops::Deref for ResearchValueVec { ++// Delete owned argument and result vectors on every return path. ++pub(super) struct OwnedValueVec(pub(super) wasm_val_vec_t); ++impl std::ops::Deref for OwnedValueVec { + type Target = wasm_val_vec_t; + fn deref(&self) -> &Self::Target { &self.0 } +} -+impl std::ops::DerefMut for ResearchValueVec { ++impl std::ops::DerefMut for OwnedValueVec { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } +} -+impl Drop for ResearchValueVec { ++impl Drop for OwnedValueVec { + fn drop(&mut self) { unsafe { wasm_val_vec_delete(&mut self.0) } } +} +// C vectors adopt reference wrappers; borrow a Function only through a copy. @@ -271,7 +271,7 @@ }; - let mut results = { -+ let mut results = ResearchValueVec({ ++ let mut results = OwnedValueVec({ unsafe { let mut ret = std::mem::zeroed(); wasm_val_vec_new_uninitialized(&mut ret, Rets::wasm_types().len()); @@ -285,7 +285,7 @@ let mut params = unsafe {std::mem::zeroed()}; unsafe {wasm_val_vec_new(&mut params, params_list.len(), params_list.as_ptr())}; -+ let params = ResearchValueVec(params); ++ let params = OwnedValueVec(params); let mut trap; From d94c7c5a0fec5e41f3b9c3da2cfafebdd1454237 Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Tue, 6 Oct 2026 07:55:27 +0000 Subject: [PATCH 11/22] refactor(wasix): align engine qualification and producer conventions --- src/wasix/runtime/engine/package.test.mts | 71 ------ src/wasix/runtime/engine/prepare-sources.mts | 13 +- src/wasix/runtime/engine/prepare-sources.sh | 10 +- src/wasix/runtime/engine/test-packaged.mts | 80 +++++++ .../runtime/engine/windows/cache-producer.c | 202 ++++++++++++------ src/wasix/runtime/moon.yml | 3 +- 6 files changed, 231 insertions(+), 148 deletions(-) create mode 100644 src/wasix/runtime/engine/test-packaged.mts diff --git a/src/wasix/runtime/engine/package.test.mts b/src/wasix/runtime/engine/package.test.mts index 8bea8567c..36307b6b5 100644 --- a/src/wasix/runtime/engine/package.test.mts +++ b/src/wasix/runtime/engine/package.test.mts @@ -148,74 +148,3 @@ test('freezes a closed engine family, rejects mixed inputs, and reconstructs eve assert.ok(build, 'the extracted carrier must reconstruct its own DLL'); assert.deepEqual(readFileSync(path.join(build.out_dir, 'oliphaunt_wee8.dll')), dll); }); - -test('qualified Windows engine runs from the frozen Cargo package closure', { - skip: process.platform !== 'win32', - timeout: 1_800_000, -}, (t) => { - const root = mkdtempSync(path.join(os.tmpdir(), 'oliphaunt-installed-engine-')); - t.after(() => rmSync(root, { recursive: true, force: true })); - const version = Bun.TOML.parse( - readFileSync(path.join(import.meta.dir, 'crates/wasmer/Cargo.toml'), 'utf8'), - ).package.version; - const packages = packageEngine({ - sourceRoot: path.join(root, 'source'), - outputDir: path.join(root, 'packages'), - version, - }); - const consumer = path.join(root, 'consumer'); - const wasmer = packages.find((row) => row.name === 'oliphaunt-wasmer'); - const manifest = preparePackagedCargoTestClosure({ - cratePath: wasmer.cratePath, - scratch: consumer, - dependencyCrates: packages.filter((row) => row !== wasmer).map((row) => row.cratePath), - }); - function run(command: string[]) { - const result = Bun.spawnSync(command, { - cwd: consumer, - env: { ...process.env, CARGO_TARGET_DIR: path.resolve('target') }, - stdout: 'inherit', - stderr: 'inherit', - timeout: 1_200_000, - }); - assert.equal(result.exitCode, 0, `installed engine: ${command.join(' ')}`); - } - // Only the extracted archives supply private dependencies. No workspace - // producer, source archive, native library, or compiler build is consulted. - run(['cargo', '--config', 'net.offline=false', 'fetch', '--manifest-path', manifest]); - run([ - 'cargo', - 'nextest', - 'run', - '--manifest-path', - manifest, - '--locked', - '--offline', - '--no-default-features', - '--features', - 'v8', - '--lib', - '-E', - 'test(oliphaunt_contract)', - '--no-tests=fail', - '--test-threads=1', - ]); - const wasix = path.join( - consumer, - 'dependencies', - `oliphaunt-wasmer-wasix-${version}`, - 'Cargo.toml', - ); - run(['cargo', '--config', 'net.offline=false', 'fetch', '--manifest-path', wasix]); - run([ - 'cargo', - 'check', - '--manifest-path', - wasix, - '--locked', - '--offline', - '--no-default-features', - '--features', - 'sys-minimal,sys-poll,host-vnet,time,v8,wasmer/headless', - ]); -}); diff --git a/src/wasix/runtime/engine/prepare-sources.mts b/src/wasix/runtime/engine/prepare-sources.mts index b76340dcb..bc3c4887a 100644 --- a/src/wasix/runtime/engine/prepare-sources.mts +++ b/src/wasix/runtime/engine/prepare-sources.mts @@ -1,17 +1,16 @@ import assert from 'node:assert/strict'; import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; import path from 'node:path'; -import { ENGINE_SOURCE_CRATES } from './contract.mts'; import { archiveTreeDigest, promotePathTransactional, sha256File, } from '../../../third-party/tools/source-fetch-core.mts'; +import { ENGINE_SOURCE_CRATES } from './contract.mts'; const root = path.resolve(import.meta.dir, '../../../..'); const owner = path.join(root, 'src/wasix/runtime/engine'); const pins = Bun.TOML.parse(readFileSync(path.join(owner, 'source.toml'), 'utf8')); -const crates = ENGINE_SOURCE_CRATES; function inputs(name) { const directory = path.join(owner, 'patches', name); @@ -35,8 +34,8 @@ function prepare(name) { } const stage = path.join(directory, `.upstream-${process.pid}`); rmSync(stage, { recursive: true, force: true }); - cpSync(source, stage, { recursive: true }); try { + cpSync(source, stage, { recursive: true }); rmSync(path.join(stage, '.oliphaunt-source-pin')); for (const patch of inputs(name)) { const result = Bun.spawnSync(['git', 'apply', '--check', patch], { cwd: stage }); @@ -54,7 +53,7 @@ function prepare(name) { const [operation, plan] = Bun.argv.slice(2); if (operation === 'plan' && plan) { mkdirSync(plan, { recursive: true }); - for (const name of crates) { + for (const name of ENGINE_SOURCE_CRATES) { const pin = pins[name]; writeFileSync( path.join(plan, `${name}.json`), @@ -69,8 +68,12 @@ if (operation === 'plan' && plan) { }), ); } + writeFileSync( + path.join(plan, 'pins'), + ENGINE_SOURCE_CRATES.map((name) => path.resolve(plan, `${name}.json`) + '\0').join(''), + ); } else if (operation === 'prepare' && !plan) { - for (const name of crates) prepare(name); + for (const name of ENGINE_SOURCE_CRATES) prepare(name); } else { throw new Error('usage: prepare-sources.mts plan DIRECTORY | prepare'); } diff --git a/src/wasix/runtime/engine/prepare-sources.sh b/src/wasix/runtime/engine/prepare-sources.sh index c8af518fb..1efb7e477 100644 --- a/src/wasix/runtime/engine/prepare-sources.sh +++ b/src/wasix/runtime/engine/prepare-sources.sh @@ -4,9 +4,11 @@ cd "$(git rev-parse --show-toplevel)" source src/third-party/tools/fetch-sources.sh engine_dir=src/wasix/runtime/engine plan=target/oliphaunt-wasix/engine/source-plan -mkdir -p "$plan" bun "$engine_dir/prepare-sources.mts" plan "$plan" -for name in wasmer wasmer-wasix-types wasmer-journal wasmer-wasix; do - fetch_source "$plan/$name.json" target/oliphaunt-sources/checkouts target/oliphaunt-sources/archives fetch -done +while IFS= read -r -d '' pin; do + fetch_source "$pin" target/oliphaunt-sources/checkouts target/oliphaunt-sources/archives fetch + # Bash 3.2 does not propagate a failed subshell function through this loop. + status=$? + [[ "$status" == 0 ]] || exit "$status" +done < "$plan/pins" bun "$engine_dir/prepare-sources.mts" prepare diff --git a/src/wasix/runtime/engine/test-packaged.mts b/src/wasix/runtime/engine/test-packaged.mts new file mode 100644 index 000000000..b4e2a1b0e --- /dev/null +++ b/src/wasix/runtime/engine/test-packaged.mts @@ -0,0 +1,80 @@ +import assert from 'node:assert/strict'; +import { mkdtempSync, readFileSync, rmSync } from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { preparePackagedCargoTestClosure } from '../../../../tools/packaging/cargo-package-test-closure.mts'; +import { packageEngine } from './package.mts'; + +assert.equal(process.platform, 'win32', 'installed engine qualification requires Windows'); +const root = mkdtempSync(path.join(os.tmpdir(), 'oliphaunt-installed-engine-')); +try { + const version = Bun.TOML.parse( + readFileSync(path.join(import.meta.dir, 'crates/wasmer/Cargo.toml'), 'utf8'), + ).package.version; + const packages = packageEngine({ + sourceRoot: path.join(root, 'source'), + outputDir: path.join(root, 'packages'), + version, + }); + const consumer = path.join(root, 'consumer'); + const wasmer = packages.find((row) => row.name === 'oliphaunt-wasmer'); + const manifest = preparePackagedCargoTestClosure({ + cratePath: wasmer.cratePath, + scratch: consumer, + dependencyCrates: packages.filter((row) => row !== wasmer).map((row) => row.cratePath), + }); + const env = Object.fromEntries( + Object.entries(process.env).filter(([name]) => !name.startsWith('OLIPHAUNT_')), + ); + env.CARGO_TARGET_DIR = path.join(root, 'target'); + function run(command: string[]) { + const result = Bun.spawnSync(command, { + cwd: consumer, + env, + stdout: 'inherit', + stderr: 'inherit', + timeout: 1_200_000, + }); + assert.equal(result.exitCode, 0, `installed engine: ${command.join(' ')}`); + } + // Only the extracted archives supply private dependencies. No workspace + // producer, source archive, native library, or compiler build is consulted. + run(['cargo', '--config', 'net.offline=false', 'fetch', '--manifest-path', manifest]); + run([ + 'cargo', + 'nextest', + 'run', + '--manifest-path', + manifest, + '--locked', + '--offline', + '--no-default-features', + '--features', + 'v8', + '--lib', + '-E', + 'test(oliphaunt_contract)', + '--no-tests=fail', + '--test-threads=1', + ]); + const wasix = path.join( + consumer, + 'dependencies', + `oliphaunt-wasmer-wasix-${version}`, + 'Cargo.toml', + ); + run(['cargo', '--config', 'net.offline=false', 'fetch', '--manifest-path', wasix]); + run([ + 'cargo', + 'check', + '--manifest-path', + wasix, + '--locked', + '--offline', + '--no-default-features', + '--features', + 'sys-minimal,sys-poll,host-vnet,time,v8,wasmer/headless', + ]); +} finally { + rmSync(root, { recursive: true, force: true }); +} diff --git a/src/wasix/runtime/engine/windows/cache-producer.c b/src/wasix/runtime/engine/windows/cache-producer.c index 11b08c15d..4f37fcc75 100644 --- a/src/wasix/runtime/engine/windows/cache-producer.c +++ b/src/wasix/runtime/engine/windows/cache-producer.c @@ -2,9 +2,14 @@ #define WIN32_LEAN_AND_MEAN #include #include + /* Producer-only Windows V8 cache control, linked without the CRT. */ typedef SIZE_T usize; -typedef struct { usize size; unsigned char *data; } Vec; +typedef struct { + usize size; + unsigned char *data; +} Vec; + #define IMP __declspec(dllimport) IMP void oliphaunt_set_v8_flags(const char *, usize); IMP void *wee8_wasm_engine_new(void); @@ -18,79 +23,144 @@ IMP void *wee8_wasm_module_deserialize(void *, const Vec *); IMP void wee8_wasm_module_serialize(void *, Vec *); IMP void wee8_wasm_module_delete(void *); -static void print(const char *s) { - DWORD done; usize n=0; while(s[n]) n++; - WriteFile(GetStdHandle((DWORD)-11),s,(DWORD)n,&done,0); +static void print(const char *text) { + DWORD done; + usize length = 0; + while (text[length]) length++; + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), text, (DWORD)length, &done, NULL); } + static void number(usize value, int hex) { - char out[32]; unsigned n=0, base=hex?16:10; - do { unsigned d=value%base; out[n++]=(char)(d<10?'0'+d:'a'+d-10); value/=base; } while(value); - if(hex) print("0x"); - for(unsigned i=0;i0x7fffffff) fail("read open/size failed",10); - wee8_wasm_byte_vec_new_uninitialized(&v,(usize)size.QuadPart); - if(!ReadFile(f,v.data,(DWORD)size.QuadPart,&got,0) || got!=size.QuadPart) fail("read bytes failed",11); - CloseHandle(f); return v; + +static Vec read_bytes(const WCHAR *path) { + HANDLE file = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + LARGE_INTEGER size; + DWORD got = 0; + Vec bytes; + if (file == INVALID_HANDLE_VALUE || !GetFileSizeEx(file, &size) || + size.QuadPart <= 0 || size.QuadPart > 0x7fffffff) { + fail("read open/size failed", 10); + } + wee8_wasm_byte_vec_new_uninitialized(&bytes, (usize)size.QuadPart); + if (!ReadFile(file, bytes.data, (DWORD)size.QuadPart, &got, NULL) || got != size.QuadPart) { + fail("read bytes failed", 11); + } + CloseHandle(file); + return bytes; } -static void write(const WCHAR *path, const Vec *v) { - HANDLE f=CreateFileW(path,0x40000000,0,0,2,0,0); DWORD done=0; - if(f==(HANDLE)-1 || !WriteFile(f,v->data,(DWORD)v->size,&done,0) || done!=v->size) fail("write failed",12); - CloseHandle(f); + +static void write_bytes(const WCHAR *path, const Vec *bytes) { + HANDLE file = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + DWORD done = 0; + if (file == INVALID_HANDLE_VALUE || + !WriteFile(file, bytes->data, (DWORD)bytes->size, &done, NULL) || done != bytes->size) { + fail("write failed", 12); + } + CloseHandle(file); } + void mainCRTStartup(void) { - int cpu[4]; - __cpuidex(cpu,1,0); print("cpuid_leaf1_ecx="); number((DWORD)cpu[2],1); - __cpuidex(cpu,7,0); print(" cpuid_leaf7_ecx="); number((DWORD)cpu[2],1); print("\n"); - static const char flags[]=OLIPHAUNT_V8_FLAGS; - oliphaunt_set_v8_flags(flags,sizeof(flags)-1); - int reader=0; const WCHAR *cmd=GetCommandLineW(); - for(usize i=0;cmd[i];i++) if(cmd[i]==' ' && cmd[i+1]=='r' && cmd[i+2]=='e' && cmd[i+3]=='a' && cmd[i+4]=='d') reader=1; - print(reader?"operation=read\n":"operation=write\n"); - void *engine=wee8_wasm_engine_new(), *store=wee8_wasm_store_new(engine); - if(!engine || !store) fail("engine/store failed",20); - Vec data=read(reader?(const WCHAR*)L"cache.bin":(const WCHAR*)L"guest.wasm"); - if(!reader) { - void *m=wee8_wasm_module_new(store,&data); - if(!m) fail("compile failed",21); - Vec cache; wee8_wasm_module_serialize(m,&cache); - wee8_wasm_module_delete(m); wee8_wasm_byte_vec_delete(&data); - data=cache; write((const WCHAR*)L"cache.bin",&data); - } - usize p=0, wire=0; unsigned shift=0; - do { if(p>=data.size || shift>63) fail("invalid wire length",22); - unsigned c=data.data[p++]; wire|=(usize)(c&127)<data.size-p || data.size-p-wire<20) fail("missing native cache",23); - usize offset=p+wire; - print("native_bytes="); number(data.size-offset,0); - print(" cpu_mask="); number(word(data.data+offset+8),1); - print(" flag_hash="); number(word(data.data+offset+12),1); print("\n"); - void *module=wee8_wasm_module_deserialize(store,&data); - if(!module) fail("native_deserialize=REJECT",24); - print("native_deserialize=PASS (no compilation fallback)\n"); - wee8_wasm_module_delete(module); wee8_wasm_byte_vec_delete(&data); - print("stage=module_deleted\n"); - wee8_wasm_store_delete(store); print("stage=store_deleted\n"); - wee8_wasm_engine_delete(engine); print("stage=engine_deleted\n"); - /* Wine/QEMU crashes in process detach after all the above cleanup returns. - SDK applications retain ordinary Windows process shutdown. */ - print("stage=emulated_producer_completed\n"); - finish(0); + int cpu[4]; + __cpuidex(cpu, 1, 0); + print("cpuid_leaf1_ecx="); + number((DWORD)cpu[2], 1); + __cpuidex(cpu, 7, 0); + print(" cpuid_leaf7_ecx="); + number((DWORD)cpu[2], 1); + print("\n"); + + static const char flags[] = OLIPHAUNT_V8_FLAGS; + oliphaunt_set_v8_flags(flags, sizeof(flags) - 1); + int reader = 0; + const WCHAR *command = GetCommandLineW(); + for (usize i = 0; command[i]; i++) { + if (command[i] == ' ' && command[i + 1] == 'r' && command[i + 2] == 'e' && + command[i + 3] == 'a' && command[i + 4] == 'd') reader = 1; + } + print(reader ? "operation=read\n" : "operation=write\n"); + + void *engine = wee8_wasm_engine_new(); + if (!engine) fail("engine failed", 20); + void *store = wee8_wasm_store_new(engine); + if (!store) fail("store failed", 20); + Vec data = read_bytes(reader ? L"cache.bin" : L"guest.wasm"); + if (!reader) { + void *module = wee8_wasm_module_new(store, &data); + if (!module) fail("compile failed", 21); + Vec cache; + wee8_wasm_module_serialize(module, &cache); + wee8_wasm_module_delete(module); + wee8_wasm_byte_vec_delete(&data); + data = cache; + write_bytes(L"cache.bin", &data); + } + + usize position = 0, wire = 0; + unsigned shift = 0; + do { + if (position >= data.size || shift > 63) fail("invalid wire length", 22); + unsigned byte = data.data[position++]; + wire |= (usize)(byte & 127) << shift; + shift += 7; + if (!(byte & 128)) break; + } while (1); + if (wire > data.size - position || data.size - position - wire < 20) { + fail("missing native cache", 23); + } + usize offset = position + wire; + print("native_bytes="); + number(data.size - offset, 0); + print(" cpu_mask="); + number(word(data.data + offset + 8), 1); + print(" flag_hash="); + number(word(data.data + offset + 12), 1); + print("\n"); + + void *module = wee8_wasm_module_deserialize(store, &data); + if (!module) fail("native_deserialize=REJECT", 24); + print("native_deserialize=PASS (no compilation fallback)\n"); + wee8_wasm_module_delete(module); + wee8_wasm_byte_vec_delete(&data); + print("stage=module_deleted\n"); + wee8_wasm_store_delete(store); + print("stage=store_deleted\n"); + wee8_wasm_engine_delete(engine); + print("stage=engine_deleted\n"); + print("stage=emulated_producer_completed\n"); + finish(0); } diff --git a/src/wasix/runtime/moon.yml b/src/wasix/runtime/moon.yml index 977401a53..892215cf6 100644 --- a/src/wasix/runtime/moon.yml +++ b/src/wasix/runtime/moon.yml @@ -99,8 +99,7 @@ tasks: set -eu case "$(uname -s)" in MINGW*|MSYS*|CYGWIN*) - cargo nextest run -p oliphaunt-wasmer --locked --profile ci --no-default-features --features v8 --lib -E 'test(oliphaunt_contract)' --no-tests=fail --test-threads=1 - bun test ./src/wasix/runtime/engine/package.test.mts -t 'qualified Windows engine' + bun src/wasix/runtime/engine/test-packaged.mts ;; esac inputs: ["@group(engine-source)", "@group(cargo-workspace)", "/.config/nextest.toml"] From 0bdab72276e76eeea6ef3217b82111c8c0cee47d Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Tue, 6 Oct 2026 10:14:22 +0000 Subject: [PATCH 12/22] fix(ci): prepare WASIX engine toolchains and lifecycle sources --- .github/workflows/wasix-host.yml | 14 ++++++++------ src/wasix/sdks/rust/moon.yml | 3 ++- tools/ci/workflow-moon-transfers.test.mts | 8 +++++++- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/.github/workflows/wasix-host.yml b/.github/workflows/wasix-host.yml index 1120b589c..c19b7e7a3 100644 --- a/.github/workflows/wasix-host.yml +++ b/.github/workflows/wasix-host.yml @@ -31,7 +31,7 @@ jobs: windows-engine: name: WASIX Windows engine if: ${{ contains(inputs.matrix, 'windows-x64-msvc') }} - runs-on: windows-2022 + runs-on: windows-2025-vs2026 timeout-minutes: 40 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd @@ -155,6 +155,13 @@ jobs: with: name: liboliphaunt-wasix-windows-baseline-caches path: target/oliphaunt-wasix/engine/windows-x64-msvc/cache/sse41 + - name: Set up macOS acquisition and smoke timeout + if: ${{ runner.os == 'macOS' }} + run: | + command -v bash + bash --version + brew list coreutils >/dev/null 2>&1 || HOMEBREW_NO_AUTO_UPDATE=1 brew install coreutils + - name: Prepare engine source run: moon run liboliphaunt-wasix:engine-sources @@ -252,11 +259,6 @@ jobs: path: target/extensions/wasix/release-assets merge-multiple: true - - name: Set up macOS smoke timeout - if: ${{ contains(fromJson(inputs.napi-targets), matrix.target_id) && runner.os == 'macOS' }} - shell: bash - run: brew list coreutils >/dev/null 2>&1 || HOMEBREW_NO_AUTO_UPDATE=1 brew install coreutils - - name: Build WASIX Node-API release assets if: ${{ contains(fromJson(inputs.napi-targets), matrix.target_id) }} shell: bash diff --git a/src/wasix/sdks/rust/moon.yml b/src/wasix/sdks/rust/moon.yml index a898e4c79..d5d279ca6 100644 --- a/src/wasix/sdks/rust/moon.yml +++ b/src/wasix/sdks/rust/moon.yml @@ -50,7 +50,7 @@ tasks: test-regression: tags: ["regression", "runtime", "requires-rust", "ci-wasix-release-regression"] command: "bash src/wasix/runtime/tools/runtime-smoke.sh regression" - deps: [{target: "cargo-sources", cacheStrategy: hash}, "liboliphaunt-wasix:runtime-aot", "extension-artifacts-wasix:build-target", "extension-artifacts-wasix:build-aot", "postgres-tools-wasix:build-aot"] + deps: [{target: "cargo-sources", cacheStrategy: hash}, "liboliphaunt-wasix:engine-sources", "liboliphaunt-wasix:runtime-aot", "extension-artifacts-wasix:build-target", "extension-artifacts-wasix:build-aot", "postgres-tools-wasix:build-aot"] inputs: ["@group(code)", "@group(cargo-workspace)", "/src/wasix/runtime/tools/{runtime-smoke,runtime-preflight,cargo-test-filter}.sh", "/src/wasix/runtime/tools/wasix-extension-features.mts", "/src/extensions/artifacts/packages/tools/**/*"] options: {runFromWorkspaceRoot: true, cache: false} test-integration: @@ -59,6 +59,7 @@ tasks: deps: - target: cargo-sources cacheStrategy: hash + - "liboliphaunt-wasix:engine-sources" - "liboliphaunt-wasix:runtime-aot" - "database-resources:build-wasix-standard" - "database-resources:build-wasix-icu" diff --git a/tools/ci/workflow-moon-transfers.test.mts b/tools/ci/workflow-moon-transfers.test.mts index 6214c3e74..5cc2bf20c 100644 --- a/tools/ci/workflow-moon-transfers.test.mts +++ b/tools/ci/workflow-moon-transfers.test.mts @@ -365,7 +365,7 @@ if (!process.env.OLIPHAUNT_TRANSFER_FIXTURE_PHASE) ], [ 'wasix-release-regression', - ['oliphaunt-wasix-rust:test-integration'], + ['oliphaunt-wasix-rust:test-integration', 'oliphaunt-wasix-rust:test-regression'], [ 'liboliphaunt-wasix:compiler-output', 'liboliphaunt-wasix:runtime-aot', @@ -389,6 +389,12 @@ if (!process.env.OLIPHAUNT_TRANSFER_FIXTURE_PHASE) ); assert(execution.targets.includes(root)); const executed = [...execution.targets, ...execution.localDependencies]; + if (job === 'wasix-release-regression') { + assert( + executed.includes('liboliphaunt-wasix:engine-sources'), + `${root} must prepare patched engine sources despite the transferred AOT boundary`, + ); + } for (const target of forbidden) assert(!executed.includes(target), `${root} rebuilds ${target}`); assert.equal( From 56839512dbcc7663b0d0e733d0e7735d8e2156f7 Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Tue, 6 Oct 2026 19:57:29 +0000 Subject: [PATCH 13/22] fix(ci): execute selected WASIX engine tasks without base lookup --- .github/workflows/wasix-host.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wasix-host.yml b/.github/workflows/wasix-host.yml index c19b7e7a3..0ae2bbca5 100644 --- a/.github/workflows/wasix-host.yml +++ b/.github/workflows/wasix-host.yml @@ -47,7 +47,7 @@ jobs: tools: cargo-nextest@0.9.137 - uses: ./.github/actions/setup-msvc - name: Produce and test pinned Windows engine - run: moon run liboliphaunt-wasix:engine-test + run: .github/scripts/run-moon-targets.sh liboliphaunt-wasix:engine-test - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with: name: liboliphaunt-wasix-windows-engine @@ -163,7 +163,7 @@ jobs: brew list coreutils >/dev/null 2>&1 || HOMEBREW_NO_AUTO_UPDATE=1 brew install coreutils - name: Prepare engine source - run: moon run liboliphaunt-wasix:engine-sources + run: .github/scripts/run-moon-targets.sh liboliphaunt-wasix:engine-sources - name: Download portable WASIX build outputs uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c From 5e36955bda1fa74e4fce85c75834b180cf3d459e Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Tue, 6 Oct 2026 20:59:25 +0000 Subject: [PATCH 14/22] fix(wasix): extract Windows engine archive with a local path --- src/wasix/runtime/engine/windows/build.mts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wasix/runtime/engine/windows/build.mts b/src/wasix/runtime/engine/windows/build.mts index b1de59643..982c97fe9 100644 --- a/src/wasix/runtime/engine/windows/build.mts +++ b/src/wasix/runtime/engine/windows/build.mts @@ -49,7 +49,8 @@ function build() { statSync(library).size !== pin['member-bytes'] || sha256File(library) !== pin['member-sha256'] ) { - const extracted = Bun.spawnSync(['tar', '-xOf', archive, '--', pin.member], { + const extracted = Bun.spawnSync(['tar', '-xOf', path.basename(archive), '--', pin.member], { + cwd: inputs, stdout: Bun.file(library), stderr: 'inherit', }); From ed5fa47567798359d98ef1a3798bbac67cad8180 Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Tue, 6 Oct 2026 22:18:17 +0000 Subject: [PATCH 15/22] fix(wasix): close installed engine qualification dependencies --- src/wasix/runtime/engine/package.test.mts | 44 +++++++++++++++++++++- src/wasix/runtime/engine/test-packaged.mts | 29 +++++++------- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/wasix/runtime/engine/package.test.mts b/src/wasix/runtime/engine/package.test.mts index 36307b6b5..4869030a0 100644 --- a/src/wasix/runtime/engine/package.test.mts +++ b/src/wasix/runtime/engine/package.test.mts @@ -1,6 +1,14 @@ import assert from 'node:assert/strict'; import { randomBytes } from 'node:crypto'; -import { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { + cpSync, + mkdirSync, + mkdtempSync, + readFileSync, + realpathSync, + rmSync, + writeFileSync, +} from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import test from 'node:test'; @@ -147,4 +155,38 @@ test('freezes a closed engine family, rejects mixed inputs, and reconstructs eve ); assert.ok(build, 'the extracted carrier must reconstruct its own DLL'); assert.deepEqual(readFileSync(path.join(build.out_dir, 'oliphaunt_wee8.dll')), dll); + + const wasix = packages.find((row) => row.name === 'oliphaunt-wasmer-wasix'); + const wasixConsumer = path.join(root, 'wasix-consumer'); + const wasixManifest = preparePackagedCargoTestClosure({ + cratePath: wasix.cratePath, + scratch: wasixConsumer, + dependencyCrates: packages.filter((row) => row !== wasix).map((row) => row.cratePath), + }); + const resolved = Bun.spawnSync( + [ + 'cargo', + '--config', + 'net.offline=false', + 'metadata', + '--manifest-path', + wasixManifest, + '--format-version=1', + '--filter-platform=x86_64-pc-windows-msvc', + '--no-default-features', + '--features', + 'sys-minimal,sys-poll,host-vnet,time,v8,wasmer/headless', + ], + { cwd: wasixConsumer, env, timeout: 300_000 }, + ); + assert.equal(resolved.exitCode, 0, resolved.stderr.toString()); + const family = JSON.parse(resolved.stdout.toString()).packages.filter((row) => + ENGINE_CARGO_PACKAGES.includes(row.name), + ); + assert.deepEqual(family.map((row) => row.name).sort(), [...ENGINE_CARGO_PACKAGES].sort()); + for (const row of family) { + assert.equal(row.version, '9.8.7'); + assert.equal(row.source, null, `${row.name} must come from the extracted engine family`); + assert.ok(realpathSync(row.manifest_path).startsWith(realpathSync(wasixConsumer) + path.sep)); + } }); diff --git a/src/wasix/runtime/engine/test-packaged.mts b/src/wasix/runtime/engine/test-packaged.mts index b4e2a1b0e..46e6b3823 100644 --- a/src/wasix/runtime/engine/test-packaged.mts +++ b/src/wasix/runtime/engine/test-packaged.mts @@ -17,12 +17,19 @@ try { version, }); const consumer = path.join(root, 'consumer'); - const wasmer = packages.find((row) => row.name === 'oliphaunt-wasmer'); - const manifest = preparePackagedCargoTestClosure({ - cratePath: wasmer.cratePath, + // Root the closure at WASIX so its Wasmer dependency is patched locally. + const wasix = packages.find((row) => row.name === 'oliphaunt-wasmer-wasix'); + const wasixManifest = preparePackagedCargoTestClosure({ + cratePath: wasix.cratePath, scratch: consumer, - dependencyCrates: packages.filter((row) => row !== wasmer).map((row) => row.cratePath), + dependencyCrates: packages.filter((row) => row !== wasix).map((row) => row.cratePath), }); + const wasmerManifest = path.join( + consumer, + 'dependencies', + `oliphaunt-wasmer-${version}`, + 'Cargo.toml', + ); const env = Object.fromEntries( Object.entries(process.env).filter(([name]) => !name.startsWith('OLIPHAUNT_')), ); @@ -39,13 +46,13 @@ try { } // Only the extracted archives supply private dependencies. No workspace // producer, source archive, native library, or compiler build is consulted. - run(['cargo', '--config', 'net.offline=false', 'fetch', '--manifest-path', manifest]); + run(['cargo', '--config', 'net.offline=false', 'fetch', '--manifest-path', wasmerManifest]); run([ 'cargo', 'nextest', 'run', '--manifest-path', - manifest, + wasmerManifest, '--locked', '--offline', '--no-default-features', @@ -57,18 +64,12 @@ try { '--no-tests=fail', '--test-threads=1', ]); - const wasix = path.join( - consumer, - 'dependencies', - `oliphaunt-wasmer-wasix-${version}`, - 'Cargo.toml', - ); - run(['cargo', '--config', 'net.offline=false', 'fetch', '--manifest-path', wasix]); + run(['cargo', '--config', 'net.offline=false', 'fetch', '--manifest-path', wasixManifest]); run([ 'cargo', 'check', '--manifest-path', - wasix, + wasixManifest, '--locked', '--offline', '--no-default-features', From edb935f06a96f4a4ffcd4adab7952de4b7ca499a Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Wed, 7 Oct 2026 00:28:13 +0000 Subject: [PATCH 16/22] fix(packaging): normalize native Windows Cargo file listings --- tools/packaging/cargo-source-package.mts | 23 +++++--- tools/packaging/cargo-source-package.test.mts | 53 +++++++++++++++++++ tools/packaging/cargo-source-package.test.sh | 2 +- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/tools/packaging/cargo-source-package.mts b/tools/packaging/cargo-source-package.mts index e365a4d84..4bca74237 100644 --- a/tools/packaging/cargo-source-package.mts +++ b/tools/packaging/cargo-source-package.mts @@ -130,8 +130,15 @@ function portablePackagePath(value, manifest, { fail, rel }) { } } -function parseCargoPackageFiles(text, manifest, { fail, rel }) { - const files = text.split(/\r?\n/u).filter(Boolean); +export function parseCargoPackageFiles( + text, + manifest, + { fail = null, rel = String, platform = process.platform } = {}, +) { + const files = text + .split(/\r?\n/u) + .filter(Boolean) + .map((file) => (platform === 'win32' ? file.replaceAll('\\', '/') : file)); if (files.length === 0) { abort(fail, `cargo package --list returned no files for ${rel(manifest)}`); } @@ -415,12 +422,12 @@ if (import.meta.main) { const [phase, stateFile, ...args] = process.argv.slice(2); if (phase === 'prepare' && args.length === 3) { const [manifest, outputDir, listing] = args; - const state = prepareCargoPackageSource( - manifest, - outputDir, - parseCargoPackageFiles(readFileSync(listing, 'utf8'), manifest, { fail: null, rel: String }), - { noticeProfile: process.env.OLIPHAUNT_CARGO_NOTICE_PROFILE ?? null }, - ); + const files = parseCargoPackageFiles(readFileSync(listing, 'utf8'), manifest); + const state = prepareCargoPackageSource(manifest, outputDir, files, { + noticeProfile: process.env.OLIPHAUNT_CARGO_NOTICE_PROFILE ?? null, + }); + // The caller retains this listing for cross-platform archive validation. + writeFileSync(listing, files.join('\n') + '\n'); writeFileSync(stateFile, JSON.stringify(state)); console.log(state.stagedManifest); } else if (phase === 'finish' && args.length === 1) { diff --git a/tools/packaging/cargo-source-package.test.mts b/tools/packaging/cargo-source-package.test.mts index 3ce75ef96..ad57bb493 100644 --- a/tools/packaging/cargo-source-package.test.mts +++ b/tools/packaging/cargo-source-package.test.mts @@ -21,9 +21,11 @@ import { fitCargoPayloadParts, packageGeneratedCargoSource, packagedCargoManifestText, + parseCargoPackageFiles, } from './cargo-source-package.mts'; import { readPortableArchiveEntries } from './portable-archive.mts'; import { assertReleaseNoticesInArchive } from './release-notices.mts'; +import { requireCrateMatchesCargoListing } from './release-carrier.mts'; function fixture(t, name) { const root = mkdtempSync(path.join(os.tmpdir(), `oliphaunt-${name}-`)); @@ -86,6 +88,16 @@ if (['prepare', 'verify'].includes(process.argv[2])) { const first = readFileSync(path.join(root, 'first.path'), 'utf8').trim(); const second = readFileSync(path.join(root, 'second.path'), 'utf8').trim(); assert.deepEqual(readFileSync(first), readFileSync(second)); + for (const output of ['first', 'second']) { + const listing = path.join(root, `${output}-files.txt`); + assert.equal(readFileSync(listing, 'utf8').includes('\\'), false); + requireCrateMatchesCargoListing( + readFileSync(path.join(root, `${output}.path`), 'utf8').trim(), + listing, + 'selected-package', + '0.1.0', + ); + } assert.equal( readFileSync(first).subarray(0, 10).toString('hex'), '1f8b0800000000000003', @@ -232,6 +244,47 @@ test('rejects absolute, parent, backslash, and non-portable Cargo member paths', } }); +test('normalizes native Cargo listings before validating portable member paths', () => { + const expected = ['Cargo.toml', 'src/async_api.rs', 'tests/runtime_smoke.rs']; + const manifest = 'fixture/Cargo.toml'; + assert.deepEqual(parseCargoPackageFiles(expected.join('\n'), manifest), expected); + assert.deepEqual( + parseCargoPackageFiles( + 'Cargo.toml\r\nsrc\\async_api.rs\r\ntests\\runtime_smoke.rs\r\n', + manifest, + { + platform: 'win32', + }, + ), + expected, + ); + for (const candidate of [ + '..\\escape', + 'src\\..\\escape', + '\\absolute', + 'C:\\absolute', + '\\\\server\\share', + 'src\\\\empty', + 'bad:name', + ]) { + assert.throws( + () => parseCargoPackageFiles(`Cargo.toml\n${candidate}\n`, manifest, { platform: 'win32' }), + /Cargo package path/u, + ); + } + assert.throws( + () => + parseCargoPackageFiles('Cargo.toml\nsrc\\lib.rs\nsrc/lib.rs\n', manifest, { + platform: 'win32', + }), + /repeated src\/lib.rs/u, + ); + assert.throws( + () => parseCargoPackageFiles('Cargo.toml\nsrc\\lib.rs\n', manifest, { platform: 'linux' }), + /unsafe Cargo package path/u, + ); +}); + test('generated Cargo carriers match Cargo-selected source bytes and reject links', (t) => { const { root, source } = fixture(t, 'generated-cargo'); writeFileSync(path.join(source, 'Cargo.toml'), manifest('generated-carrier')); diff --git a/tools/packaging/cargo-source-package.test.sh b/tools/packaging/cargo-source-package.test.sh index 39de67f04..ea3ac378e 100644 --- a/tools/packaging/cargo-source-package.test.sh +++ b/tools/packaging/cargo-source-package.test.sh @@ -6,7 +6,7 @@ scratch=$(mktemp -d "${TMPDIR:-/tmp}/oliphaunt-cargo-source-XXXXXX") trap 'rm -rf "$scratch"' EXIT bun tools/packaging/cargo-source-package.test.mts prepare "$scratch" for output in first second; do - bash tools/packaging/package-cargo-source.sh "$scratch/source/Cargo.toml" "$scratch/$output" > "$scratch/$output.path" + bash tools/packaging/package-cargo-source.sh "$scratch/source/Cargo.toml" "$scratch/$output" "$scratch/$output-files.txt" > "$scratch/$output.path" done bash tools/packaging/package-cargo-source.sh "$scratch/generated/Cargo.toml" "$scratch/cargo-generated" > "$scratch/generated.path" OLIPHAUNT_CARGO_NOTICE_PROFILE=source-sdk bash tools/packaging/package-cargo-source.sh "$scratch/source/Cargo.toml" "$scratch/notices" > "$scratch/notices.path" From 99e11895b9c67636310fa192c2b7b3961f6d2252 Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Wed, 7 Oct 2026 02:50:59 +0000 Subject: [PATCH 17/22] fix(wasix): keep dependency resolution in native qualification --- src/wasix/runtime/engine/package.test.mts | 44 +---------------------- 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/src/wasix/runtime/engine/package.test.mts b/src/wasix/runtime/engine/package.test.mts index 4869030a0..36307b6b5 100644 --- a/src/wasix/runtime/engine/package.test.mts +++ b/src/wasix/runtime/engine/package.test.mts @@ -1,14 +1,6 @@ import assert from 'node:assert/strict'; import { randomBytes } from 'node:crypto'; -import { - cpSync, - mkdirSync, - mkdtempSync, - readFileSync, - realpathSync, - rmSync, - writeFileSync, -} from 'node:fs'; +import { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import test from 'node:test'; @@ -155,38 +147,4 @@ test('freezes a closed engine family, rejects mixed inputs, and reconstructs eve ); assert.ok(build, 'the extracted carrier must reconstruct its own DLL'); assert.deepEqual(readFileSync(path.join(build.out_dir, 'oliphaunt_wee8.dll')), dll); - - const wasix = packages.find((row) => row.name === 'oliphaunt-wasmer-wasix'); - const wasixConsumer = path.join(root, 'wasix-consumer'); - const wasixManifest = preparePackagedCargoTestClosure({ - cratePath: wasix.cratePath, - scratch: wasixConsumer, - dependencyCrates: packages.filter((row) => row !== wasix).map((row) => row.cratePath), - }); - const resolved = Bun.spawnSync( - [ - 'cargo', - '--config', - 'net.offline=false', - 'metadata', - '--manifest-path', - wasixManifest, - '--format-version=1', - '--filter-platform=x86_64-pc-windows-msvc', - '--no-default-features', - '--features', - 'sys-minimal,sys-poll,host-vnet,time,v8,wasmer/headless', - ], - { cwd: wasixConsumer, env, timeout: 300_000 }, - ); - assert.equal(resolved.exitCode, 0, resolved.stderr.toString()); - const family = JSON.parse(resolved.stdout.toString()).packages.filter((row) => - ENGINE_CARGO_PACKAGES.includes(row.name), - ); - assert.deepEqual(family.map((row) => row.name).sort(), [...ENGINE_CARGO_PACKAGES].sort()); - for (const row of family) { - assert.equal(row.version, '9.8.7'); - assert.equal(row.source, null, `${row.name} must come from the extracted engine family`); - assert.ok(realpathSync(row.manifest_path).startsWith(realpathSync(wasixConsumer) + path.sep)); - } }); From 2cf911685b4f5103dd1513b8df0b3ec55f07af19 Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Wed, 7 Oct 2026 04:08:10 +0000 Subject: [PATCH 18/22] fix(ci): serialize WASIX server executable consumers --- src/wasix/pgwire-server/moon.yml | 7 +++++++ src/wasix/pgwire-server/src/server.rs | 5 ++++- src/wasix/sdks/rust/moon.yml | 7 ++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/wasix/pgwire-server/moon.yml b/src/wasix/pgwire-server/moon.yml index b5bec2db1..3e6ec8f32 100644 --- a/src/wasix/pgwire-server/moon.yml +++ b/src/wasix/pgwire-server/moon.yml @@ -39,6 +39,8 @@ tasks: tags: ["build", "requires-rust"] inputs: ["@group(sources)", "/Cargo.toml", "/Cargo.lock"] outputs: ["/target/debug/oliphaunt-pgwire-server*", "/target/debug/liboliphaunt_pgwire_server.rlib"] + options: + mutex: "oliphaunt-pgwire-server" test: deps: - target: cargo-sources @@ -46,6 +48,8 @@ tasks: command: "cargo test --locked" tags: ["quality", "unit", "requires-rust"] inputs: ["@group(sources)", "README.md", "/Cargo.toml", "/Cargo.lock"] + options: + mutex: "oliphaunt-pgwire-server" test-integration: command: "cargo test --locked -- --ignored --test-threads=1" tags: ["runtime", "integration", "requires-rust", "ci-liboliphaunt-wasix-aot"] @@ -53,6 +57,8 @@ tasks: inputs: ["@group(sources)", "/Cargo.toml", "/Cargo.lock", "/src/extensions/catalog/extensions.source.json", "/src/test-fixtures/extensions/**/*"] options: cache: false + # Cargo releases its build lock while the CLI smoke test runs the executable. + mutex: "oliphaunt-pgwire-server" test-aot: command: "bash src/wasix/pgwire-server/tools/test-aot.sh" tags: ["runtime", "integration", "requires-rust", "ci-liboliphaunt-wasix-aot"] @@ -60,6 +66,7 @@ tasks: inputs: ["@group(sources)", "tools/test-aot.sh", "/Cargo.toml", "/Cargo.lock"] options: cache: false + mutex: "oliphaunt-pgwire-server" runFromWorkspaceRoot: true package: command: "bash src/wasix/pgwire-server/tools/package.sh" diff --git a/src/wasix/pgwire-server/src/server.rs b/src/wasix/pgwire-server/src/server.rs index 8e1a12bcf..f89baf429 100644 --- a/src/wasix/pgwire-server/src/server.rs +++ b/src/wasix/pgwire-server/src/server.rs @@ -7,7 +7,10 @@ use std::os::unix::ffi::OsStrExt; use std::os::unix::fs::{FileTypeExt, MetadataExt}; #[cfg(unix)] use std::os::unix::net::{UnixListener, UnixStream}; -use std::path::{Path, PathBuf}; +#[cfg(unix)] +use std::path::Path; +#[cfg(any(unix, all(test, feature = "icu")))] +use std::path::PathBuf; use std::sync::{ Arc, atomic::{AtomicBool, Ordering}, diff --git a/src/wasix/sdks/rust/moon.yml b/src/wasix/sdks/rust/moon.yml index d5d279ca6..6876d3174 100644 --- a/src/wasix/sdks/rust/moon.yml +++ b/src/wasix/sdks/rust/moon.yml @@ -52,7 +52,11 @@ tasks: command: "bash src/wasix/runtime/tools/runtime-smoke.sh regression" deps: [{target: "cargo-sources", cacheStrategy: hash}, "liboliphaunt-wasix:engine-sources", "liboliphaunt-wasix:runtime-aot", "extension-artifacts-wasix:build-target", "extension-artifacts-wasix:build-aot", "postgres-tools-wasix:build-aot"] inputs: ["@group(code)", "@group(cargo-workspace)", "/src/wasix/runtime/tools/{runtime-smoke,runtime-preflight,cargo-test-filter}.sh", "/src/wasix/runtime/tools/wasix-extension-features.mts", "/src/extensions/artifacts/packages/tools/**/*"] - options: {runFromWorkspaceRoot: true, cache: false} + options: + cache: false + # The full lifecycle also builds the pgwire CLI with catalog features. + mutex: "oliphaunt-pgwire-server" + runFromWorkspaceRoot: true test-integration: tags: ["runtime", "integration", "requires-rust", "ci-wasix-release-regression", "platform-linux-x64-gnu"] command: "bash src/wasix/sdks/rust/tools/test-resources.sh" @@ -106,6 +110,7 @@ tasks: - "/src/wasix/runtime/tools/xtask/**/*" options: cache: false + mutex: "oliphaunt-pgwire-server" runFromWorkspaceRoot: true build: From 8db3bd8885c32a5ca546f9038fc04764ab56610b Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Wed, 7 Oct 2026 07:01:54 +0000 Subject: [PATCH 19/22] fix(wasix): split oversized Windows AOT Cargo payloads --- .../x86_64-pc-windows-msvc/build-support.rs | 12 ++ .../tools/package-cargo-artifacts.mts | 43 +++--- .../x86_64-pc-windows-msvc/build-support.rs | 12 ++ src/wasix/runtime/tools/package-carriers.mts | 16 +++ ...age_liboliphaunt_wasix_cargo_artifacts.mts | 12 +- .../sdks/rust/tools/test-packaged-aot.mts | 10 +- tools/packaging/wasix-cargo-payload.mts | 124 +++++++++++++++++- tools/packaging/wasix-cargo-payload.test.mts | 82 +++++++++++- tools/packaging/wasix-cargo-payload.test.sh | 23 ++++ tools/release/publication-catalog.mts | 5 +- tools/release/publication-catalog.test.mts | 24 +++- 11 files changed, 329 insertions(+), 34 deletions(-) diff --git a/src/wasix/postgres-tools/crates/aot/x86_64-pc-windows-msvc/build-support.rs b/src/wasix/postgres-tools/crates/aot/x86_64-pc-windows-msvc/build-support.rs index 314deb6d9..6e138809d 100644 --- a/src/wasix/postgres-tools/crates/aot/x86_64-pc-windows-msvc/build-support.rs +++ b/src/wasix/postgres-tools/crates/aot/x86_64-pc-windows-msvc/build-support.rs @@ -32,6 +32,9 @@ fn main() { } fn emit_expected_artifact_inputs(target: &str) { + if PACKAGE_LOCAL { + return; + } if let Some(path) = env::var_os("OLIPHAUNT_WASIX_TOOLS_AOT_DIR") { let path = PathBuf::from(path); let candidate = if path.ends_with(target) { @@ -73,6 +76,11 @@ fn find_artifact_dir(target: &str) -> Option { } if PACKAGE_LOCAL { + let reconstructed = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR is set by Cargo")) + .join("artifacts"); + if reconstructed.join("manifest.json").is_file() { + return Some(reconstructed); + } panic!("published WASIX carrier requires package-local artifacts"); } @@ -108,6 +116,10 @@ fn repo_root_from_manifest_dir(manifest_dir: &Path) -> Option<&Path> { } fn emit_rerun_directives(artifact_dir: &Path) { + // Reconstructed payloads are build outputs; their part crates are Cargo inputs. + if artifact_dir.starts_with(env::var_os("OUT_DIR").expect("OUT_DIR is set by Cargo")) { + return; + } println!("cargo:rerun-if-changed={}", artifact_dir.display()); if let Ok(entries) = fs::read_dir(artifact_dir) { for entry in entries.flatten() { diff --git a/src/wasix/postgres-tools/tools/package-cargo-artifacts.mts b/src/wasix/postgres-tools/tools/package-cargo-artifacts.mts index 9e3d85c32..e37a0d979 100644 --- a/src/wasix/postgres-tools/tools/package-cargo-artifacts.mts +++ b/src/wasix/postgres-tools/tools/package-cargo-artifacts.mts @@ -9,7 +9,7 @@ import { WASIX_CARGO_ARTIFACT_SCHEMA, } from '../../runtime/tools/wasix-cargo-artifact-contract.mts'; import { extractPortableArchiveTree } from '../../../../tools/packaging/portable-archive.mts'; -import { packageSpec } from '../../../../tools/packaging/wasix-cargo-payload.mts'; +import { packageAotSpec, packageSpec } from '../../../../tools/packaging/wasix-cargo-payload.mts'; import { currentProductVersionSync } from '../../../../tools/release/release-artifact-targets.mts'; import { fail, @@ -38,7 +38,7 @@ export function packageWasixToolsCargoArtifacts(argv) { mkdirSync(directory, { recursive: true }); } const targets = values.target ?? ['portable', ...Object.keys(AOT_TARGET_TRIPLES)]; - const packages = targets.map((target) => { + const packages = targets.flatMap((target) => { const triple = AOT_TARGET_TRIPLES[target]; if (target !== 'portable' && !triple) fail(`unsupported WASIX tools target ${target}`); const payloadRoot = path.join(extracted, target); @@ -50,26 +50,25 @@ export function packageWasixToolsCargoArtifacts(argv) { extractPortableArchiveTree(archive, payloadRoot); if (triple) validateToolsAotPayload(payloadRoot, triple); else validatePortableToolsPayload(payloadRoot, values.version); - return packageSpec( - { - name: triple ? TOOLS_AOT_PACKAGES[target] : TOOLS_PACKAGE, - target: triple ?? 'portable', - kind: triple ? 'wasix-tools-aot' : 'wasix-tools', - templateDir: path.join( - ROOT, - 'src/wasix/postgres-tools/crates', - triple ? `aot/${triple}` : 'tools', - ), - payloadRoot, - payloadDirName: triple ? 'artifacts' : 'payload', - }, - { - version: values.version, - sourceRoot, - outputDir, - cargoTargetDir: path.join(work, 'cargo-package-target'), - }, - ); + const spec = { + name: triple ? TOOLS_AOT_PACKAGES[target] : TOOLS_PACKAGE, + target: triple ?? 'portable', + kind: triple ? 'wasix-tools-aot' : 'wasix-tools', + templateDir: path.join( + ROOT, + 'src/wasix/postgres-tools/crates', + triple ? `aot/${triple}` : 'tools', + ), + payloadRoot, + payloadDirName: triple ? 'artifacts' : 'payload', + }; + const options = { + version: values.version, + sourceRoot, + outputDir, + cargoTargetDir: path.join(work, 'cargo-package-target'), + }; + return triple ? packageAotSpec(spec, options) : [packageSpec(spec, options)]; }); const relative = (value) => path.relative(ROOT, value).split(path.sep).join('/'); writeFileSync( diff --git a/src/wasix/runtime/crates/aot/x86_64-pc-windows-msvc/build-support.rs b/src/wasix/runtime/crates/aot/x86_64-pc-windows-msvc/build-support.rs index 7b19a128e..bc0921dea 100644 --- a/src/wasix/runtime/crates/aot/x86_64-pc-windows-msvc/build-support.rs +++ b/src/wasix/runtime/crates/aot/x86_64-pc-windows-msvc/build-support.rs @@ -32,6 +32,9 @@ fn main() { } fn emit_expected_artifact_inputs(target: &str) { + if PACKAGE_LOCAL { + return; + } if let Some(path) = env::var_os("OLIPHAUNT_WASM_GENERATED_AOT_DIR") { let path = PathBuf::from(path); let candidate = if path.ends_with(target) { @@ -69,6 +72,11 @@ fn find_artifact_dir(target: &str) -> Option { } if PACKAGE_LOCAL { + let reconstructed = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR is set by Cargo")) + .join("artifacts"); + if reconstructed.join("manifest.json").is_file() { + return Some(reconstructed); + } panic!("published WASIX carrier requires package-local artifacts"); } @@ -102,6 +110,10 @@ fn repo_root_from_manifest_dir(manifest_dir: &Path) -> Option<&Path> { } fn emit_rerun_directives(artifact_dir: &Path) { + // Reconstructed payloads are build outputs; their part crates are Cargo inputs. + if artifact_dir.starts_with(env::var_os("OUT_DIR").expect("OUT_DIR is set by Cargo")) { + return; + } println!("cargo:rerun-if-changed={}", artifact_dir.display()); if let Ok(entries) = fs::read_dir(artifact_dir) { for entry in entries.flatten() { diff --git a/src/wasix/runtime/tools/package-carriers.mts b/src/wasix/runtime/tools/package-carriers.mts index 530a58012..ee8cb6cc2 100644 --- a/src/wasix/runtime/tools/package-carriers.mts +++ b/src/wasix/runtime/tools/package-carriers.mts @@ -15,6 +15,7 @@ import { TOOL, } from '../../../../tools/packaging/release-carrier.mts'; import { writeChecksumManifest } from '../../../../tools/packaging/write-checksum-manifest.mts'; +import { aotPayloadPartParent } from '../../../../tools/packaging/wasix-cargo-payload.mts'; import { compareText, contribCarrierDescriptor, @@ -143,6 +144,7 @@ export function validateWasixCargoArtifacts(outputDir) { if ( !expectedBaseCrates.has(name) && !(kind === 'wasix-engine' && isEnginePayloadPart(name)) && + !(kind === 'wasix-aot' && expectedBaseCrates.has(aotPayloadPartParent(name))) && !isExpectedWasixExtensionPackage(name, kind, expectedExtensionInventory) ) { fail(`unexpected ${WASIX_PRODUCT} Cargo artifact crate ${name}`); @@ -183,6 +185,20 @@ export function validateWasixCargoArtifacts(outputDir) { new Set(packages.map((row) => row.name).filter(isEnginePayloadPart)), new Set(engineParts), ); + for (const parentName of expectedBaseCrates) { + if (!parentName.startsWith('liboliphaunt-wasix-aot-')) continue; + const parent = packages.find((row) => row.name === parentName); + const manifest = Bun.TOML.parse(readFileSync(parent.manifestPath, 'utf8')); + const declaredParts = Object.keys(manifest['build-dependencies'] ?? {}).filter( + (name) => aotPayloadPartParent(name) === parentName, + ); + const generatedParts = packages.filter((row) => aotPayloadPartParent(row.name) === parentName); + assertSameStringSet( + `${parentName} AOT payload parts must match the facade dependency closure`, + new Set(generatedParts.map((row) => row.name)), + new Set(declaredParts), + ); + } const unexpected = readdirSync(outputDir) .filter((name) => name.endsWith('.crate')) .map((name) => path.join(outputDir, name)) diff --git a/src/wasix/runtime/tools/package_liboliphaunt_wasix_cargo_artifacts.mts b/src/wasix/runtime/tools/package_liboliphaunt_wasix_cargo_artifacts.mts index 5ea1a4432..9f48c3431 100755 --- a/src/wasix/runtime/tools/package_liboliphaunt_wasix_cargo_artifacts.mts +++ b/src/wasix/runtime/tools/package_liboliphaunt_wasix_cargo_artifacts.mts @@ -37,6 +37,7 @@ import { import { RUST_BUILD_SCRIPT_SHA256 } from '../../../../tools/packaging/rust-build-script-sha256.mts'; import { cargoPackage, + packageAotSpec, packageSpec, validateCrateSize, } from '../../../../tools/packaging/wasix-cargo-payload.mts'; @@ -1775,8 +1776,8 @@ export function packageWasixCargoArtifacts(argv) { ...extensionAotSources.flatMap((source) => packageExtensionAotSource(source, { outputDir, cargoTargetDir }), ), - ...specs.map((spec) => - packageSpec(spec, { + ...specs.flatMap((spec) => { + const options = { version: args.version, sourceRoot, outputDir, @@ -1786,8 +1787,11 @@ export function packageWasixCargoArtifacts(argv) { ? (text) => injectRuntimeExtensionDependencies(text, extensionSources, extensionAotSources) : undefined, - }), - ), + }; + return spec.kind === 'wasix-aot' + ? packageAotSpec(spec, options) + : [packageSpec(spec, options)]; + }), ]; writePackagesManifest(packages, outputDir); console.log( diff --git a/src/wasix/sdks/rust/tools/test-packaged-aot.mts b/src/wasix/sdks/rust/tools/test-packaged-aot.mts index 52484c8be..89d4bde14 100644 --- a/src/wasix/sdks/rust/tools/test-packaged-aot.mts +++ b/src/wasix/sdks/rust/tools/test-packaged-aot.mts @@ -3,7 +3,10 @@ import { mkdirSync, mkdtempSync, rmSync } from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { preparePackagedCargoTestClosure } from '../../../../../tools/packaging/cargo-package-test-closure.mts'; -import { packageSpec } from '../../../../../tools/packaging/wasix-cargo-payload.mts'; +import { + packageAotSpec, + packageSpec, +} from '../../../../../tools/packaging/wasix-cargo-payload.mts'; import { currentProductVersionSync, ROOT, @@ -54,7 +57,10 @@ try { payloadDirName: 'artifacts', }, ]) { - packages.push(packageSpec(spec, { version, sourceRoot, outputDir, cargoTargetDir: scratch })); + const options = { version, sourceRoot, outputDir, cargoTargetDir: scratch }; + packages.push( + ...(spec.kind === 'wasix-aot' ? packageAotSpec(spec, options) : [packageSpec(spec, options)]), + ); } const sdkVersion = currentProductVersionSync('oliphaunt-wasix-rust'); const queryVersion = currentProductVersionSync('oliphaunt-query'); diff --git a/tools/packaging/wasix-cargo-payload.mts b/tools/packaging/wasix-cargo-payload.mts index ebe760ff1..ce887d498 100644 --- a/tools/packaging/wasix-cargo-payload.mts +++ b/tools/packaging/wasix-cargo-payload.mts @@ -1,8 +1,19 @@ import { createHash } from 'node:crypto'; -import { copyFileSync, cpSync, existsSync, readFileSync, statSync, writeFileSync } from 'node:fs'; +import { + copyFileSync, + cpSync, + existsSync, + lstatSync, + mkdirSync, + readdirSync, + readFileSync, + rmSync, + statSync, + writeFileSync, +} from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { packageGeneratedCargoSource } from './cargo-source-package.mts'; +import { fitCargoPayloadParts, packageGeneratedCargoSource } from './cargo-source-package.mts'; import { assertReleaseNoticesInArchive, assertReleaseNoticesInDirectory, @@ -120,6 +131,10 @@ export function packageSpec( { version, sourceRoot, outputDir, cargoTargetDir, transformManifest }, ) { const crateDir = copyPackageSource(spec, sourceRoot, version, transformManifest); + return freezeSpec(spec, crateDir, { version, outputDir, cargoTargetDir }); +} + +function freezeSpec(spec, crateDir, { version, outputDir, cargoTargetDir }) { const cratePath = cargoPackage(crateDir, cargoTargetDir); validateCrateSize(cratePath); const output = path.join(outputDir, path.basename(cratePath)); @@ -139,3 +154,108 @@ export function packageSpec( sha256: sha256File(output), }; } + +export function aotPayloadPartParent(name) { + return ( + name.match( + /^(liboliphaunt-wasix-aot-x86_64-pc-windows-msvc|oliphaunt-wasix-tools-aot-x86_64-pc-windows-msvc)-part-(?!000)[0-9]{3}$/u, + )?.[1] ?? null + ); +} + +/** Preserve the AOT facade; Cargo privately reconstructs oversized V8 payloads. */ +export function packageAotSpec(spec, options) { + if (spec.target !== 'x86_64-pc-windows-msvc') return [packageSpec(spec, options)]; + const { version, sourceRoot, cargoTargetDir, transformManifest } = options; + const crateDir = copyPackageSource(spec, sourceRoot, version, transformManifest); + const probe = (directory) => + packageGeneratedCargoSource( + path.join(directory, 'Cargo.toml'), + path.join(cargoTargetDir, 'size-probe'), + { packageSizeLimitBytes: Number.MAX_SAFE_INTEGER }, + ); + if (statSync(probe(crateDir)).size <= CRATES_IO_MAX_BYTES) { + return [freezeSpec(spec, crateDir, options)]; + } + const payloadRoot = path.join(crateDir, spec.payloadDirName); + const files = readdirSync(payloadRoot, { recursive: true }) + .sort() + .flatMap((relative) => { + const file = path.join(payloadRoot, relative); + const metadata = lstatSync(file); + if (metadata.isDirectory()) return []; + if (!metadata.isFile()) fail(`AOT payload must contain regular files: ${file}`); + return [{ relative: relative.split(path.sep).join('/'), bytes: readFileSync(file) }]; + }); + const payload = Buffer.concat(files.map((file) => file.bytes)); + const profile = noticeProfileForSpec(spec); + const parts = fitCargoPayloadParts( + (budget) => { + const result = []; + for (let offset = 0; offset < payload.length; offset += budget) { + if (result.length >= 999) fail('AOT payload exceeds supported part count'); + const name = `${spec.name}-part-${String(result.length + 1).padStart(3, '0')}`; + const directory = path.join(sourceRoot, name); + mkdirSync(path.join(directory, 'src'), { recursive: true }); + writeFileSync( + path.join(directory, 'Cargo.toml'), + `[package]\nname = ${JSON.stringify(name)}\nversion = ${JSON.stringify(version)}\nedition = "2024"\nrust-version = "1.96"\ndescription = "Internal Oliphaunt V8 AOT payload part"\nrepository = "https://github.com/f0rr0/oliphaunt"\nlicense = ${JSON.stringify(releaseProfilePackageLicense(profile).spdx)}\n\n[workspace]\n`, + ); + writeFileSync( + path.join(directory, 'payload.part'), + payload.subarray(offset, offset + budget), + ); + writeFileSync( + path.join(directory, 'src/lib.rs'), + 'pub const PAYLOAD: &[u8] = include_bytes!("../payload.part");\n', + ); + stageReleaseNotices(directory, { profile }); + result.push({ ...spec, name, directory }); + } + return result; + }, + (part) => probe(part.directory), + ); + const manifest = path.join(crateDir, 'Cargo.toml'); + writeFileSync( + manifest, + readFileSync(manifest, 'utf8').replace( + '[build-dependencies]', + `[build-dependencies]\n${parts.map(({ name }) => `${name} = "=${version}"`).join('\n')}`, + ), + ); + let offset = 0; + const writes = files + .map(({ relative, bytes }) => { + const begin = offset; + offset += bytes.length; + return ` let file = root.join(${JSON.stringify(relative)});\n fs::create_dir_all(file.parent().unwrap()).expect("create AOT directory");\n fs::write(file, &payload[${begin}..${offset}]).expect("write bundled AOT artifact");`; + }) + .join('\n'); + writeFileSync( + path.join(crateDir, 'build.rs'), + `use std::{env, fs, path::PathBuf}; +use sha2::{Digest, Sha256}; +mod aot { + const PACKAGE_LOCAL: bool = true; + include!("build-support.rs"); + pub fn generate() { main(); } +} +fn main() { + let parts: &[&[u8]] = &[${parts.map(({ name }) => `${name.replaceAll('-', '_')}::PAYLOAD`).join(', ')}]; + let payload = parts.concat(); + assert_eq!(format!("{:x}", Sha256::digest(&payload)), ${JSON.stringify(createHash('sha256').update(payload).digest('hex'))}, "bundled AOT payload digest"); + let root = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR")).join("artifacts"); + if root.exists() { fs::remove_dir_all(&root).expect("remove stale AOT payload"); } +${writes} + println!("cargo::rerun-if-changed=build.rs"); + aot::generate(); +} +`, + ); + rmSync(payloadRoot, { recursive: true }); + return [ + ...parts.map((part) => freezeSpec(part, part.directory, options)), + freezeSpec(spec, crateDir, options), + ]; +} diff --git a/tools/packaging/wasix-cargo-payload.test.mts b/tools/packaging/wasix-cargo-payload.test.mts index aa079305c..2eb0fa24b 100644 --- a/tools/packaging/wasix-cargo-payload.test.mts +++ b/tools/packaging/wasix-cargo-payload.test.mts @@ -1,3 +1,5 @@ +import assert from 'node:assert/strict'; +import { createHash, randomBytes } from 'node:crypto'; import { cpSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; import path from 'node:path'; import { @@ -6,7 +8,8 @@ import { } from '../../src/database-resources/contracts/native-manifest.mts'; import { createDeterministicTar } from './cargo-source-package.mts'; import { extractPortableArchiveTree, releaseZstdCompressSync } from './portable-archive.mts'; -import { packageSpec } from './wasix-cargo-payload.mts'; +import { packageAotSpec, packageSpec } from './wasix-cargo-payload.mts'; +import { preparePackagedCargoTestClosure } from './cargo-package-test-closure.mts'; import { WASIX_AOT_ENGINE } from '../../src/wasix/runtime/tools/wasix-aot-manifest.mts'; const root = path.resolve(import.meta.dir, '../..'); @@ -149,3 +152,80 @@ for (const [id, template, kind, payloadDirName, ancestorAssets, variable, expres rows.push([crate, payloadDirName, variable, external].join('\t')); } writeFileSync(path.join(scratch, 'cases.tsv'), `${rows.join('\n')}\n`); + +const splitRows = []; +for (const [owner, kind, artifact] of [ + ['runtime', 'wasix-aot', 'runtime:oliphaunt'], + ['postgres-tools', 'wasix-tools-aot', 'tool:pg_dump'], +]) { + const target = 'x86_64-pc-windows-msvc'; + const templateDir = path.join(root, `src/wasix/${owner}/crates/aot/${target}`); + const { name, version } = Bun.TOML.parse( + readFileSync(path.join(templateDir, 'Cargo.toml'), 'utf8'), + ).package; + const base = path.join(scratch, `split-${owner}`); + const payloadRoot = path.join(base, 'payload'); + mkdirSync(payloadRoot, { recursive: true }); + const bytes = randomBytes(11 * 1024 * 1024); + const file = owner === 'runtime' ? 'oliphaunt-v8.bin.zst' : 'pg_dump-v8.bin.zst'; + writeFileSync(path.join(payloadRoot, file), bytes); + writeFileSync( + path.join(payloadRoot, 'manifest.json'), + JSON.stringify({ + engine: 'v8', + artifacts: [{ name: artifact, path: file }], + }), + ); + const spec = { name, kind, target, templateDir, payloadRoot, payloadDirName: 'artifacts' }; + const options = { + version, + sourceRoot: path.join(base, 'source'), + outputDir: path.join(base, 'packages'), + cargoTargetDir: path.join(base, 'package-target'), + }; + mkdirSync(options.outputDir); + assert.throws( + () => packageSpec(spec, { ...options, sourceRoot: path.join(base, 'unsplit') }), + /10 MiB/u, + ); + const packages = packageAotSpec(spec, options); + assert.equal(packages.length, 3); + assert(packages.every(({ size }) => size < 10 * 1024 * 1024)); + assert.deepEqual( + packages.map(({ name }) => name), + [`${name}-part-001`, `${name}-part-002`, name], + ); + const parent = packages.at(-1); + const buildDependencies = Bun.TOML.parse(readFileSync(parent.manifestPath, 'utf8'))[ + 'build-dependencies' + ]; + for (const part of packages.slice(0, -1)) + assert.equal(buildDependencies[part.name], `=${version}`); + const manifest = preparePackagedCargoTestClosure({ + cratePath: parent.cratePath, + dependencyCrates: packages.slice(0, -1).map(({ cratePath }) => cratePath), + scratch: path.join(base, 'consumer'), + }); + const crate = path.dirname(manifest); + mkdirSync(path.join(crate, 'examples')); + writeFileSync(manifest, readFileSync(manifest, 'utf8') + '\n[dev-dependencies]\nsha2 = "0.10"\n'); + writeFileSync( + path.join(crate, 'examples/probe.rs'), + `use sha2::{Digest, Sha256}; +fn main() { + let bytes = ${name.replaceAll('-', '_')}::artifact_bytes(${JSON.stringify(artifact)}).unwrap(); + assert_eq!(format!("{:x}", Sha256::digest(bytes)), ${JSON.stringify(createHash('sha256').update(bytes).digest('hex'))}); + assert!(${name.replaceAll('-', '_')}::HAS_EMBEDDED_AOT); + assert_eq!(${name.replaceAll('-', '_')}::ENGINE, "v8"); +} +`, + ); + const part = path.join( + base, + 'consumer/dependencies', + `${packages[0].name}-${version}`, + 'payload.part', + ); + splitRows.push([crate, part].join('\t')); +} +writeFileSync(path.join(scratch, 'split-cases.tsv'), `${splitRows.join('\n')}\n`); diff --git a/tools/packaging/wasix-cargo-payload.test.sh b/tools/packaging/wasix-cargo-payload.test.sh index 96fec9862..fbfce578f 100644 --- a/tools/packaging/wasix-cargo-payload.test.sh +++ b/tools/packaging/wasix-cargo-payload.test.sh @@ -50,4 +50,27 @@ while IFS=$'\t' read -r crate payload variable external; do CARGO_TARGET_DIR="$scratch/cargo-target" cargo check --locked --offline --quiet \ --manifest-path "$crate/Cargo.toml" --lib done < "$scratch/cases.tsv" +while IFS=$'\t' read -r crate part; do + ( + cd "$crate" + cargo --config net.offline=false fetch --manifest-path "$crate/Cargo.toml" + CARGO_TARGET_DIR="$scratch/cargo-target" cargo run --locked --offline --quiet \ + --manifest-path "$crate/Cargo.toml" --example probe + CARGO_TARGET_DIR="$scratch/cargo-target" cargo run --locked --offline --quiet \ + --manifest-path "$crate/Cargo.toml" --example probe --message-format=json > "$scratch/fresh.json" + if rg -q '"fresh":false' "$scratch/fresh.json"; then + echo "Published V8 carrier rebuilt without input changes: $crate" >&2 + exit 1 + fi + cp "$part" "$part.saved" + printf 'corrupt' >> "$part" + if CARGO_TARGET_DIR="$scratch/cargo-target" cargo check --locked --offline --quiet \ + --manifest-path "$crate/Cargo.toml" --lib > "$scratch/corrupt-part.log" 2>&1; then + echo "Published V8 carrier accepted a corrupt AOT payload part: $crate" >&2 + exit 1 + fi + rg -q 'bundled AOT payload digest' "$scratch/corrupt-part.log" + mv "$part.saved" "$part" + ) +done < "$scratch/split-cases.tsv" printf 'WASIX extracted Cargo carrier payload isolation passed\n' diff --git a/tools/release/publication-catalog.mts b/tools/release/publication-catalog.mts index fb5bf20d2..cbb9a95cc 100644 --- a/tools/release/publication-catalog.mts +++ b/tools/release/publication-catalog.mts @@ -146,7 +146,10 @@ function isSplittableCargoCarrier({ ecosystem, name, product, role }) { (SPLITTABLE_CARGO_ROLES.has(role) || (product === 'postgres-tools-native' && role === 'tool-leaf' && - name.startsWith('oliphaunt-tools-'))) + name.startsWith('oliphaunt-tools-')) || + (product === 'postgres-tools-wasix' && + role === 'tool-leaf' && + name === 'oliphaunt-wasix-tools-aot-x86_64-pc-windows-msvc')) ); } diff --git a/tools/release/publication-catalog.test.mts b/tools/release/publication-catalog.test.mts index 8448bad03..c38861a8c 100644 --- a/tools/release/publication-catalog.test.mts +++ b/tools/release/publication-catalog.test.mts @@ -64,8 +64,11 @@ test('native tool target leaves admit exact payload parts while facades remain n const otherToolLeaves = catalog.carriers .filter( - ({ ecosystem, product, role }) => - ecosystem === 'cargo' && product !== 'postgres-tools-native' && role === 'tool-leaf', + ({ ecosystem, name, product, role }) => + ecosystem === 'cargo' && + product !== 'postgres-tools-native' && + role === 'tool-leaf' && + name !== 'oliphaunt-wasix-tools-aot-x86_64-pc-windows-msvc', ) .map(({ name }) => name) .sort((left, right) => (left < right ? -1 : left > right ? 1 : 0)); @@ -77,6 +80,23 @@ test('native tool target leaves admit exact payload parts while facades remain n } }); +test('Windows V8 AOT payload parts retain their declared runtime or tools owner', () => { + const catalog = loadPublicationCatalog('publication-catalog.test'); + for (const [name, product] of [ + ['liboliphaunt-wasix-aot-x86_64-pc-windows-msvc', 'liboliphaunt-wasix'], + ['oliphaunt-wasix-tools-aot-x86_64-pc-windows-msvc', 'postgres-tools-wasix'], + ]) { + expect(resolveActualCarrier(catalog, 'cargo', `${name}-part-001`)).toMatchObject({ + parentCarrier: `cargo:${name}`, + product, + part: 1, + role: 'payload-part', + declared: false, + }); + expect(() => resolveActualCarrier(catalog, 'cargo', `${name}-part-000`)).toThrow(/1-based/u); + } +}); + describe('WASIX extension portable publication carriers', () => { test('assigns every independently versioned portable carrier an explicit canonical target', () => { const products = exactExtensionProducts('publication-catalog.test'); From 8fddaa66e90c97dc970f6ca26e4dba3dd3f46a00 Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Thu, 8 Oct 2026 00:38:37 +0000 Subject: [PATCH 20/22] fix(wasix): align qualification and maintainer workflows --- .github/workflows/ci.yml | 18 ++++---- .github/workflows/wasix-host.yml | 4 +- src/docs/maintainers/development.md | 11 ++++- src/wasix/runtime/tools/serialize-aot.sh | 4 -- src/wasix/runtime/tools/serialize-aot.test.sh | 10 ++++- src/wasix/sdks/rust/tools/test-aot.sh | 5 +-- tools/ci/workflow-moon-transfers.test.mts | 41 +++++++++++++++++++ 7 files changed, 72 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d7cee6c41..3331d5b6d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -543,7 +543,7 @@ jobs: extension-artifacts-native-android: name: Native Extensions (Android Packaging) needs: [affected, extension-artifacts-native-android-static, extension-artifacts-native-linux] - if: ${{ !cancelled() && needs.affected.result == 'success' && needs.extension-artifacts-native-android-static.result == 'success' && needs.extension-artifacts-native-linux.result == 'success' && fromJson(needs.affected.outputs.extension_artifacts_native_matrix_android).include[0] != null }} + if: ${{ !cancelled() && !failure() && needs.affected.result == 'success' && needs.extension-artifacts-native-android-static.result == 'success' && needs.extension-artifacts-native-linux.result == 'success' && fromJson(needs.affected.outputs.extension_artifacts_native_matrix_android).include[0] != null }} uses: ./.github/workflows/extension-artifacts-native.yml with: phase: android-package @@ -591,7 +591,7 @@ jobs: needs: - affected - liboliphaunt-wasix-runtime - if: ${{ !cancelled() && needs.affected.result == 'success' && needs.liboliphaunt-wasix-runtime.result == 'success' && contains(fromJson(needs.affected.outputs.jobs), 'extension-artifacts-wasix') }} + if: ${{ !cancelled() && !failure() && needs.affected.result == 'success' && needs.liboliphaunt-wasix-runtime.result == 'success' && contains(fromJson(needs.affected.outputs.jobs), 'extension-artifacts-wasix') }} strategy: fail-fast: false matrix: ${{ fromJson(needs.affected.outputs.extension_artifacts_wasix_matrix) }} @@ -704,7 +704,7 @@ jobs: mobile-extension-packages-android: name: Extension Packages (Android) needs: [affected, extension-artifacts-native-android] - if: ${{ !cancelled() && needs.affected.result == 'success' && needs.extension-artifacts-native-android.result == 'success' && needs.affected.outputs.mobile_extension_package_native_targets_android_csv != '' }} + if: ${{ !cancelled() && !failure() && needs.affected.result == 'success' && needs.extension-artifacts-native-android.result == 'success' && needs.affected.outputs.mobile_extension_package_native_targets_android_csv != '' }} uses: ./.github/workflows/mobile-extension-packages.yml with: cache-save-if: ${{ needs.affected.outputs.cache_save_if == 'true' }} @@ -716,7 +716,7 @@ jobs: mobile-extension-packages-ios: name: Extension Packages (iOS) needs: [affected, extension-artifacts-native-ios] - if: ${{ !cancelled() && needs.affected.result == 'success' && needs.extension-artifacts-native-ios.result == 'success' && needs.affected.outputs.mobile_extension_package_native_targets_ios_csv != '' }} + if: ${{ !cancelled() && !failure() && needs.affected.result == 'success' && needs.extension-artifacts-native-ios.result == 'success' && needs.affected.outputs.mobile_extension_package_native_targets_ios_csv != '' }} uses: ./.github/workflows/mobile-extension-packages.yml with: cache-save-if: ${{ needs.affected.outputs.cache_save_if == 'true' }} @@ -1102,7 +1102,7 @@ jobs: - liboliphaunt-native-desktop - liboliphaunt-native-ios - liboliphaunt-native-ios-abi - if: ${{ !cancelled() && needs.affected.result == 'success' && needs.liboliphaunt-native-android.result == 'success' && needs.liboliphaunt-native-android-abi.result == 'success' && needs.liboliphaunt-native-desktop.result == 'success' && needs.liboliphaunt-native-ios.result == 'success' && needs.liboliphaunt-native-ios-abi.result == 'success' && contains(fromJson(needs.affected.outputs.jobs), 'liboliphaunt-native-release-assets') }} + if: ${{ !cancelled() && !failure() && needs.affected.result == 'success' && needs.liboliphaunt-native-android.result == 'success' && needs.liboliphaunt-native-android-abi.result == 'success' && needs.liboliphaunt-native-desktop.result == 'success' && needs.liboliphaunt-native-ios.result == 'success' && needs.liboliphaunt-native-ios-abi.result == 'success' && contains(fromJson(needs.affected.outputs.jobs), 'liboliphaunt-native-release-assets') }} runs-on: ubuntu-24.04 timeout-minutes: 30 steps: @@ -1515,7 +1515,7 @@ jobs: - affected - liboliphaunt-native-ios-abi - swift-bindings - if: ${{ !cancelled() && needs.affected.result == 'success' && needs.liboliphaunt-native-ios-abi.result == 'success' && needs.swift-bindings.result == 'success' && contains(fromJson(needs.affected.outputs.jobs), 'swift-sdk-package') }} + if: ${{ !cancelled() && !failure() && needs.affected.result == 'success' && needs.liboliphaunt-native-ios-abi.result == 'success' && needs.swift-bindings.result == 'success' && contains(fromJson(needs.affected.outputs.jobs), 'swift-sdk-package') }} runs-on: ubuntu-24.04 timeout-minutes: 90 steps: @@ -2274,7 +2274,7 @@ jobs: - affected - liboliphaunt-wasix-runtime - liboliphaunt-wasix-aot - if: ${{ !cancelled() && needs.affected.result == 'success' && needs.liboliphaunt-wasix-runtime.result == 'success' && needs.liboliphaunt-wasix-aot.result == 'success' && contains(fromJson(needs.affected.outputs.jobs), 'liboliphaunt-wasix-release-assets') && (github.event_name != 'workflow_dispatch' || inputs.wasm_target == 'all') }} + if: ${{ !cancelled() && !failure() && needs.affected.result == 'success' && needs.liboliphaunt-wasix-runtime.result == 'success' && needs.liboliphaunt-wasix-aot.result == 'success' && contains(fromJson(needs.affected.outputs.jobs), 'liboliphaunt-wasix-release-assets') && (github.event_name != 'workflow_dispatch' || inputs.wasm_target == 'all') }} runs-on: ubuntu-24.04 timeout-minutes: 30 steps: @@ -3024,7 +3024,7 @@ jobs: - liboliphaunt-native-ios-abi - react-native-sdk-package - swift-sdk-package - if: ${{ !cancelled() && needs.affected.result == 'success' && needs.js-sdk-package.result == 'success' && needs.mobile-extension-packages-ios.result == 'success' && needs.liboliphaunt-native-ios.result == 'success' && needs.liboliphaunt-native-ios-abi.result == 'success' && needs.react-native-sdk-package.result == 'success' && needs.swift-sdk-package.result == 'success' && contains(fromJson(needs.affected.outputs.jobs), 'mobile-build-ios') }} + if: ${{ !cancelled() && !failure() && needs.affected.result == 'success' && needs.js-sdk-package.result == 'success' && needs.mobile-extension-packages-ios.result == 'success' && needs.liboliphaunt-native-ios.result == 'success' && needs.liboliphaunt-native-ios-abi.result == 'success' && needs.react-native-sdk-package.result == 'success' && needs.swift-sdk-package.result == 'success' && contains(fromJson(needs.affected.outputs.jobs), 'mobile-build-ios') }} runs-on: macos-26 timeout-minutes: 180 env: @@ -3350,7 +3350,7 @@ jobs: needs: - affected - mobile-build-ios - if: ${{ !cancelled() && needs.affected.result == 'success' && needs.mobile-build-ios.result == 'success' && contains(fromJson(needs.affected.outputs.jobs), 'mobile-build-ios') }} + if: ${{ !cancelled() && !failure() && needs.affected.result == 'success' && needs.mobile-build-ios.result == 'success' && contains(fromJson(needs.affected.outputs.jobs), 'mobile-build-ios') }} runs-on: macos-26 timeout-minutes: 45 steps: diff --git a/.github/workflows/wasix-host.yml b/.github/workflows/wasix-host.yml index 0ae2bbca5..7cb6b56c6 100644 --- a/.github/workflows/wasix-host.yml +++ b/.github/workflows/wasix-host.yml @@ -115,6 +115,7 @@ jobs: name: WASIX host (${{ matrix.target_id }}) runs-on: ${{ matrix.os }} needs: [windows-engine, windows-caches] + # Linux/macOS must still run when Windows producers are skipped or fail. if: ${{ !cancelled() }} timeout-minutes: 240 strategy: @@ -198,7 +199,8 @@ jobs: - name: Build, validate, and smoke target AOT artifacts env: AOT_TARGET: ${{ matrix.target }} - OLIPHAUNT_MOON_TRANSFERRED_DEPS_JSON: '["liboliphaunt-wasix:runtime-portable", "postgres-tools-wasix:compiler-output", "extension-artifacts-wasix:compiler-output", "liboliphaunt-wasix:engine-build"]' + # The uploaded engine already passed engine-test in its producer job. + OLIPHAUNT_MOON_TRANSFERRED_DEPS_JSON: '["liboliphaunt-wasix:runtime-portable", "postgres-tools-wasix:compiler-output", "extension-artifacts-wasix:compiler-output", "liboliphaunt-wasix:engine-test", "liboliphaunt-wasix:engine-build"]' run: .github/scripts/run-planned-moon-job.sh liboliphaunt-wasix-aot - name: Upload target PostgreSQL WASIX tools diff --git a/src/docs/maintainers/development.md b/src/docs/maintainers/development.md index 734b1c3ba..581d1cb1d 100644 --- a/src/docs/maintainers/development.md +++ b/src/docs/maintainers/development.md @@ -296,13 +296,20 @@ bash src/wasix/runtime/tools/build-aot-target.sh moon run liboliphaunt-wasix:smoke ``` -Local AOT generation requires the Wasmer LLVM 22.1.x build for the -maintainer-only serializer. That build includes the LLVM target set Wasmer's +On Linux and macOS, local AOT generation requires the Wasmer LLVM 22.1.x build +for the maintainer-only serializer. That build includes the LLVM target set Wasmer's LLVM backend expects, including LoongArch and WebAssembly. Set `LLVM_SYS_221_PREFIX` to an extracted `wasmerio/llvm-custom-builds` 22.x archive, or use downloaded-artifact mode to avoid local LLVM setup. +Windows AOT uses the bundled V8 engine and same-commit native cache profiles. +The [WASIX host workflow](../../../.github/workflows/wasix-host.yml) builds and +tests the engine on Windows, produces the SSE4.1 baseline cache on Linux, and +transfers both outputs into Windows AOT generation. Use downloaded-artifact +mode for local Windows runtime testing; the serialization commands below +require those producer outputs. + When the portable WASIX assets are already current and only the host AOT crate needs to be refreshed, skip the source/Docker build and generate host AOT from the existing generated portable assets: diff --git a/src/wasix/runtime/tools/serialize-aot.sh b/src/wasix/runtime/tools/serialize-aot.sh index acb05b2da..a29b3ebec 100644 --- a/src/wasix/runtime/tools/serialize-aot.sh +++ b/src/wasix/runtime/tools/serialize-aot.sh @@ -10,10 +10,6 @@ suffix= case "$(uname -s)" in MINGW* | MSYS* | CYGWIN*) suffix=.exe - llvm_prefix="${LLVM_SYS_221_PREFIX:-${LLVM_PATH:-}}" - if [[ -n "$llvm_prefix" && -d "$llvm_prefix/lib" ]]; then - export LIB="$(cygpath -aw "$llvm_prefix/lib")${LIB:+;$LIB}" - fi ;; esac diff --git a/src/wasix/runtime/tools/serialize-aot.test.sh b/src/wasix/runtime/tools/serialize-aot.test.sh index 55a6293be..0beaf2728 100644 --- a/src/wasix/runtime/tools/serialize-aot.test.sh +++ b/src/wasix/runtime/tools/serialize-aot.test.sh @@ -12,11 +12,12 @@ printf '%s\n' "$AOT_FIXTURE_ROOT" SH cat >"$fixture/bin/uname" <<'SH' #!/usr/bin/env bash -printf 'Linux\n' +printf '%s\n' "${AOT_FIXTURE_UNAME:-Linux}" SH cat >"$fixture/bin/cargo" <<'SH' #!/usr/bin/env bash set -euo pipefail +if [[ -n "${AOT_FIXTURE_LIB:-}" ]]; then [[ "${LIB:-}" == "$AOT_FIXTURE_LIB" ]]; fi if [[ "$1" == build ]]; then exit "${FAIL_BUILD:-0}"; fi [[ "$*" == 'run -p xtask --locked -- assets prepare-aot --target-triple fixture' ]] printf 'input one.wasm\toutput one.zst\ninput two.wasm\toutput two.zst\n' @@ -34,6 +35,13 @@ bash "$script" --target-triple fixture printf 'input one.wasm\toutput one.zst\ninput two.wasm\toutput two.zst\n' >"$fixture/expected" cmp "$fixture/expected" "$fixture/serialized" rm "$fixture/serialized" +cp "$CARGO_TARGET_DIR/release/xtask" "$CARGO_TARGET_DIR/release/xtask.exe" +mkdir -p "$fixture/llvm/lib" +AOT_FIXTURE_UNAME=MINGW64_NT LLVM_SYS_221_PREFIX="$fixture/llvm" \ + LIB='existing MSVC libraries' AOT_FIXTURE_LIB='existing MSVC libraries' \ + bash "$script" --target-triple fixture +cmp "$fixture/expected" "$fixture/serialized" +rm "$fixture/serialized" if FAIL_BUILD=1 bash "$script" --target-triple fixture; then exit 1; fi [[ ! -e "$fixture/serialized" ]] if FAIL_SERIALIZE=1 bash "$script" --target-triple fixture; then exit 1; fi diff --git a/src/wasix/sdks/rust/tools/test-aot.sh b/src/wasix/sdks/rust/tools/test-aot.sh index b792d0e6d..f8bb51a39 100644 --- a/src/wasix/sdks/rust/tools/test-aot.sh +++ b/src/wasix/sdks/rust/tools/test-aot.sh @@ -30,14 +30,11 @@ if [ "$host" = x86_64-pc-windows-msvc ]; then # V8 has different import, memory, and terminal handling from the LLVM hosts. # Qualify the same full catalog lifecycle against this backend as on Linux. bash src/wasix/runtime/tools/runtime-smoke.sh regression + tools/dev/bun.sh src/wasix/sdks/rust/tools/test-packaged-aot.mts else bash src/wasix/runtime/tools/runtime-smoke.sh core-smoke fi -if [ "$host" = x86_64-pc-windows-msvc ]; then - tools/dev/bun.sh src/wasix/sdks/rust/tools/test-packaged-aot.mts -fi - # The Linux regression and Windows V8 lane exercise every catalogued extension. Each host # must also deserialize and execute machine code produced for that exact host, # including a side module and the split pg_dump/psql tool artifacts. Keep this diff --git a/tools/ci/workflow-moon-transfers.test.mts b/tools/ci/workflow-moon-transfers.test.mts index 5cc2bf20c..7590b45b2 100644 --- a/tools/ci/workflow-moon-transfers.test.mts +++ b/tools/ci/workflow-moon-transfers.test.mts @@ -347,6 +347,47 @@ if (!process.env.OLIPHAUNT_TRANSFER_FIXTURE_PHASE) } }); +if (!process.env.OLIPHAUNT_TRANSFER_FIXTURE_PHASE) + test('WASIX AOT consumers reuse the same-run engine qualification', () => { + const host = Bun.YAML.parse( + readFileSync(path.join(ROOT, '.github/workflows/wasix-host.yml'), 'utf8'), + ); + const step = host.jobs.build.steps.find((step) => + step.run?.includes('run-planned-moon-job.sh liboliphaunt-wasix-aot'), + ); + const job = 'liboliphaunt-wasix-aot'; + for (const roots of [['liboliphaunt-wasix:runtime-aot'], CI_JOB_TARGETS[job]]) { + const result = Bun.spawnSync( + ['bun', '.github/scripts/resolve-planned-moon-execution.mts', job], + { + cwd: ROOT, + env: { + ...process.env, + ...step.env, + OLIPHAUNT_CI_JOB_TARGETS_JSON: JSON.stringify({ [job]: roots }), + }, + }, + ); + assert.equal(result.exitCode, 0, result.stderr.toString()); + const execution = result.stdout + .toString() + .trim() + .split('\n') + .map((line) => line.split('\t')); + const executed = execution + .filter(([kind]) => kind !== 'transferred') + .flatMap(([, targets]) => targets.split(' ')); + for (const root of roots) assert(executed.includes(root), `${root} must still execute`); + for (const task of ['liboliphaunt-wasix:engine-test', 'liboliphaunt-wasix:engine-build']) + assert(!executed.includes(task), `${task} must consume the producer's qualification`); + assert( + execution.some( + ([kind, target]) => kind === 'transferred' && target === 'liboliphaunt-wasix:engine-test', + ), + ); + } + }); + if (!process.env.OLIPHAUNT_TRANSFER_FIXTURE_PHASE) test('SDK runtime suites execute in artifact-consuming jobs without rebuilding their producers', () => { for (const [job, roots, forbidden] of [ From 794a2a9f02a81f3aa7dc70312a1561fd3e202954 Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Thu, 8 Oct 2026 00:48:31 +0000 Subject: [PATCH 21/22] perf(packaging): reuse validated Windows payload archives --- src/wasix/runtime/engine/package.mts | 13 ++++++++----- tools/packaging/wasix-cargo-payload.mts | 22 +++++++++++++++++----- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/wasix/runtime/engine/package.mts b/src/wasix/runtime/engine/package.mts index 3e9810abe..46b8b0755 100644 --- a/src/wasix/runtime/engine/package.mts +++ b/src/wasix/runtime/engine/package.mts @@ -67,11 +67,13 @@ export function packageEngine({ } const packages = []; - function freeze(name: string, profile: string) { + function freeze(name: string, profile: string, existingCrate?: string) { const manifestPath = path.join(sourceRoot, name, 'Cargo.toml'); - const cratePath = packageGeneratedCargoSource(manifestPath, outputDir, { - noticeProfile: profile, - }); + const cratePath = + existingCrate ?? + packageGeneratedCargoSource(manifestPath, outputDir, { + noticeProfile: profile, + }); const row = { name, manifestPath, @@ -120,7 +122,8 @@ export function packageEngine({ }), 8 * 1024 * 1024, ); - for (const name of parts) freeze(name, 'wasix-engine-windows'); + for (const name of parts) + freeze(name, 'wasix-engine-windows', path.join(outputDir, `${name}-${version}.crate`)); const directory = manifest( ENGINE_PAYLOAD_PACKAGE, 'wasix-engine-windows', diff --git a/tools/packaging/wasix-cargo-payload.mts b/tools/packaging/wasix-cargo-payload.mts index ce887d498..f96e896df 100644 --- a/tools/packaging/wasix-cargo-payload.mts +++ b/tools/packaging/wasix-cargo-payload.mts @@ -134,8 +134,12 @@ export function packageSpec( return freezeSpec(spec, crateDir, { version, outputDir, cargoTargetDir }); } -function freezeSpec(spec, crateDir, { version, outputDir, cargoTargetDir }) { - const cratePath = cargoPackage(crateDir, cargoTargetDir); +function freezeSpec( + spec, + crateDir, + { version, outputDir, cargoTargetDir }, + cratePath = cargoPackage(crateDir, cargoTargetDir), +) { validateCrateSize(cratePath); const output = path.join(outputDir, path.basename(cratePath)); copyFileSync(cratePath, output); @@ -174,8 +178,9 @@ export function packageAotSpec(spec, options) { path.join(cargoTargetDir, 'size-probe'), { packageSizeLimitBytes: Number.MAX_SAFE_INTEGER }, ); - if (statSync(probe(crateDir)).size <= CRATES_IO_MAX_BYTES) { - return [freezeSpec(spec, crateDir, options)]; + const candidate = probe(crateDir); + if (statSync(candidate).size <= CRATES_IO_MAX_BYTES) { + return [freezeSpec(spec, crateDir, options, candidate)]; } const payloadRoot = path.join(crateDir, spec.payloadDirName); const files = readdirSync(payloadRoot, { recursive: true }) @@ -255,7 +260,14 @@ ${writes} ); rmSync(payloadRoot, { recursive: true }); return [ - ...parts.map((part) => freezeSpec(part, part.directory, options)), + ...parts.map((part) => + freezeSpec( + part, + part.directory, + options, + path.join(cargoTargetDir, 'size-probe', `${part.name}-${version}.crate`), + ), + ), freezeSpec(spec, crateDir, options), ]; } From d684f94421dd9fee3dcc43ac5d03a5eaee25544b Mon Sep 17 00:00:00 2001 From: Sid Jain Date: Thu, 8 Oct 2026 02:31:36 +0000 Subject: [PATCH 22/22] fix(wasix): align engine patches and qualification inputs --- .moon/tasks/inputs.yml | 4 + src/wasix/runtime/engine/README.md | 12 +- src/wasix/runtime/engine/contract.mts | 4 + src/wasix/runtime/engine/package.mts | 4 - src/wasix/runtime/engine/package.test.mts | 2 + ...cached-loading-and-terminal-shutdown.patch | 187 --- .../0001-optional-runner-features.patch | 86 ++ .../0002-cached-module-loading.patch | 86 ++ .../0003-terminal-memory-attachment.patch | 67 + .../engine/patches/wasmer-wasix/series | 4 +- ...ive-ownership-and-terminal-lifetimes.patch | 1330 ----------------- .../wasmer/0001-v8-engine-delivery.patch | 51 + .../0002-v8-handle-and-call-ownership.patch | 691 +++++++++ .../0003-v8-native-cache-profiles.patch | 309 ++++ ...004-v8-memory-and-terminal-lifetimes.patch | 546 +++++++ .../runtime/engine/patches/wasmer/series | 5 +- src/wasix/runtime/engine/prepare-sources.mts | 81 +- .../runtime/engine/prepare-sources.test.mts | 27 + src/wasix/runtime/moon.yml | 25 +- src/wasix/runtime/tools/package-carriers.mts | 3 +- tools/packaging/moon.yml | 3 +- 21 files changed, 1963 insertions(+), 1564 deletions(-) delete mode 100644 src/wasix/runtime/engine/patches/wasmer-wasix/0001-cached-loading-and-terminal-shutdown.patch create mode 100644 src/wasix/runtime/engine/patches/wasmer-wasix/0001-optional-runner-features.patch create mode 100644 src/wasix/runtime/engine/patches/wasmer-wasix/0002-cached-module-loading.patch create mode 100644 src/wasix/runtime/engine/patches/wasmer-wasix/0003-terminal-memory-attachment.patch delete mode 100644 src/wasix/runtime/engine/patches/wasmer/0001-native-ownership-and-terminal-lifetimes.patch create mode 100644 src/wasix/runtime/engine/patches/wasmer/0001-v8-engine-delivery.patch create mode 100644 src/wasix/runtime/engine/patches/wasmer/0002-v8-handle-and-call-ownership.patch create mode 100644 src/wasix/runtime/engine/patches/wasmer/0003-v8-native-cache-profiles.patch create mode 100644 src/wasix/runtime/engine/patches/wasmer/0004-v8-memory-and-terminal-lifetimes.patch create mode 100644 src/wasix/runtime/engine/prepare-sources.test.mts diff --git a/.moon/tasks/inputs.yml b/.moon/tasks/inputs.yml index ef06d28b5..46fac3aa9 100644 --- a/.moon/tasks/inputs.yml +++ b/.moon/tasks/inputs.yml @@ -22,6 +22,8 @@ fileGroups: - "/src/third-party/postgres/COPYRIGHT" - "/src/third-party/icu/LICENSE" - "/src/third-party/openssl/LICENSE.txt" + - "/src/wasix/runtime/engine/licenses/Wasmer-LICENSE" + - "/src/wasix/runtime/engine/licenses/V8-LICENSES.txt" packaging-source: - "/tools/packaging/*.{mjs,mts,sh}" - "!/tools/packaging/*.test.{mjs,mts,sh}" @@ -42,6 +44,8 @@ fileGroups: - "/src/extensions/contrib/*.toml" - "/src/extensions/external/**/{recipe,source}.toml" - "/src/third-party/postgres/source.toml" + - "/src/wasix/runtime/engine/source.toml" + - "/src/wasix/runtime/engine/contract.mts" - "/tools/release/release-graph.mts" - "/tools/release/release-history.mts" - "/src/extensions/artifacts/packages/tools/contrib-carriers.mts" diff --git a/src/wasix/runtime/engine/README.md b/src/wasix/runtime/engine/README.md index 9198e0dbe..f791936fd 100644 --- a/src/wasix/runtime/engine/README.md +++ b/src/wasix/runtime/engine/README.md @@ -15,12 +15,18 @@ caches for CPUs with and without CET, compiled with the same pinned DLL and flags. V8 validates the original cache headers and selects a compatible profile; we do not rewrite CPU masks or fall back to compilation. -The Wasmer patch owns C API handles, preserves borrowed call arguments, copies -memory views across growth, and coordinates terminal interrupts with store -lifetimes. The WASIX patch enforces cached module loading and terminal shutdown. +The ordered Wasmer patches cover engine delivery, C API ownership, native cache +profiles, and shared memory/terminal lifetimes. The WASIX patches cover optional +runner features, cached module loading, and terminal shutdown. The complete +ordered series is the build and qualification unit. These are internal compatibility patches, not a promise to support every V8 backend API exposed upstream. +`windows/bindings.rs` retains the generated C ABI for the pinned Wasmer header; +the producer verifies that header's digest before generating DLL dispatch. +Consumers use the frozen bindings and need no binding generator or native +engine build. + Use `moon run liboliphaunt-wasix:engine-sources` to replay the pinned source patches. The Windows host workflow produces the DLL and baseline caches before normal AOT, runtime, extension, and SDK checks. Packaging freezes those same-run diff --git a/src/wasix/runtime/engine/contract.mts b/src/wasix/runtime/engine/contract.mts index bb6be37fe..514349bc5 100644 --- a/src/wasix/runtime/engine/contract.mts +++ b/src/wasix/runtime/engine/contract.mts @@ -6,6 +6,10 @@ export const ENGINE_SOURCE_CRATES = Object.freeze([ ]); export const ENGINE_PAYLOAD_PACKAGE = 'oliphaunt-wasmer-v8-windows-x64-msvc'; +export function isEnginePayloadPart(name: string): boolean { + return new RegExp(`^${ENGINE_PAYLOAD_PACKAGE}-part-(?!000)[0-9]{3}$`, 'u').test(name); +} + export const ENGINE_CARGO_PACKAGES = Object.freeze([ ...ENGINE_SOURCE_CRATES.map((name) => `oliphaunt-${name}`), ENGINE_PAYLOAD_PACKAGE, diff --git a/src/wasix/runtime/engine/package.mts b/src/wasix/runtime/engine/package.mts index 46b8b0755..863cf7281 100644 --- a/src/wasix/runtime/engine/package.mts +++ b/src/wasix/runtime/engine/package.mts @@ -16,10 +16,6 @@ import { ENGINE_PAYLOAD_PACKAGE, ENGINE_SOURCE_CRATES } from './contract.mts'; const OWNER = path.join(ROOT, 'src/wasix/runtime/engine'); const DLL = 'oliphaunt_wee8.dll'; -export function isEnginePayloadPart(name: string): boolean { - return new RegExp(`^${ENGINE_PAYLOAD_PACKAGE}-part-[0-9]{3}$`, 'u').test(name); -} - /** Freeze qualified sources and engine bytes; this step never builds the engine. */ export function packageEngine({ sourceRoot, diff --git a/src/wasix/runtime/engine/package.test.mts b/src/wasix/runtime/engine/package.test.mts index 36307b6b5..e47608671 100644 --- a/src/wasix/runtime/engine/package.test.mts +++ b/src/wasix/runtime/engine/package.test.mts @@ -14,6 +14,7 @@ import { ENGINE_CARGO_PACKAGES, ENGINE_PAYLOAD_PACKAGE, ENGINE_SOURCE_CRATES, + isEnginePayloadPart, } from './contract.mts'; import { packageEngine } from './package.mts'; @@ -68,6 +69,7 @@ test('freezes a closed engine family, rejects mixed inputs, and reconstructs eve save(); const packages = packageEngine(options); const partNames = [`${ENGINE_PAYLOAD_PACKAGE}-part-001`, `${ENGINE_PAYLOAD_PACKAGE}-part-002`]; + assert(!isEnginePayloadPart(`${ENGINE_PAYLOAD_PACKAGE}-part-000`)); assert.deepEqual( packages.map((row) => row.name).sort(), [...ENGINE_CARGO_PACKAGES, ...partNames].sort(), diff --git a/src/wasix/runtime/engine/patches/wasmer-wasix/0001-cached-loading-and-terminal-shutdown.patch b/src/wasix/runtime/engine/patches/wasmer-wasix/0001-cached-loading-and-terminal-shutdown.patch deleted file mode 100644 index 2e2780fde..000000000 --- a/src/wasix/runtime/engine/patches/wasmer-wasix/0001-cached-loading-and-terminal-shutdown.patch +++ /dev/null @@ -1,187 +0,0 @@ ---- a/src/os/task/process.rs -+++ b/src/os/task/process.rs -@@ -925,9 +925,9 @@ - }; - - if signal == Signal::Sigkill { -- // On kill, disable atomics to prevent threads from resuming. -- // NOTE: disable_atomics also wakes all current waiters. -- if let Err(err) = memory.disable_atomics() { -+ // Terminal cancellation: V8 interrupts whole execution contexts; -+ // Sys keeps its existing memory-specific atomics shutdown. -+ if let Err(err) = memory.terminate_execution_contexts() { - tracing::trace!( - pid=%process.pid, - error = &err as &dyn std::error::Error, ---- a/src/runners/wasi.rs -+++ b/src/runners/wasi.rs -@@ -13,10 +13,11 @@ - Runtime, WasiEnvBuilder, WasiError, WasiRuntimeError, - bin_factory::BinaryPackage, - capabilities::Capabilities, -- journal::{DynJournal, DynReadableJournal, SnapshotTrigger}, - runners::{MappedDirectory, MountedDirectory, wasi_common::CommonWasiOptions}, - runtime::task_manager::VirtualTaskManagerExt, - }; -+#[cfg(feature = "journal")] -+use crate::journal::{DynJournal, DynReadableJournal, SnapshotTrigger}; - - use super::wasi_common::{ - ExistingMountConflictBehavior, MAPPED_CURRENT_DIR_DEFAULT_PATH, MappedCommand, -@@ -364,13 +365,16 @@ - - let wasi = webc::metadata::annotations::Wasi::new(program_name); - -- let mut builder = runner.prepare_webc_env( -+ let builder = runner.prepare_webc_env( - program_name, - &wasi, - PackageOrHash::Hash(module_hash), - runtime_or_engine, - None, - )?; -+ -+ #[cfg(any(feature = "ctrlc", feature = "journal"))] -+ let mut builder = builder; - - #[cfg(feature = "ctrlc")] - { ---- a/src/runners/wasi_common.rs -+++ b/src/runners/wasi_common.rs -@@ -6,6 +6,7 @@ - }; - - use anyhow::{Context, Error}; -+#[cfg(feature = "host-fs")] - use tokio::runtime::Handle; - use virtual_fs::{ - ArcFileSystem, ExactMountConflictMode, FileSystem, MountFileSystem, OverlayFileSystem, -@@ -18,8 +19,9 @@ - bin_factory::{BinaryPackage, BinaryPackageMounts}, - capabilities::Capabilities, - fs::WasiFsRoot, -- journal::{DynJournal, DynReadableJournal, SnapshotTrigger}, - }; -+#[cfg(feature = "journal")] -+use crate::journal::{DynJournal, DynReadableJournal, SnapshotTrigger}; - - pub const MAPPED_CURRENT_DIR_DEFAULT_PATH: &str = "/home"; - -@@ -49,10 +51,15 @@ - pub(crate) is_home_mapped: bool, - pub(crate) injected_packages: Vec, - pub(crate) capabilities: Capabilities, -+ #[cfg(feature = "journal")] - pub(crate) read_only_journals: Vec>, -+ #[cfg(feature = "journal")] - pub(crate) writable_journals: Vec>, -+ #[cfg(feature = "journal")] - pub(crate) snapshot_on: Vec, -+ #[cfg(feature = "journal")] - pub(crate) snapshot_interval: Option, -+ #[cfg(feature = "journal")] - pub(crate) stop_running_after_snapshot: bool, - pub(crate) skip_stdio_during_bootstrap: bool, - pub(crate) current_dir: Option, ---- a/src/runtime/mod.rs -+++ b/src/runtime/mod.rs -@@ -353,6 +353,9 @@ - Arc::new(UnsupportedPackageLoader) - } - -+ /// Require cached modules instead of compiling on cache misses. -+ fn module_cache_only(&self) -> bool { false } -+ - /// A cache for compiled modules. - fn module_cache(&self) -> Arc { - // Return a cache that uses a thread-local variable. This isn't ideal -@@ -471,7 +474,19 @@ - - let module_cache = self.module_cache(); - -- let task = async move { load_module(&engine, &module_cache, input, on_progress).await }; -+ let cache_only = self.module_cache_only(); -+ let task = async move { -+ if cache_only { -+ let result = if let Some(progress) = on_progress { -+ module_cache.load_with_progress(input.hash(), &engine, progress).await -+ } else { -+ module_cache.load(input.hash(), &engine).await -+ }; -+ result.map_err(SpawnError::CacheError) -+ } else { -+ load_module(&engine, &module_cache, input, on_progress).await -+ } -+ }; - Box::pin(task) - } - -@@ -674,6 +689,7 @@ - pub source: Arc, - pub engine: Engine, - pub module_cache: Arc, -+ pub module_cache_only: bool, - pub tty: Option>, - #[cfg(feature = "journal")] - pub read_only_journals: Vec>, -@@ -715,6 +731,7 @@ - source: Arc::new(source), - package_loader: Arc::new(loader), - module_cache: Arc::new(module_cache::in_memory()), -+ module_cache_only: false, - #[cfg(feature = "journal")] - read_only_journals: Vec::new(), - #[cfg(feature = "journal")] -@@ -878,6 +895,8 @@ - } - - impl Runtime for PluggableRuntime { -+ fn module_cache_only(&self) -> bool { self.module_cache_only } -+ - fn networking(&self) -> &DynVirtualNetworking { - &self.networking - } -@@ -1111,6 +1130,8 @@ - } - - impl Runtime for OverriddenRuntime { -+ fn module_cache_only(&self) -> bool { self.inner.module_cache_only() } -+ - fn networking(&self) -> &DynVirtualNetworking { - if let Some(net) = self.networking.as_ref() { - net ---- a/src/runtime/task_manager/mod.rs -+++ b/src/runtime/task_manager/mod.rs -@@ -206,7 +206,8 @@ - })?; - Ok(Some(mem)) - } -- SpawnType::AttachMemory(mem) => Ok(Some(mem.attach(store))), -+ SpawnType::AttachMemory(mem) => mem.try_attach(store) -+ .map(Some).map_err(WasiThreadError::MemoryCreateFailed), - SpawnType::CreateMemory | SpawnType::NewLinkerInstanceGroup(..) => Ok(None), - } - } ---- a/src/runtime/task_manager/tokio.rs -+++ b/src/runtime/task_manager/tokio.rs -@@ -150,7 +150,8 @@ - SpawnType::CreateMemoryOfType(t) => (SpawnMemoryTypeOrStore::Type(t), None), - SpawnType::AttachMemory(mem) => { - let mut store = task.env.runtime().new_store(); -- let memory = mem.attach(&mut store); -+ let memory = mem.try_attach(&mut store) -+ .map_err(WasiThreadError::MemoryCreateFailed)?; - (SpawnMemoryTypeOrStore::StoreAndMemory(store, memory), None) - } - }; ---- a/src/state/linker/mod.rs -+++ b/src/state/linker/mod.rs -@@ -692,7 +692,7 @@ - - let mut imports = import_object_for_all_wasi_versions(&main_module, store, &func_env.env); - -- let memory = memory.attach(store); -+ let memory = memory.try_attach(store)?; - - let indirect_function_table = create_indirect_function_table( - store, diff --git a/src/wasix/runtime/engine/patches/wasmer-wasix/0001-optional-runner-features.patch b/src/wasix/runtime/engine/patches/wasmer-wasix/0001-optional-runner-features.patch new file mode 100644 index 000000000..f25ae28f7 --- /dev/null +++ b/src/wasix/runtime/engine/patches/wasmer-wasix/0001-optional-runner-features.patch @@ -0,0 +1,86 @@ +From: Sid Jain +Date: Thu, 8 Oct 2026 00:00:00 +0000 +Subject: [PATCH 1/3] wasix: respect optional runner feature boundaries + +Keep journal and host filesystem runner imports and fields behind their existing +feature gates so the minimal desktop feature set compiles. + +The complete ordered series is the build and qualification unit. + +diff --git a/src/runners/wasi.rs b/src/runners/wasi.rs +--- a/src/runners/wasi.rs ++++ b/src/runners/wasi.rs +@@ -9,11 +9,12 @@ + use wasmer_types::ModuleHash; + use webc::metadata::{Command, annotations::Wasi}; + ++#[cfg(feature = "journal")] ++use crate::journal::{DynJournal, DynReadableJournal, SnapshotTrigger}; + use crate::{ + Runtime, WasiEnvBuilder, WasiError, WasiRuntimeError, + bin_factory::BinaryPackage, + capabilities::Capabilities, +- journal::{DynJournal, DynReadableJournal, SnapshotTrigger}, + runners::{MappedDirectory, MountedDirectory, wasi_common::CommonWasiOptions}, + runtime::task_manager::VirtualTaskManagerExt, + }; +@@ -364,13 +365,16 @@ + + let wasi = webc::metadata::annotations::Wasi::new(program_name); + +- let mut builder = runner.prepare_webc_env( ++ let builder = runner.prepare_webc_env( + program_name, + &wasi, + PackageOrHash::Hash(module_hash), + runtime_or_engine, + None, + )?; ++ ++ #[cfg(any(feature = "ctrlc", feature = "journal"))] ++ let mut builder = builder; + + #[cfg(feature = "ctrlc")] + { +diff --git a/src/runners/wasi_common.rs b/src/runners/wasi_common.rs +--- a/src/runners/wasi_common.rs ++++ b/src/runners/wasi_common.rs +@@ -6,6 +6,7 @@ + }; + + use anyhow::{Context, Error}; ++#[cfg(feature = "host-fs")] + use tokio::runtime::Handle; + use virtual_fs::{ + ArcFileSystem, ExactMountConflictMode, FileSystem, MountFileSystem, OverlayFileSystem, +@@ -13,12 +14,13 @@ + }; + use webc::metadata::annotations::Wasi as WasiAnnotation; + ++#[cfg(feature = "journal")] ++use crate::journal::{DynJournal, DynReadableJournal, SnapshotTrigger}; + use crate::{ + WasiEnvBuilder, + bin_factory::{BinaryPackage, BinaryPackageMounts}, + capabilities::Capabilities, + fs::WasiFsRoot, +- journal::{DynJournal, DynReadableJournal, SnapshotTrigger}, + }; + + pub const MAPPED_CURRENT_DIR_DEFAULT_PATH: &str = "/home"; +@@ -49,10 +51,15 @@ + pub(crate) is_home_mapped: bool, + pub(crate) injected_packages: Vec, + pub(crate) capabilities: Capabilities, ++ #[cfg(feature = "journal")] + pub(crate) read_only_journals: Vec>, ++ #[cfg(feature = "journal")] + pub(crate) writable_journals: Vec>, ++ #[cfg(feature = "journal")] + pub(crate) snapshot_on: Vec, ++ #[cfg(feature = "journal")] + pub(crate) snapshot_interval: Option, ++ #[cfg(feature = "journal")] + pub(crate) stop_running_after_snapshot: bool, + pub(crate) skip_stdio_during_bootstrap: bool, + pub(crate) current_dir: Option, diff --git a/src/wasix/runtime/engine/patches/wasmer-wasix/0002-cached-module-loading.patch b/src/wasix/runtime/engine/patches/wasmer-wasix/0002-cached-module-loading.patch new file mode 100644 index 000000000..dc801adc3 --- /dev/null +++ b/src/wasix/runtime/engine/patches/wasmer-wasix/0002-cached-module-loading.patch @@ -0,0 +1,86 @@ +From: Sid Jain +Date: Thu, 8 Oct 2026 00:00:00 +0000 +Subject: [PATCH 2/3] wasix: require cached modules for SDK runtime loading + +Add an opt-in cache-only policy to the existing Runtime abstraction and preserve +it through runtime overrides. The SDK enables it to keep cache misses from +compiling guests or extensions at runtime. + +The complete ordered series is the build and qualification unit. + +diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs +--- a/src/runtime/mod.rs ++++ b/src/runtime/mod.rs +@@ -353,6 +353,11 @@ + Arc::new(UnsupportedPackageLoader) + } + ++ /// Require cached modules instead of compiling on cache misses. ++ fn module_cache_only(&self) -> bool { ++ false ++ } ++ + /// A cache for compiled modules. + fn module_cache(&self) -> Arc { + // Return a cache that uses a thread-local variable. This isn't ideal +@@ -471,7 +476,21 @@ + + let module_cache = self.module_cache(); + +- let task = async move { load_module(&engine, &module_cache, input, on_progress).await }; ++ let cache_only = self.module_cache_only(); ++ let task = async move { ++ if cache_only { ++ let result = if let Some(progress) = on_progress { ++ module_cache ++ .load_with_progress(input.hash(), &engine, progress) ++ .await ++ } else { ++ module_cache.load(input.hash(), &engine).await ++ }; ++ result.map_err(SpawnError::CacheError) ++ } else { ++ load_module(&engine, &module_cache, input, on_progress).await ++ } ++ }; + Box::pin(task) + } + +@@ -674,6 +693,7 @@ + pub source: Arc, + pub engine: Engine, + pub module_cache: Arc, ++ pub module_cache_only: bool, + pub tty: Option>, + #[cfg(feature = "journal")] + pub read_only_journals: Vec>, +@@ -715,6 +735,7 @@ + source: Arc::new(source), + package_loader: Arc::new(loader), + module_cache: Arc::new(module_cache::in_memory()), ++ module_cache_only: false, + #[cfg(feature = "journal")] + read_only_journals: Vec::new(), + #[cfg(feature = "journal")] +@@ -878,6 +899,10 @@ + } + + impl Runtime for PluggableRuntime { ++ fn module_cache_only(&self) -> bool { ++ self.module_cache_only ++ } ++ + fn networking(&self) -> &DynVirtualNetworking { + &self.networking + } +@@ -1111,6 +1136,10 @@ + } + + impl Runtime for OverriddenRuntime { ++ fn module_cache_only(&self) -> bool { ++ self.inner.module_cache_only() ++ } ++ + fn networking(&self) -> &DynVirtualNetworking { + if let Some(net) = self.networking.as_ref() { + net diff --git a/src/wasix/runtime/engine/patches/wasmer-wasix/0003-terminal-memory-attachment.patch b/src/wasix/runtime/engine/patches/wasmer-wasix/0003-terminal-memory-attachment.patch new file mode 100644 index 000000000..27ef23ddf --- /dev/null +++ b/src/wasix/runtime/engine/patches/wasmer-wasix/0003-terminal-memory-attachment.patch @@ -0,0 +1,67 @@ +From: Sid Jain +Date: Thu, 8 Oct 2026 00:00:00 +0000 +Subject: [PATCH 3/3] wasix: propagate terminal shared memory cancellation + +Use the engine terminal-cancellation operation for process shutdown. Propagate +failed shared-memory attachment through the existing task and linker errors +instead of panicking when shutdown races with task creation. + +The complete ordered series is the build and qualification unit. + +diff --git a/src/os/task/process.rs b/src/os/task/process.rs +--- a/src/os/task/process.rs ++++ b/src/os/task/process.rs +@@ -925,9 +925,9 @@ + }; + + if signal == Signal::Sigkill { +- // On kill, disable atomics to prevent threads from resuming. +- // NOTE: disable_atomics also wakes all current waiters. +- if let Err(err) = memory.disable_atomics() { ++ // Terminal cancellation: V8 interrupts whole execution contexts; ++ // Sys keeps its existing memory-specific atomics shutdown. ++ if let Err(err) = memory.terminate_execution_contexts() { + tracing::trace!( + pid=%process.pid, + error = &err as &dyn std::error::Error, +diff --git a/src/runtime/task_manager/mod.rs b/src/runtime/task_manager/mod.rs +--- a/src/runtime/task_manager/mod.rs ++++ b/src/runtime/task_manager/mod.rs +@@ -206,7 +206,10 @@ + })?; + Ok(Some(mem)) + } +- SpawnType::AttachMemory(mem) => Ok(Some(mem.attach(store))), ++ SpawnType::AttachMemory(mem) => mem ++ .try_attach(store) ++ .map(Some) ++ .map_err(WasiThreadError::MemoryCreateFailed), + SpawnType::CreateMemory | SpawnType::NewLinkerInstanceGroup(..) => Ok(None), + } + } +diff --git a/src/runtime/task_manager/tokio.rs b/src/runtime/task_manager/tokio.rs +--- a/src/runtime/task_manager/tokio.rs ++++ b/src/runtime/task_manager/tokio.rs +@@ -150,7 +150,9 @@ + SpawnType::CreateMemoryOfType(t) => (SpawnMemoryTypeOrStore::Type(t), None), + SpawnType::AttachMemory(mem) => { + let mut store = task.env.runtime().new_store(); +- let memory = mem.attach(&mut store); ++ let memory = mem ++ .try_attach(&mut store) ++ .map_err(WasiThreadError::MemoryCreateFailed)?; + (SpawnMemoryTypeOrStore::StoreAndMemory(store, memory), None) + } + }; +diff --git a/src/state/linker/mod.rs b/src/state/linker/mod.rs +--- a/src/state/linker/mod.rs ++++ b/src/state/linker/mod.rs +@@ -692,7 +692,7 @@ + + let mut imports = import_object_for_all_wasi_versions(&main_module, store, &func_env.env); + +- let memory = memory.attach(store); ++ let memory = memory.try_attach(store)?; + + let indirect_function_table = create_indirect_function_table( + store, diff --git a/src/wasix/runtime/engine/patches/wasmer-wasix/series b/src/wasix/runtime/engine/patches/wasmer-wasix/series index 02baccc2d..0ecfae9ad 100644 --- a/src/wasix/runtime/engine/patches/wasmer-wasix/series +++ b/src/wasix/runtime/engine/patches/wasmer-wasix/series @@ -1 +1,3 @@ -0001-cached-loading-and-terminal-shutdown.patch +0001-optional-runner-features.patch +0002-cached-module-loading.patch +0003-terminal-memory-attachment.patch diff --git a/src/wasix/runtime/engine/patches/wasmer/0001-native-ownership-and-terminal-lifetimes.patch b/src/wasix/runtime/engine/patches/wasmer/0001-native-ownership-and-terminal-lifetimes.patch deleted file mode 100644 index 1d8a0c727..000000000 --- a/src/wasix/runtime/engine/patches/wasmer/0001-native-ownership-and-terminal-lifetimes.patch +++ /dev/null @@ -1,1330 +0,0 @@ ---- a/src/backend/v8/entities/engine.rs -+++ b/src/backend/v8/entities/engine.rs -@@ -1,4 +1,4 @@ --//! Data types, functions and traits for `sys` runtime's `Engine` implementation. -+//! Data types, functions and traits for `v8` runtime's `Engine` implementation. - use crate::{ - BackendEngine, - backend::v8::bindings::{wasm_engine_delete, wasm_engine_new, wasm_engine_t}, -@@ -27,9 +27,15 @@ - - impl Default for CApiEngine { - fn default() -> Self { -- let engine = ENGINE -- .get_or_init(|| unsafe { std::sync::Mutex::new(EngineCapsule(wasm_engine_new())) }); -- let engine = unsafe { engine.lock().unwrap().0 }; -+ #[cfg(windows)] -+ crate::backend::v8::bindings::oliphaunt_v8_prepare() -+ .expect("prepare Windows V8 engine; use V8::try_new for delivery errors"); -+ let engine = ENGINE.get_or_init(|| unsafe { -+ #[cfg(windows)] -+ crate::backend::v8::bindings::oliphaunt_v8_configure(); -+ std::sync::Mutex::new(EngineCapsule(wasm_engine_new())) -+ }); -+ let engine = engine.lock().unwrap().0; - Self { engine } - } - } -@@ -48,6 +54,13 @@ - /// Create a new instance of the `V8` engine. - pub fn new() -> Self { - Self::default() -+ } -+ -+ /// Prepare the embedded native engine, returning retryable delivery errors. -+ pub fn try_new() -> std::io::Result { -+ #[cfg(windows)] -+ crate::backend::v8::bindings::oliphaunt_v8_prepare()?; -+ Ok(Self::default()) - } - - pub(crate) fn deterministic_id(&self) -> String { ---- a/src/backend/v8/entities/function/mod.rs -+++ b/src/backend/v8/entities/function/mod.rs -@@ -59,6 +59,12 @@ - .field("env_is_some", &self.env_handle.is_some()) - .finish() - } -+} -+ -+// Each constructor transfers one unique callback environment to V8. -+// V8's existing host-info finalizer releases it when its isolate is disposed. -+unsafe extern "C" fn drop_callback_env(ptr: *mut c_void) { -+ unsafe { drop(Box::from_raw(ptr.cast::>())) }; - } - - impl Function { -@@ -145,8 +151,15 @@ - wasm_functype, - Some(callback), - callback_env as *mut _ as _, -- None, -+ Some(drop_callback_env::), - ) -+ }; -+ -+ // The callback has a borrowed StoreMut; retain only its unique C root. -+ unsafe { -+ let root = wasm_func_as_ref(wasm_function); -+ (*callback_env).store.inner.store.as_v8_mut().owned_refs.push(super::store::OwnedRef(root)); -+ wasm_functype_delete(wasm_functype); - }; - - if wasm_function.is_null() { -@@ -217,8 +230,15 @@ - wasm_functype, - Some(callback), - callback_env as _, -- None, -+ Some(drop_callback_env::), - ) -+ }; -+ -+ // The callback has a borrowed StoreMut; retain only its unique C root. -+ unsafe { -+ let root = wasm_func_as_ref(wasm_function); -+ (*callback_env).store.inner.store.as_v8_mut().owned_refs.push(super::store::OwnedRef(root)); -+ wasm_functype_delete(wasm_functype); - }; - - if wasm_function.is_null() { -@@ -297,8 +317,15 @@ - wasm_functype, - Some(callback), - callback_env as _, -- None, -+ Some(drop_callback_env::), - ) -+ }; -+ -+ // The callback has a borrowed StoreMut; retain only its unique C root. -+ unsafe { -+ let root = wasm_func_as_ref(wasm_function); -+ (*callback_env).store.inner.store.as_v8_mut().owned_refs.push(super::store::OwnedRef(root)); -+ wasm_functype_delete(wasm_functype); - }; - - if wasm_function.is_null() { -@@ -332,6 +359,7 @@ - res - }; - -+ unsafe { wasm_functype_delete(type_) }; - FunctionType::new(params, returns) - } - -@@ -375,31 +403,33 @@ - - // let wasm_func_param_arity(self.handle) - -- let mut args = unsafe { -+ let mut args = typed::OwnedValueVec(unsafe { - let mut wasm_params = params - .iter() -- .map(|v| IntoCApiValue::into_cv(v.clone())) -+ .map(|v| typed::own_call_parameter(IntoCApiValue::into_cv(v.clone()))) - .collect::>() - .into_boxed_slice(); - let mut vec = Default::default(); - wasm_val_vec_new(&mut vec, wasm_params.len(), wasm_params.as_ptr()); - vec -- }; -+ }); - - let size = unsafe { wasm_func_result_arity(self.handle) }; - -- let mut results = { -+ let mut results = typed::OwnedValueVec({ - unsafe { - let mut vec = Default::default(); - wasm_val_vec_new_uninitialized(&mut vec, size); - vec - } -- }; -+ }); - - let mut trap; - - loop { -- trap = unsafe { wasm_func_call(self.handle, &mut args as _, &mut results as *mut _) }; -+ store.as_store_ref().inner.store.as_v8().ensure_running()?; -+ let raw_trap = unsafe { wasm_func_call(self.handle, &mut *args as _, &mut *results as *mut _) }; -+ trap = (!raw_trap.is_null()).then(|| Trap::from(raw_trap)); - let store_mut = store.as_store_mut(); - if let Some(callback) = store_mut.inner.on_called.take() { - match callback(store_mut) { -@@ -418,15 +448,17 @@ - break; - } - -- if !trap.is_null() { -- return Err(Into::::into(trap).into()); -+ store.as_store_ref().inner.store.as_v8().ensure_running()?; -+ if let Some(trap) = trap { -+ return Err(trap.into()); - } - - let values = unsafe { - let results = std::ptr::slice_from_raw_parts(results.data, results.size); - (*results) - .iter() -- .map(|v| IntoWasmerValue::into_wv(*v)) -+ .zip(signature.results()) -+ .map(|(v, expected)| typed::into_call_value(*v, *expected, store)) - .collect::>() - .into_boxed_slice() - }; -@@ -526,8 +558,9 @@ - unsafe { trap.into_wasm_trap(&mut store) } - } - -- Err(e) => { -- unimplemented!("host function panicked"); -+ Err(_) => { -+ let trap = Trap::user(Box::new(RuntimeError::new("host function panicked"))); -+ unsafe { trap.into_wasm_trap(&mut store) } - } - } - } -@@ -634,8 +667,9 @@ - // unimplemented!("host function panicked"); - }, - -- Err(e) => { -- unimplemented!("host function panicked"); -+ Err(_) => { -+ let trap = Trap::user(Box::new(RuntimeError::new("host function panicked"))); -+ unsafe { trap.into_wasm_trap(store) } - } - } - }} -@@ -707,7 +741,10 @@ - let trap = crate::backend::v8::error::Trap::user(Box::new(e)); unsafe { trap.into_wasm_trap(store) } - }, - -- Err(e) => { unimplemented!("host function panicked"); } -+ Err(_) => { -+ let trap = Trap::user(Box::new(RuntimeError::new("host function panicked"))); -+ unsafe { trap.into_wasm_trap(store) } -+ } - } - }} - ---- a/src/backend/v8/entities/function/typed.rs -+++ b/src/backend/v8/entities/function/typed.rs -@@ -11,12 +11,47 @@ - use std::iter::FromIterator; - - use crate::{ -- AsStoreMut, FromToNativeWasmType, NativeWasmType, NativeWasmTypeInto, RuntimeError, -+ AsStoreMut, AsStoreRef, FromToNativeWasmType, NativeWasmType, NativeWasmTypeInto, RuntimeError, - TypedFunction, Value, WasmTypeList, - backend::v8::{bindings::*, error::Trap, function::Function, utils::convert::*}, - }; - use wasmer_types::RawValue; - -+// Delete owned argument and result vectors on every return path. -+pub(super) struct OwnedValueVec(pub(super) wasm_val_vec_t); -+impl std::ops::Deref for OwnedValueVec { -+ type Target = wasm_val_vec_t; -+ fn deref(&self) -> &Self::Target { &self.0 } -+} -+impl std::ops::DerefMut for OwnedValueVec { -+ fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } -+} -+impl Drop for OwnedValueVec { -+ fn drop(&mut self) { unsafe { wasm_val_vec_delete(&mut self.0) } } -+} -+// C vectors adopt reference wrappers; borrow a Function only through a copy. -+pub(super) fn own_call_parameter(value: wasm_val_t) -> wasm_val_t { -+ if value.kind == wasm_valkind_enum_WASM_FUNCREF as _ { -+ let mut owned = value; -+ unsafe { wasm_val_copy(&mut owned, &value) }; -+ owned -+ } else { value } -+} -+ -+// Returned Function aliases must outlive the temporary native result vector. -+pub(super) fn into_call_value(mut value: wasm_val_t, expected: wasmer_types::Type, store: &mut impl AsStoreMut) -> Value { -+ // The pinned C API reports returned function references as generic refs. -+ if expected == wasmer_types::Type::FuncRef { -+ value.kind = wasm_valkind_enum_WASM_FUNCREF as _; -+ let pointer = unsafe { value.of.ref_ }; -+ if pointer.is_null() { return Value::FuncRef(None); } -+ let owned = unsafe { wasm_ref_copy(pointer) }; -+ store.as_store_mut().inner.store.as_v8_mut().owned_refs -+ .push(super::super::store::OwnedRef(owned)); -+ value.of.ref_ = owned; -+ } -+ value.into_wv() -+} - macro_rules! impl_native_traits { - ( $( $x:ident ),* ) => { - #[allow(unused_parens, non_snake_case)] -@@ -37,28 +72,31 @@ - vec![ $( { - let raw = $x.to_native().into_raw(store); - let value = Value::from_raw(&mut store, <$x::Native as NativeWasmType>::WASM_TYPE, raw); -- value.into_cv() -+ own_call_parameter(value.into_cv()) - } ),* ] - }; - -- let mut results = { -+ let mut results = OwnedValueVec({ - unsafe { - let mut ret = std::mem::zeroed(); - wasm_val_vec_new_uninitialized(&mut ret, Rets::wasm_types().len()); - ret - } -- }; -+ }); - - - let func = unsafe { wasm_extern_as_func(self.func.to_vm_extern().unwrap_v_8()) }; - - let mut params = unsafe {std::mem::zeroed()}; - unsafe {wasm_val_vec_new(&mut params, params_list.len(), params_list.as_ptr())}; -+ let params = OwnedValueVec(params); - - let mut trap; - - loop { -- trap = unsafe { wasm_func_call(func, ¶ms, &mut results) }; -+ store.as_store_ref().inner.store.as_v8().ensure_running()?; -+ let raw_trap = unsafe { wasm_func_call(func, &*params, &mut *results) }; -+ trap = (!raw_trap.is_null()).then(|| Trap::from(raw_trap)); - - let store_mut = store.as_store_mut(); - if let Some(callback) = store_mut.inner.on_called.take() { -@@ -74,12 +112,10 @@ - - - -- if !trap.is_null() { -- unsafe { -- let trap: Trap = trap.into(); -- return Err(RuntimeError::from(trap)); -- } -- } -+ store.as_store_ref().inner.store.as_v8().ensure_running()?; -+ if let Some(trap) = trap { -+ return Err(RuntimeError::from(trap)); -+ } - - unsafe { - let mut rets_list_array = Rets::empty_array(); -@@ -89,13 +125,13 @@ - 0 => {}, - 1 => { - let val = (*results.data.wrapping_add(0)).clone(); -- let val = val.into_wv(); -+ let val = into_call_value(val, Rets::wasm_types()[0], &mut store); - *mut_rets = val.as_raw(&mut store); - } - _n => { - for (i, ret_type) in Rets::wasm_types().iter().enumerate() { - let val = (*results.data.wrapping_add(i)).clone(); -- let val = val.into_wv(); -+ let val = into_call_value(val, *ret_type, &mut store); - let slot = mut_rets.add(i); - *slot = val.as_raw(&mut store); - } ---- a/src/backend/v8/entities/global.rs -+++ b/src/backend/v8/entities/global.rs -@@ -60,9 +60,10 @@ - let v8_global_type = - unsafe { wasm_globaltype_new(wasm_valtype_new(v8_type), v8_mutability) }; - -- Ok(Self { -- handle: unsafe { wasm_global_new(v8_store.inner, v8_global_type, &v8_value) }, -- }) -+ let handle = unsafe { wasm_global_new(v8_store.inner, v8_global_type, &v8_value) }; -+ unsafe { bindings::wasm_globaltype_delete(v8_global_type) }; -+ store.inner.store.as_v8_mut().owned_refs.push(super::store::OwnedRef(unsafe { bindings::wasm_global_as_ref(handle) })); -+ Ok(Self { handle }) - } - - pub fn ty(&self, store: &impl AsStoreRef) -> GlobalType { -@@ -73,14 +74,16 @@ - let valtype = unsafe { wasm_globaltype_content(r#type) }; - let wasmer_type = valtype.into_wt(); - -- GlobalType::new( -+ let result = GlobalType::new( - wasmer_type, - if mutability == bindings::wasm_mutability_enum_WASM_VAR as u8 { - Mutability::Var - } else { - Mutability::Const - }, -- ) -+ ); -+ unsafe { bindings::wasm_globaltype_delete(r#type) }; -+ result - } - - pub fn get(&self, store: &mut impl AsStoreMut) -> Value { ---- a/src/backend/v8/entities/instance.rs -+++ b/src/backend/v8/entities/instance.rs -@@ -26,20 +26,15 @@ - let externs: Vec<_> = externs.into_iter().map(|v| v.unwrap_v_8()).collect(); - - let instance = unsafe { -- let mut imports = unsafe { -- let mut vec = Default::default(); -- wasm_extern_vec_new(&mut vec, externs.len(), externs.as_ptr()); -- vec -- }; -- -- std::mem::forget(externs); -- -- wasm_instance_new( -- store, -- wasm_module_obtain(store, module), -- &mut imports, -- &mut trap, -- ) -+ // The vector consumes C wrappers: copy borrowed reusable imports. -+ let copied: Vec<_> = externs.iter().map(|p| wasm_extern_copy(*p)).collect(); -+ let mut imports = wasm_extern_vec_t::default(); -+ wasm_extern_vec_new(&mut imports, copied.len(), copied.as_ptr()); -+ let local_module = wasm_module_obtain(store, module); -+ let instance = wasm_instance_new(store, local_module, &imports, &mut trap); -+ wasm_module_delete(local_module); -+ wasm_extern_vec_delete(&mut imports); -+ instance - }; - - if instance.is_null() { -@@ -65,7 +60,7 @@ - }; - - let exports_ty = module.exports().collect::>(); -- exports_ty -+ let result = exports_ty - .iter() - .zip(wasm_exports.iter()) - .map(|(export_type, wasm_export)| { -@@ -77,14 +72,14 @@ - let extern_ = Extern::from_vm_extern(&mut store, VMExtern::V8(*wasm_export)); - (name.to_string(), extern_) - }) -- .collect::() -+ .collect::(); -+ store.as_store_mut().inner.store.as_v8_mut().owned_exports -+ .push(super::store::OwnedExports(exports)); -+ result - } - } --impl Drop for InstanceHandle { -- fn drop(&mut self) { -- unsafe { wasm_instance_delete(self.0) } -- } --} -+// Store owns the C wrapper; a Rust alias may outlive its Store. -+ - - #[derive(Clone, PartialEq, Eq)] - /// A WebAssembly `instance` in the `v8` runtime. -@@ -117,6 +112,7 @@ - ) -> Result<(Self, Exports), InstantiationError> { - check_isolate(store); - let store_ref = store.as_store_ref(); -+ store_ref.inner.store.as_v8().ensure_running().map_err(InstantiationError::Start)?; - let externs: Vec = externs - .iter() - .map(|extern_| extern_.to_vm_extern()) -@@ -126,6 +122,10 @@ - module.as_v8().handle.v8_shared_module_handle, - externs, - )?; -+ // C instance wrappers belong to the Store, like their export aliases. -+ let owned = unsafe { wasm_instance_as_ref(instance.0) }; -+ store.as_store_mut().inner.store.as_v8_mut().owned_refs.push(super::store::OwnedRef(owned)); -+ store.as_store_ref().inner.store.as_v8().ensure_running().map_err(InstantiationError::Start)?; - let exports = instance.get_exports(store, module); - - Ok(( ---- a/src/backend/v8/entities/memory/mod.rs -+++ b/src/backend/v8/entities/memory/mod.rs -@@ -53,14 +53,12 @@ - }); - } - -- let limits = Box::into_raw(Box::new(wasm_limits_t { -- min, -- max, -- shared: ty.shared, -- })); -- -- let memorytype = unsafe { wasm_memorytype_new(limits) }; -+ let limits = wasm_limits_t { min, max, shared: ty.shared }; -+ -+ let memorytype = unsafe { wasm_memorytype_new(&limits) }; - let c_memory = unsafe { wasm_memory_new(v8_store.inner, memorytype) }; -+ unsafe { wasm_memorytype_delete(memorytype) }; -+ store_mut.inner.store.as_v8_mut().owned_refs.push(super::store::OwnedRef(unsafe { wasm_memory_as_ref(c_memory) })); - - Ok(Self { - handle: VMMemory(c_memory), -@@ -84,7 +82,7 @@ - let memory_type: *mut wasm_memorytype_t = unsafe { wasm_memory_type(self.handle.0) }; - let limits: *const wasm_limits_t = unsafe { wasm_memorytype_limits(memory_type) }; - -- MemoryType { -+ let result = MemoryType { - shared: unsafe { (*limits).shared }, - minimum: unsafe { wasmer_types::Pages((*limits).min) }, - maximum: unsafe { -@@ -94,7 +92,9 @@ - Some(wasmer_types::Pages((*limits).max)) - } - }, -- } -+ }; -+ unsafe { wasm_memorytype_delete(memory_type) }; -+ result - } - - pub fn size(&self, store: &impl AsStoreRef) -> Pages { -@@ -198,6 +198,8 @@ - )); - } - -+ // Own the temporary wrapper on success and every early-return path. -+ let _copied_owner = super::store::OwnedRef(unsafe { wasm_memory_as_ref(copied_memory) }); - let copied = Self { - handle: VMMemory(copied_memory), - }; -@@ -226,9 +228,9 @@ - .copy_to_memory(src_view.data_size(), &dst_view) - .map_err(|err| MemoryError::Generic(format!("failed to copy V8 memory: {err}")))?; - -- Ok(SharedMemory::new(crate::vm::VMSharedMemory::V8( -- copied.handle.as_shared()?, -- ))) -+ let shared = copied.handle.as_shared(super::store::interrupt::MemoryInterrupt::detached())?; -+ let ops = shared.1.clone(); -+ Ok(SharedMemory::new_with_ops(crate::vm::VMSharedMemory::V8(shared), ops)) - } - - pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { -@@ -238,9 +240,11 @@ - - pub fn as_shared(&self, store: &impl AsStoreRef) -> Result { - check_isolate(store); -- Ok(SharedMemory::new(crate::vm::VMSharedMemory::V8( -- self.handle.as_shared()?, -- ))) -+ let store = store.as_store_ref(); -+ let v8_store = store.inner.store.as_v8(); -+ let shared = self.handle.as_shared(v8_store.shared_memory_ops(self.handle.0))?; -+ let ops = shared.1.clone(); -+ Ok(SharedMemory::new_with_ops(crate::vm::VMSharedMemory::V8(shared), ops)) - } - } - ---- a/src/backend/v8/entities/module.rs -+++ b/src/backend/v8/entities/module.rs -@@ -15,22 +15,84 @@ - }; - use wasmparser::{Parser, Payload}; - -+#[cfg(all(test, windows))] -+mod oliphaunt_contract { -+ use super::*; -+ -+ const GUEST: &[u8] = b"\0asm\x01\0\0\0\x01\x05\x01\x60\0\x01\x7f\x03\x02\x01\0\x07\x0a\x01\x06answer\0\0\x0a\x06\x01\x04\0\x41\x2a\x0b"; -+ -+ fn native_offset(cache: &[u8]) -> usize { -+ assert!(cache[0] < 128, "the fixture has a one-byte wire length"); -+ 1 + usize::from(cache[0]) -+ } -+ -+ fn encoded(info: &V8ModuleInfo) -> Vec { -+ rkyv::to_bytes::(info).unwrap().to_vec() -+ } -+ -+ #[test] -+ fn rejects_wire_only_and_incompatible_caches_without_compilation() { -+ let engine: crate::Engine = crate::v8::V8::try_new().unwrap().into(); -+ let module = Module::from_binary(&engine, GUEST).unwrap(); -+ let mut info = module.info.clone(); -+ let offset = native_offset(&info.v8_module_bytes[0]); -+ info.v8_module_bytes[0].truncate(offset); -+ let error = unsafe { crate::Module::deserialize(&engine, encoded(&info)) }.unwrap_err(); -+ assert!(error.to_string().contains("guest compilation is disabled")); -+ -+ let mut info = module.info.clone(); -+ // Deliberately incompatible test bytes, never a producer workaround. -+ info.v8_module_bytes[0][offset + 8..offset + 12].copy_from_slice(&u32::MAX.to_le_bytes()); -+ assert!(unsafe { crate::Module::deserialize(&engine, encoded(&info)) }.is_err()); -+ } -+ -+ #[test] -+ fn skips_an_incompatible_cpu_profile_and_executes_the_next_native_cache() { -+ let engine: crate::Engine = crate::v8::V8::try_new().unwrap().into(); -+ let module = Module::from_binary(&engine, GUEST).unwrap(); -+ let mut info = module.info.clone(); -+ let original = info.v8_module_bytes[0].clone(); -+ let offset = native_offset(&original); -+ assert_eq!(u32::from_le_bytes(original[offset + 8..offset + 12].try_into().unwrap()), 0x1000e, "the native runner must qualify the CET profile"); -+ info.v8_module_bytes[0][offset + 8..offset + 12].copy_from_slice(&u32::MAX.to_le_bytes()); -+ info.v8_module_bytes.push(original); -+ let loaded = unsafe { crate::Module::deserialize(&engine, encoded(&info)) }.unwrap(); -+ let mut store = crate::Store::new(engine); -+ let instance = crate::Instance::new(&mut store, &loaded, &crate::imports! {}).unwrap(); -+ let answer = instance.exports.get_typed_function::<(), i32>(&store, "answer").unwrap(); -+ assert_eq!(answer.call(&mut store).unwrap(), 42); -+ } -+ -+ #[test] -+ fn rejects_variants_for_another_guest() { -+ let engine: crate::Engine = crate::v8::V8::try_new().unwrap().into(); -+ let module = Module::from_binary(&engine, GUEST).unwrap(); -+ let mut different = GUEST.to_vec(); -+ let position = different.len() - 2; -+ different[position] = 43; -+ let other = Module::from_binary(&engine, &different).unwrap(); -+ assert!(module.serialize_with_native_variants(&other.info.v8_module_bytes).is_err()); -+ } -+} -+ - #[derive(Debug)] - pub(crate) struct ModuleHandle { - pub(crate) v8_shared_module_handle: *mut wasm_shared_module_t, -- pub(crate) orig_store: Store, -+ pub(crate) engine: crate::Engine, - } - - impl PartialEq for ModuleHandle { - fn eq(&self, other: &Self) -> bool { -+ // Compare in one isolate; each obtained wrapper is unique. - unsafe { -- wasm_module_same( -- wasm_module_obtain(self.orig_store.as_v8().inner, self.v8_shared_module_handle), -- wasm_module_obtain( -- other.orig_store.as_v8().inner, -- other.v8_shared_module_handle, -- ), -- ) -+ let transient = Store::new(self.engine.clone()); -+ let store = transient.as_v8().inner; -+ let left = wasm_module_obtain(store, self.v8_shared_module_handle); -+ let right = wasm_module_obtain(store, other.v8_shared_module_handle); -+ let same = wasm_module_same(left, right); -+ wasm_module_delete(right); -+ wasm_module_delete(left); -+ same - } - } - } -@@ -46,7 +108,7 @@ - - let engine = engine.as_engine_ref().engine().clone(); - let store = Store::new(engine.clone()); -- let engine = engine.as_v8().inner.engine; -+ - - let inner = unsafe { wasm_module_new(store.as_v8().inner, &bytes as *const _) }; - -@@ -56,7 +118,11 @@ - )); - } - -- let inner = unsafe { wasm_module_share(inner) }; -+ let inner = unsafe { -+ let shared = wasm_module_share(inner); -+ wasm_module_delete(inner); -+ shared -+ }; - - if inner.is_null() { - return Err(CompileError::Validate( -@@ -66,11 +132,27 @@ - - Ok(Self { - v8_shared_module_handle: inner, -- orig_store: store, -+ engine, - }) - } - - fn deserialize(engine: &impl AsEngineRef, binary: &[u8]) -> Result { -+ // The pinned C API compiles wire-only serialized modules. Deserialization -+ // of an AOT artifact must reject that case before entering V8. -+ let mut wire_len = 0u64; -+ let mut prefix_len = None; -+ for (index, byte) in binary.iter().copied().take(10).enumerate() { -+ if index == 9 && byte > 1 { break; } -+ wire_len |= u64::from(byte & 0x7f) << (index * 7); -+ if byte & 0x80 == 0 { prefix_len = Some(index + 1); break; } -+ } -+ let native_start = prefix_len -+ .and_then(|n| usize::try_from(wire_len).ok().and_then(|wire| n.checked_add(wire))); -+ if native_start.and_then(|n| binary.len().checked_sub(n)).is_none_or(|n| n < 20) { -+ return Err(CompileError::Validate( -+ "V8 AOT artifact has no complete native payload; guest compilation is disabled".into(), -+ )); -+ } - let bytes = wasm_byte_vec_t { - size: binary.len(), - data: binary.as_ptr() as _, -@@ -86,7 +168,11 @@ - )); - } - -- let inner = unsafe { wasm_module_share(inner) }; -+ let inner = unsafe { -+ let shared = wasm_module_share(inner); -+ wasm_module_delete(inner); -+ shared -+ }; - - if inner.is_null() { - return Err(CompileError::Validate( -@@ -96,15 +182,16 @@ - - Ok(Self { - v8_shared_module_handle: inner, -- orig_store: store, -+ engine, - }) - } - - #[tracing::instrument(skip(self))] - pub fn serialize(&self) -> Result, SerializeError> { -+ let transient = Store::new(self.engine.clone()); - let handle = unsafe { - wasm_module_obtain( -- self.orig_store.as_v8().inner, -+ transient.as_v8().inner, - self.v8_shared_module_handle as *const _, - ) - }; -@@ -147,7 +234,7 @@ - // (store) than the one V8 associated with the module, which can crash. - imports: Vec, - exports: Vec, -- v8_module_bytes: Vec, -+ v8_module_bytes: Vec>, - // Copy of the custom sections, since the current API cannot retrieve them - // later from the handle. Use a BTreeMap to keep entries in deterministic order, - // producing stable serialized bytes. -@@ -205,7 +292,7 @@ - name: info.name, - imports, - exports, -- v8_module_bytes, -+ v8_module_bytes: vec![v8_module_bytes], - custom_sections, - }, - }) -@@ -242,6 +329,38 @@ - Ok(info.into()) - } - -+ /// Serialize this module with additional producer-built native CPU profiles. -+ /// Every profile must contain the same WebAssembly wire bytes. -+ pub fn serialize_with_native_variants(&self, variants: &[Vec]) -> Result { -+ fn wire(bytes: &[u8]) -> Option<&[u8]> { -+ let mut length = 0u64; -+ for (index, byte) in bytes.iter().copied().take(10).enumerate() { -+ if index == 9 && byte > 1 { return None; } -+ length |= u64::from(byte & 0x7f) << (index * 7); -+ if byte & 0x80 == 0 { -+ let end = (index + 1).checked_add(usize::try_from(length).ok()?)?; -+ if bytes.len().checked_sub(end)? < 20 { return None; } -+ return bytes.get(index + 1..end); -+ } -+ } -+ None -+ } -+ let original = self.info.v8_module_bytes.first() -+ .ok_or_else(|| SerializeError::Generic("module has no native cache".into()))?; -+ let expected = wire(original) -+ .ok_or_else(|| SerializeError::Generic("module has no complete native cache".into()))?; -+ let mut info = self.info.clone(); -+ for variant in variants { -+ if wire(variant) != Some(expected) { -+ return Err(SerializeError::Generic("native cache wire identity mismatch".into())); -+ } -+ info.v8_module_bytes.push(variant.clone()); -+ } -+ rkyv::to_bytes::(&info) -+ .map(|bytes| bytes.to_vec().into()) -+ .map_err(|error| SerializeError::Generic(format!("{error:?}"))) -+ } -+ - #[allow(clippy::arc_with_non_send_sync)] - pub unsafe fn deserialize_unchecked( - engine: &impl AsEngineRef, -@@ -252,7 +371,17 @@ - let info = rkyv::access::(&binary) - .and_then(rkyv::deserialize::) - .map_err(|err| DeserializeError::CorruptedBinary(format!("{err:?}")))?; -- let module = ModuleHandle::deserialize(engine, &info.v8_module_bytes)?; -+ // Native caches retain their original CPU/flag headers. Try the producer's -+ // profiles without compiling a guest when no cache is compatible. -+ let mut failures = Vec::new(); -+ let module = info.v8_module_bytes.iter().find_map(|cache| { -+ match ModuleHandle::deserialize(engine, cache) { -+ Ok(module) => Some(module), -+ Err(error) => { failures.push(error.to_string()); None } -+ } -+ }).ok_or_else(|| DeserializeError::CorruptedBinary(format!( -+ "no compatible V8 native cache: {}", failures.join("; ") -+ )))?; - - Ok(Self { - handle: Arc::new(module), ---- a/src/backend/v8/entities/store/interrupt.rs -+++ b/src/backend/v8/entities/store/interrupt.rs -@@ -0,0 +1,128 @@ -+use std::{cell::Cell, ffi::c_void, sync::{Arc, Mutex, Weak, atomic::{AtomicBool, Ordering}}}; -+ -+use crate::{error::AtomicsError, location::SharedMemoryOps}; -+use crate::backend::v8::bindings::*; -+ -+#[cfg(not(windows))] -+unsafe extern "C" { -+ #[link_name = "_ZN2v87Isolate10GetCurrentEv"] -+ fn current_isolate() -> *mut c_void; -+ #[link_name = "_ZN2v87Isolate18TerminateExecutionEv"] -+ fn terminate_execution(isolate: *mut c_void); -+} -+#[cfg(windows)] -+use crate::backend::v8::bindings::{ -+ oliphaunt_v8_current_isolate as current_isolate, -+ oliphaunt_v8_terminate_execution as terminate_execution, -+}; -+ -+thread_local! { static CAPTURED: Cell = const { Cell::new(0) }; } -+ -+unsafe extern "C" fn capture( -+ _args: *const wasm_val_vec_t, -+ _results: *mut wasm_val_vec_t, -+) -> *mut wasm_trap_t { -+ // The pinned C API enters the Store's isolate before a direct host call. -+ CAPTURED.with(|slot| slot.set(unsafe { current_isolate() } as usize)); -+ std::ptr::null_mut() -+} -+ -+pub(super) struct StoreInterrupt { isolate: Mutex>, terminated: AtomicBool } -+ -+impl StoreInterrupt { -+ pub(super) fn new() -> Arc { Arc::new(Self { isolate: Mutex::new(None), terminated: AtomicBool::new(false) }) } -+ -+ pub(super) fn capture(&self, store: *mut wasm_store_t) { -+ unsafe { -+ let mut params = std::mem::zeroed(); -+ let mut results = std::mem::zeroed(); -+ let ty = wasm_functype_new(&mut params, &mut results); -+ assert!(!ty.is_null(), "cannot create V8 interrupt capture type"); -+ let function = wasm_func_new(store, ty, Some(capture)); -+ wasm_functype_delete(ty); -+ assert!(!function.is_null(), "cannot create V8 interrupt capture function"); -+ let owner = super::OwnedRef(wasm_func_as_ref(function)); -+ let mut args = std::mem::zeroed(); -+ let mut results = std::mem::zeroed(); -+ CAPTURED.with(|slot| slot.set(0)); -+ let trap = wasm_func_call(function, &mut args, &mut results); -+ let pointer = CAPTURED.with(Cell::get); -+ if !trap.is_null() { wasm_trap_delete(trap); } -+ drop(owner); -+ assert!(trap.is_null() && pointer != 0, "cannot capture V8 Store interrupt"); -+ *self.isolate.lock().unwrap_or_else(|e| e.into_inner()) = Some(pointer); -+ } -+ } -+ -+ pub(super) fn is_terminated(&self) -> bool { self.terminated.load(Ordering::Acquire) } -+ -+ fn terminate(&self) { -+ self.terminated.store(true, Ordering::Release); -+ // Keep the lock through termination. Store teardown takes this same -+ // lock before retiring the pointer and deleting the native Store. -+ let pointer = self.isolate.lock().unwrap_or_else(|e| e.into_inner()); -+ if let Some(pointer) = *pointer { -+ unsafe { terminate_execution(pointer as *mut c_void) }; -+ } -+ } -+ -+ pub(super) fn retire(&self) { -+ *self.isolate.lock().unwrap_or_else(|e| e.into_inner()) = None; -+ } -+} -+ -+#[derive(Default)] -+struct MemoryState { -+ terminal: bool, -+ stores: Vec>, -+} -+ -+pub(crate) struct MemoryInterrupt(Mutex); -+ -+impl MemoryInterrupt { -+ pub(crate) fn detached() -> Arc { Arc::new(Self(Mutex::new(MemoryState::default()))) } -+ -+ pub(super) fn new(store: &Arc) -> Arc { -+ Arc::new(Self(Mutex::new(MemoryState { -+ terminal: false, stores: vec![Arc::downgrade(store)], -+ }))) -+ } -+ -+ pub(crate) fn attach( -+ self: &Arc, -+ store: &mut super::Store, -+ memory: *mut wasm_shared_memory_t, -+ ) -> Result<*mut wasm_memory_t, crate::MemoryError> { -+ let mut state = self.0.lock().unwrap_or_else(|e| e.into_inner()); -+ if state.terminal { -+ return Err(crate::MemoryError::Generic( -+ "cannot attach V8 shared memory after execution contexts are terminal".to_owned(), -+ )); -+ } -+ let handle = unsafe { wasm_memory_obtain(store.inner, memory) }; -+ if handle.is_null() { -+ return Err(crate::MemoryError::Generic("cannot obtain V8 shared memory".to_owned())); -+ } -+ state.stores.retain(|entry| entry.strong_count() != 0); -+ let interrupt = Arc::downgrade(&store.interrupt); -+ if !state.stores.iter().any(|entry| Weak::ptr_eq(entry, &interrupt)) { -+ state.stores.push(interrupt); -+ } -+ store.remember_shared_memory(handle, self.clone()); -+ Ok(handle) -+ } -+} -+ -+impl SharedMemoryOps for MemoryInterrupt { -+ fn terminate_execution_contexts(&self) -> Result<(), AtomicsError> { -+ let mut state = self.0.lock().unwrap_or_else(|e| e.into_inner()); -+ state.terminal = true; -+ state.stores.retain(|entry| { -+ if let Some(store) = entry.upgrade() { -+ store.terminate(); -+ true -+ } else { false } -+ }); -+ Ok(()) -+ } -+} ---- a/src/backend/v8/entities/store/mod.rs -+++ b/src/backend/v8/entities/store/mod.rs -@@ -1,5 +1,5 @@ - //! Data types, functions and traits for `v8` runtime's `Store` implementation. --use std::thread::ThreadId; -+use std::{cell::RefCell, sync::Arc, thread::ThreadId}; - - use crate::{ - AsStoreRef, BackendStore, StoreRef, -@@ -7,14 +7,34 @@ - engine::{AsEngineRef, Engine, EngineRef}, - }; - -+pub(crate) mod interrupt; - mod obj; - pub use obj::*; -+ -+#[derive(Debug)] -+pub(crate) struct OwnedExports(pub(crate) crate::backend::v8::bindings::wasm_extern_vec_t); -+ -+impl Drop for OwnedExports { -+ fn drop(&mut self) { -+ unsafe { crate::backend::v8::bindings::wasm_extern_vec_delete(&mut self.0) }; -+ } -+} -+ -+#[derive(Debug)] -+pub(crate) struct OwnedRef(pub(crate) *mut crate::backend::v8::bindings::wasm_ref_t); -+impl Drop for OwnedRef { -+ fn drop(&mut self) { unsafe { crate::backend::v8::bindings::wasm_ref_delete(self.0) }; } -+} - - /// A WebAssembly `store` in the `v8` runtime. - pub struct Store { - pub(crate) engine: Engine, - pub(crate) inner: *mut wasm_store_t, - pub(crate) thread_id: ThreadId, -+ pub(crate) owned_exports: Vec, -+ pub(crate) owned_refs: Vec, -+ interrupt: Arc, -+ shared_memories: RefCell)>>, - } - - impl std::fmt::Debug for Store { -@@ -29,11 +49,46 @@ - pub(crate) fn new(engine: crate::engine::Engine) -> Self { - let inner: *mut wasm_store_t = unsafe { wasm_store_new(engine.as_v8().inner.engine) }; - let thread_id = std::thread::current().id(); -- Self { -+ let store = Self { - inner, - engine, - thread_id, -+ owned_exports: Vec::new(), -+ owned_refs: Vec::new(), -+ interrupt: interrupt::StoreInterrupt::new(), -+ shared_memories: RefCell::new(Vec::new()), -+ }; -+ store.interrupt.capture(inner); -+ store -+ } -+ -+ pub(crate) fn shared_memory_ops(&self, memory: *mut crate::backend::v8::bindings::wasm_memory_t) -> Arc { -+ use crate::backend::v8::bindings::*; -+ let mut memories = self.shared_memories.borrow_mut(); -+ if let Some((_, ops)) = memories.iter().find(|(entry, _)| unsafe { -+ wasm_memory_same(wasm_ref_as_memory(entry.0), memory) -+ }) { -+ return ops.clone(); - } -+ let ops = interrupt::MemoryInterrupt::new(&self.interrupt); -+ memories.push((OwnedRef(unsafe { wasm_memory_as_ref(wasm_memory_copy(memory)) }), ops.clone())); -+ ops -+ } -+ -+ pub(crate) fn remember_shared_memory(&self, memory: *mut crate::backend::v8::bindings::wasm_memory_t, ops: Arc) { -+ use crate::backend::v8::bindings::*; -+ let mut memories = self.shared_memories.borrow_mut(); -+ if !memories.iter().any(|(entry, _)| unsafe { -+ wasm_memory_same(wasm_ref_as_memory(entry.0), memory) -+ }) { -+ memories.push((OwnedRef(unsafe { wasm_memory_as_ref(wasm_memory_copy(memory)) }), ops)); -+ } -+ } -+ -+ pub(crate) fn ensure_running(&self) -> Result<(), crate::RuntimeError> { -+ if self.interrupt.is_terminated() { -+ Err(crate::RuntimeError::new("V8 execution context terminated")) -+ } else { Ok(()) } - } - - pub(crate) fn engine(&self) -> &Engine { -@@ -47,6 +102,12 @@ - - impl Drop for Store { - fn drop(&mut self) { -+ // These vectors uniquely own the export wrappers. Rust export clones -+ // borrow them through their Store, so retain them until Store teardown. -+ self.interrupt.retire(); -+ self.shared_memories.get_mut().clear(); -+ self.owned_exports.clear(); -+ self.owned_refs.clear(); - unsafe { wasm_store_delete(self.inner) } - } - } ---- a/src/backend/v8/entities/table.rs -+++ b/src/backend/v8/entities/table.rs -@@ -26,13 +26,12 @@ - pub(crate) fn type_to_v8(ty: TableType) -> *mut wasm_tabletype_t { - let valtype = unsafe { wasm_valtype_new(ty.ty.into_ct()) }; - -- let limits = Box::into_raw(Box::new(wasm_limits_t { -+ let limits = wasm_limits_t { - min: ty.minimum, - max: ty.maximum.unwrap_or(wasm_limits_max_default), - shared: false, -- })); -- -- unsafe { wasm_tabletype_new(valtype, limits) } -+ }; -+ unsafe { wasm_tabletype_new(valtype, &limits) } - } - - pub fn new( -@@ -49,9 +48,10 @@ - let wasm_tablety = Self::type_to_v8(ty); - let init: wasm_val_t = init.into_cv(); - -- Ok(Self { -- handle: unsafe { wasm_table_new(v8_store.inner, wasm_tablety, init.of.ref_) }, -- }) -+ let handle = unsafe { wasm_table_new(v8_store.inner, wasm_tablety, init.of.ref_) }; -+ unsafe { wasm_tabletype_delete(wasm_tablety) }; -+ store_mut.inner.store.as_v8_mut().owned_refs.push(super::store::OwnedRef(unsafe { wasm_table_as_ref(handle) })); -+ Ok(Self { handle }) - } - - pub fn to_vm_extern(&self) -> VMExtern { -@@ -65,10 +65,10 @@ - - let table_type: *mut wasm_tabletype_t = unsafe { wasm_table_type(self.handle) }; - let table_limits = unsafe { wasm_tabletype_limits(table_type) }; -- let table_type = unsafe { wasm_tabletype_element(table_type) }; -- -- TableType { -- ty: table_type.into_wt(), -+ let element_type = unsafe { wasm_tabletype_element(table_type) }; -+ -+ let result = TableType { -+ ty: element_type.into_wt(), - minimum: unsafe { (*table_limits).min }, - maximum: unsafe { - if (*table_limits).max == wasm_limits_max_default { -@@ -78,7 +78,9 @@ - } - }, - readonly: false, -- } -+ }; -+ unsafe { wasm_tabletype_delete(table_type) }; -+ result - } - - pub fn get(&self, store: &mut impl AsStoreMut, index: u32) -> Option { ---- a/src/backend/v8/entities/tag.rs -+++ b/src/backend/v8/entities/tag.rs -@@ -21,6 +21,9 @@ - - unsafe impl Send for Tag {} - unsafe impl Sync for Tag {} -+ -+// C bridge uses ExternType's virtual implementation destructor. -+use crate::backend::v8::bindings::oliphaunt_externtype_delete; - - impl Tag { - pub fn new>>(store: &mut impl AsStoreMut, params: P) -> Self { -@@ -49,6 +52,8 @@ - } - - let handle = unsafe { wasm_tag_new(v8_store.inner, tag_type) }; -+ unsafe { oliphaunt_externtype_delete(tag_type) }; -+ store_mut.inner.store.as_v8_mut().owned_refs.push(super::store::OwnedRef(unsafe { wasm_tag_as_ref(handle) })); - if handle.is_null() { - panic!("failed to create new tag: returned tag is null"); - } -@@ -69,6 +74,7 @@ - res - }; - -+ unsafe { oliphaunt_externtype_delete(type_) }; - TagType::new(wasmer_types::TagKind::Exception, params) - } - ---- a/src/backend/v8/error.rs -+++ b/src/backend/v8/error.rs -@@ -1,7 +1,4 @@ --use std::{ -- error::Error, -- ffi::{CStr, c_char}, --}; -+use std::error::Error; - - use crate::{ - AsStoreMut, -@@ -11,7 +8,7 @@ - #[derive(Debug)] - enum InnerTrap { - User(Box), -- CApi(*mut wasm_trap_t), -+ CApi(String), - } - - /// A struct representing a Trap -@@ -69,7 +66,14 @@ - #[allow(clippy::unnecessary_mut_passed)] - pub unsafe fn into_wasm_trap(self, store: &mut impl AsStoreMut) -> *mut wasm_trap_t { - match self.inner { -- InnerTrap::CApi(t) => t, -+ InnerTrap::CApi(message) => { -+ let mut data = wasm_byte_vec_t::default(); -+ unsafe { wasm_byte_vec_new(&mut data, message.len(), message.as_ptr() as _) }; -+ let store = store.as_store_mut(); -+ let trap = unsafe { wasm_trap_new(store.inner.store.as_v8().inner, &data) }; -+ unsafe { wasm_byte_vec_delete(&mut data) }; -+ trap -+ } - InnerTrap::User(err) => { - let err_ptr = Box::leak(Box::new(err)); - // UNSAFE: The boxed error is propagated by passing the string -@@ -90,13 +94,16 @@ - - impl From<*mut wasm_trap_t> for Trap { - fn from(value: *mut wasm_trap_t) -> Self { -+ // Snapshot the message while the Store is live. RuntimeError can outlive -+ // its Store, so it must not retain an isolate-owned C API trap handle. - let message = unsafe { -- let mut message = unsafe { std::mem::zeroed() }; -- unsafe { wasm_trap_message(value, &mut message) }; -- -- CStr::from_ptr(message.data as *const c_char) -- .to_str() -- .unwrap() -+ let mut bytes = wasm_byte_vec_t::default(); -+ wasm_trap_message(value, &mut bytes); -+ let text = String::from_utf8_lossy(std::slice::from_raw_parts( -+ bytes.data.cast::(), bytes.size)).trim_end_matches('\0').to_owned(); -+ wasm_byte_vec_delete(&mut bytes); -+ wasm_trap_delete(value); -+ text - }; - - // User traps are encoded by `into_wasm_trap` as a leaked `Box` pointer -@@ -104,8 +111,8 @@ - if let Some((_, ptr_str)) = message.split_once("🐛") { - let ptr: Box = unsafe { - let r = ptr_str.trim_start_matches("0x"); -- std::ptr::read(usize::from_str_radix(r, 16).unwrap() -- as *const Box) -+ *Box::from_raw(usize::from_str_radix(r, 16).unwrap() -+ as *mut Box) - }; - - Self { -@@ -113,7 +120,7 @@ - } - } else { - Self { -- inner: InnerTrap::CApi(value), -+ inner: InnerTrap::CApi(message), - } - } - } -@@ -132,18 +139,7 @@ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match &self.inner { - InnerTrap::User(e) => write!(f, "{e}"), -- InnerTrap::CApi(value) => { -- // let message: wasm_message_t; -- // wasm_trap_message(value, &mut message); -- let mut out = unsafe { -- let mut vec: wasm_byte_vec_t = Default::default(); -- wasm_byte_vec_new_empty(&mut vec); -- &mut vec as *mut _ -- }; -- unsafe { wasm_trap_message(*value, out) }; -- let cstr = unsafe { CStr::from_ptr((*out).data) }; -- write!(f, "wasm-c-api trap: {}", cstr.to_str().unwrap()) -- } -+ InnerTrap::CApi(message) => write!(f, "wasm-c-api trap: {message}"), - } - } - } ---- a/src/backend/v8/vm/mod.rs -+++ b/src/backend/v8/vm/mod.rs -@@ -154,7 +154,7 @@ - #[derive(Debug, Clone)] - pub struct VMMemory(pub(super) *mut wasm_memory_t); - struct SharedMemoryPointer(*mut wasm_shared_memory_t); --pub struct VMSharedMemory(Arc); -+pub struct VMSharedMemory(Arc, pub(crate) Arc); - pub(crate) type VMExternMemory = *mut wasm_memory_t; - - impl SharedMemoryPointer { -@@ -182,7 +182,7 @@ - - impl VMMemory { - /// Created a shared detached memory that can be attached in another Store. -- pub(crate) fn as_shared(&self) -> Result { -+ pub(crate) fn as_shared(&self, ops: Arc) -> Result { - let memory_type = unsafe { wasm_memory_type(self.0) }; - let limits = unsafe { wasm_memorytype_limits(memory_type) }; - if !unsafe { (*limits).shared } { -@@ -202,23 +202,28 @@ - )); - } - -- Ok(VMSharedMemory(Arc::new(SharedMemoryPointer::new(shared)))) -+ Ok(VMSharedMemory(Arc::new(SharedMemoryPointer::new(shared)), ops)) - } - } - - impl VMSharedMemory { - /// We can clone a shared memory handle as we want. - pub(crate) fn clone(&self) -> Self { -- Self(self.0.clone()) -+ Self(self.0.clone(), self.1.clone()) - } - - /// Attach the shared memory handle to a new Store and convert it to VMMemory - pub(crate) fn into_vm_memory(self, store: &mut Store) -> VMMemory { -- let memory = unsafe { wasm_memory_obtain(store.inner, self.0.0) }; -+ self.try_into_vm_memory(store).expect("cannot attach V8 shared memory") -+ } -+ -+ pub(crate) fn try_into_vm_memory(self, store: &mut Store) -> Result { -+ let memory = self.1.attach(store, self.0.0)?; -+ store.owned_refs.push(crate::backend::v8::store::OwnedRef(unsafe { wasm_memory_as_ref(memory) })); - assert!( - !memory.is_null(), - "Failed to obtain V8 shared memory: wasm_memory_obtain returned null" - ); -- VMMemory(memory) -- } --} -+ Ok(VMMemory(memory)) -+ } -+} ---- a/src/entities/memory/location.rs -+++ b/src/entities/memory/location.rs -@@ -24,6 +24,10 @@ - - /// See [`crate::SharedMemory`]. - pub(crate) trait SharedMemoryOps { -+ fn terminate_execution_contexts(&self) -> Result<(), AtomicsError> { -+ self.disable_atomics() -+ } -+ - /// See [`crate::SharedMemory::disable_atomics`]. - fn disable_atomics(&self) -> Result<(), AtomicsError> { - Err(AtomicsError::Unimplemented) ---- a/src/entities/memory/shared.rs -+++ b/src/entities/memory/shared.rs -@@ -68,6 +68,23 @@ - Memory::new_from_existing(store, memory) - } - -+ /// Attach shared memory, returning an error after V8 terminal cancellation. -+ /// -+ /// This permits task creation to race with terminal cancellation without -+ /// unwinding through the task or linker. Other backends retain their -+ /// existing attachment behavior. -+ pub fn try_attach(self, store: &mut impl AsStoreMut) -> Result { -+ let memory = match self.memory { -+ #[cfg(feature = "v8")] -+ VMSharedMemory::V8(memory) => { -+ let mut store = store.as_store_mut(); -+ VMMemory::V8(memory.try_into_vm_memory(store.inner.store.as_v8_mut())?) -+ } -+ memory => memory.into_vm_memory(store), -+ }; -+ Ok(Memory::new_from_existing(store, memory)) -+ } -+ - /// Create an operations handle that does not keep the underlying memory alive. - pub fn ops(&self) -> MemoryOps { - MemoryOps { -@@ -122,6 +139,16 @@ - } - - impl MemoryOps { -+ /// Terminally cancel execution contexts using this memory. -+ /// -+ /// V8 cancels whole Stores and rejects guest reentry; this is not a -+ /// memory-specific atomic operation. Hosts must retire these contexts. -+ /// Other backends retain their existing shutdown through atomics disabling. -+ #[doc(hidden)] -+ pub fn terminate_execution_contexts(&self) -> Result<(), AtomicsError> { -+ self.shared_ops()?.terminate_execution_contexts() -+ } -+ - #[inline] - fn shared_ops(&self) -> Result<&(dyn SharedMemoryOps + Send + Sync), AtomicsError> { - self.ops ---- a/src/vm/impls.rs -+++ b/src/vm/impls.rs -@@ -59,7 +59,11 @@ - #[cfg(feature = "sys")] - Self::Sys(s) => s.0.as_shared().map(VMSharedMemory::Sys), - #[cfg(feature = "v8")] -- Self::V8(s) => s.as_shared().map(VMSharedMemory::V8), -+ // V8 sharing needs its owning Store to preserve interruption state. -+ // The V8 Memory API supplies it; this store-free helper is Sys-only. -+ Self::V8(_) => Err(wasmer_types::MemoryError::Generic( -+ "V8 memory sharing requires its owning Store".to_owned(), -+ )), - #[cfg(feature = "js")] - Self::Js(s) => s.try_clone().map(VMSharedMemory::Js), - } diff --git a/src/wasix/runtime/engine/patches/wasmer/0001-v8-engine-delivery.patch b/src/wasix/runtime/engine/patches/wasmer/0001-v8-engine-delivery.patch new file mode 100644 index 000000000..4a669ea36 --- /dev/null +++ b/src/wasix/runtime/engine/patches/wasmer/0001-v8-engine-delivery.patch @@ -0,0 +1,51 @@ +From: Sid Jain +Date: Thu, 8 Oct 2026 00:00:00 +0000 +Subject: [PATCH 1/4] v8: prepare the bundled Windows engine before dispatch + +Return retryable Windows delivery errors before creating the process-wide V8 +engine. Configure the pinned flags once, before engine initialization. + +The complete ordered series is the build and qualification unit. + +diff --git a/src/backend/v8/entities/engine.rs b/src/backend/v8/entities/engine.rs +--- a/src/backend/v8/entities/engine.rs ++++ b/src/backend/v8/entities/engine.rs +@@ -1,4 +1,4 @@ +-//! Data types, functions and traits for `sys` runtime's `Engine` implementation. ++//! Data types, functions and traits for `v8` runtime's `Engine` implementation. + use crate::{ + BackendEngine, + backend::v8::bindings::{wasm_engine_delete, wasm_engine_new, wasm_engine_t}, +@@ -27,9 +27,15 @@ + + impl Default for CApiEngine { + fn default() -> Self { +- let engine = ENGINE +- .get_or_init(|| unsafe { std::sync::Mutex::new(EngineCapsule(wasm_engine_new())) }); +- let engine = unsafe { engine.lock().unwrap().0 }; ++ #[cfg(windows)] ++ crate::backend::v8::bindings::oliphaunt_v8_prepare() ++ .expect("prepare Windows V8 engine; use V8::try_new for delivery errors"); ++ let engine = ENGINE.get_or_init(|| unsafe { ++ #[cfg(windows)] ++ crate::backend::v8::bindings::oliphaunt_v8_configure(); ++ std::sync::Mutex::new(EngineCapsule(wasm_engine_new())) ++ }); ++ let engine = engine.lock().unwrap().0; + Self { engine } + } + } +@@ -48,6 +54,13 @@ + /// Create a new instance of the `V8` engine. + pub fn new() -> Self { + Self::default() ++ } ++ ++ /// Prepare the embedded native engine, returning retryable delivery errors. ++ pub fn try_new() -> std::io::Result { ++ #[cfg(windows)] ++ crate::backend::v8::bindings::oliphaunt_v8_prepare()?; ++ Ok(Self::default()) + } + + pub(crate) fn deterministic_id(&self) -> String { diff --git a/src/wasix/runtime/engine/patches/wasmer/0002-v8-handle-and-call-ownership.patch b/src/wasix/runtime/engine/patches/wasmer/0002-v8-handle-and-call-ownership.patch new file mode 100644 index 000000000..5c6da377a --- /dev/null +++ b/src/wasix/runtime/engine/patches/wasmer/0002-v8-handle-and-call-ownership.patch @@ -0,0 +1,691 @@ +From: Sid Jain +Date: Thu, 8 Oct 2026 00:00:00 +0000 +Subject: [PATCH 2/4] v8: retain C handles and preserve borrowed call values + +Give Stores ownership of C wrappers borrowed by Rust aliases. Release temporary +types and traps while their isolate is live, and copy borrowed call values and +imports before passing them to owning C API vectors. + +The complete ordered series is the build and qualification unit. + +diff --git a/src/backend/v8/entities/function/mod.rs b/src/backend/v8/entities/function/mod.rs +--- a/src/backend/v8/entities/function/mod.rs ++++ b/src/backend/v8/entities/function/mod.rs +@@ -59,6 +59,12 @@ + .field("env_is_some", &self.env_handle.is_some()) + .finish() + } ++} ++ ++// Each constructor transfers one unique callback environment to V8. ++// V8's existing host-info finalizer releases it when its isolate is disposed. ++unsafe extern "C" fn drop_callback_env(ptr: *mut c_void) { ++ unsafe { drop(Box::from_raw(ptr.cast::>())) }; + } + + impl Function { +@@ -145,8 +151,21 @@ + wasm_functype, + Some(callback), + callback_env as *mut _ as _, +- None, ++ Some(drop_callback_env::), + ) ++ }; ++ ++ // The callback has a borrowed StoreMut; retain only its unique C root. ++ unsafe { ++ let root = wasm_func_as_ref(wasm_function); ++ (*callback_env) ++ .store ++ .inner ++ .store ++ .as_v8_mut() ++ .owned_refs ++ .push(super::store::OwnedRef(root)); ++ wasm_functype_delete(wasm_functype); + }; + + if wasm_function.is_null() { +@@ -217,8 +236,21 @@ + wasm_functype, + Some(callback), + callback_env as _, +- None, ++ Some(drop_callback_env::), + ) ++ }; ++ ++ // The callback has a borrowed StoreMut; retain only its unique C root. ++ unsafe { ++ let root = wasm_func_as_ref(wasm_function); ++ (*callback_env) ++ .store ++ .inner ++ .store ++ .as_v8_mut() ++ .owned_refs ++ .push(super::store::OwnedRef(root)); ++ wasm_functype_delete(wasm_functype); + }; + + if wasm_function.is_null() { +@@ -297,8 +329,21 @@ + wasm_functype, + Some(callback), + callback_env as _, +- None, ++ Some(drop_callback_env::), + ) ++ }; ++ ++ // The callback has a borrowed StoreMut; retain only its unique C root. ++ unsafe { ++ let root = wasm_func_as_ref(wasm_function); ++ (*callback_env) ++ .store ++ .inner ++ .store ++ .as_v8_mut() ++ .owned_refs ++ .push(super::store::OwnedRef(root)); ++ wasm_functype_delete(wasm_functype); + }; + + if wasm_function.is_null() { +@@ -332,6 +377,7 @@ + res + }; + ++ unsafe { wasm_functype_delete(type_) }; + FunctionType::new(params, returns) + } + +@@ -375,31 +421,34 @@ + + // let wasm_func_param_arity(self.handle) + +- let mut args = unsafe { ++ let mut args = typed::OwnedValueVec(unsafe { + let mut wasm_params = params + .iter() +- .map(|v| IntoCApiValue::into_cv(v.clone())) ++ .map(|v| typed::own_call_parameter(IntoCApiValue::into_cv(v.clone()))) + .collect::>() + .into_boxed_slice(); + let mut vec = Default::default(); + wasm_val_vec_new(&mut vec, wasm_params.len(), wasm_params.as_ptr()); + vec +- }; ++ }); + + let size = unsafe { wasm_func_result_arity(self.handle) }; + +- let mut results = { ++ let mut results = typed::OwnedValueVec({ + unsafe { + let mut vec = Default::default(); + wasm_val_vec_new_uninitialized(&mut vec, size); + vec + } +- }; ++ }); + + let mut trap; + + loop { +- trap = unsafe { wasm_func_call(self.handle, &mut args as _, &mut results as *mut _) }; ++ store.as_store_ref().inner.store.as_v8().ensure_running()?; ++ let raw_trap = ++ unsafe { wasm_func_call(self.handle, &mut *args as _, &mut *results as *mut _) }; ++ trap = (!raw_trap.is_null()).then(|| Trap::from(raw_trap)); + let store_mut = store.as_store_mut(); + if let Some(callback) = store_mut.inner.on_called.take() { + match callback(store_mut) { +@@ -418,15 +467,17 @@ + break; + } + +- if !trap.is_null() { +- return Err(Into::::into(trap).into()); ++ store.as_store_ref().inner.store.as_v8().ensure_running()?; ++ if let Some(trap) = trap { ++ return Err(trap.into()); + } + + let values = unsafe { + let results = std::ptr::slice_from_raw_parts(results.data, results.size); + (*results) + .iter() +- .map(|v| IntoWasmerValue::into_wv(*v)) ++ .zip(signature.results()) ++ .map(|(v, expected)| typed::into_call_value(*v, *expected, store)) + .collect::>() + .into_boxed_slice() + }; +@@ -526,8 +577,9 @@ + unsafe { trap.into_wasm_trap(&mut store) } + } + +- Err(e) => { +- unimplemented!("host function panicked"); ++ Err(_) => { ++ let trap = Trap::user(Box::new(RuntimeError::new("host function panicked"))); ++ unsafe { trap.into_wasm_trap(&mut store) } + } + } + } +@@ -634,8 +686,9 @@ + // unimplemented!("host function panicked"); + }, + +- Err(e) => { +- unimplemented!("host function panicked"); ++ Err(_) => { ++ let trap = Trap::user(Box::new(RuntimeError::new("host function panicked"))); ++ unsafe { trap.into_wasm_trap(store) } + } + } + }} +@@ -707,7 +760,10 @@ + let trap = crate::backend::v8::error::Trap::user(Box::new(e)); unsafe { trap.into_wasm_trap(store) } + }, + +- Err(e) => { unimplemented!("host function panicked"); } ++ Err(_) => { ++ let trap = Trap::user(Box::new(RuntimeError::new("host function panicked"))); ++ unsafe { trap.into_wasm_trap(store) } ++ } + } + }} + +diff --git a/src/backend/v8/entities/function/typed.rs b/src/backend/v8/entities/function/typed.rs +--- a/src/backend/v8/entities/function/typed.rs ++++ b/src/backend/v8/entities/function/typed.rs +@@ -11,12 +11,66 @@ + use std::iter::FromIterator; + + use crate::{ +- AsStoreMut, FromToNativeWasmType, NativeWasmType, NativeWasmTypeInto, RuntimeError, ++ AsStoreMut, AsStoreRef, FromToNativeWasmType, NativeWasmType, NativeWasmTypeInto, RuntimeError, + TypedFunction, Value, WasmTypeList, + backend::v8::{bindings::*, error::Trap, function::Function, utils::convert::*}, + }; + use wasmer_types::RawValue; + ++// Delete owned argument and result vectors on every return path. ++pub(super) struct OwnedValueVec(pub(super) wasm_val_vec_t); ++impl std::ops::Deref for OwnedValueVec { ++ type Target = wasm_val_vec_t; ++ fn deref(&self) -> &Self::Target { ++ &self.0 ++ } ++} ++impl std::ops::DerefMut for OwnedValueVec { ++ fn deref_mut(&mut self) -> &mut Self::Target { ++ &mut self.0 ++ } ++} ++impl Drop for OwnedValueVec { ++ fn drop(&mut self) { ++ unsafe { wasm_val_vec_delete(&mut self.0) } ++ } ++} ++// C vectors adopt reference wrappers; borrow a Function only through a copy. ++pub(super) fn own_call_parameter(value: wasm_val_t) -> wasm_val_t { ++ if value.kind == wasm_valkind_enum_WASM_FUNCREF as _ { ++ let mut owned = value; ++ unsafe { wasm_val_copy(&mut owned, &value) }; ++ owned ++ } else { ++ value ++ } ++} ++ ++// Returned Function aliases must outlive the temporary native result vector. ++pub(super) fn into_call_value( ++ mut value: wasm_val_t, ++ expected: wasmer_types::Type, ++ store: &mut impl AsStoreMut, ++) -> Value { ++ // The pinned C API reports returned function references as generic refs. ++ if expected == wasmer_types::Type::FuncRef { ++ value.kind = wasm_valkind_enum_WASM_FUNCREF as _; ++ let pointer = unsafe { value.of.ref_ }; ++ if pointer.is_null() { ++ return Value::FuncRef(None); ++ } ++ let owned = unsafe { wasm_ref_copy(pointer) }; ++ store ++ .as_store_mut() ++ .inner ++ .store ++ .as_v8_mut() ++ .owned_refs ++ .push(super::super::store::OwnedRef(owned)); ++ value.of.ref_ = owned; ++ } ++ value.into_wv() ++} + macro_rules! impl_native_traits { + ( $( $x:ident ),* ) => { + #[allow(unused_parens, non_snake_case)] +@@ -37,28 +91,31 @@ + vec![ $( { + let raw = $x.to_native().into_raw(store); + let value = Value::from_raw(&mut store, <$x::Native as NativeWasmType>::WASM_TYPE, raw); +- value.into_cv() ++ own_call_parameter(value.into_cv()) + } ),* ] + }; + +- let mut results = { ++ let mut results = OwnedValueVec({ + unsafe { + let mut ret = std::mem::zeroed(); + wasm_val_vec_new_uninitialized(&mut ret, Rets::wasm_types().len()); + ret + } +- }; ++ }); + + + let func = unsafe { wasm_extern_as_func(self.func.to_vm_extern().unwrap_v_8()) }; + + let mut params = unsafe {std::mem::zeroed()}; + unsafe {wasm_val_vec_new(&mut params, params_list.len(), params_list.as_ptr())}; ++ let params = OwnedValueVec(params); + + let mut trap; + + loop { +- trap = unsafe { wasm_func_call(func, ¶ms, &mut results) }; ++ store.as_store_ref().inner.store.as_v8().ensure_running()?; ++ let raw_trap = unsafe { wasm_func_call(func, &*params, &mut *results) }; ++ trap = (!raw_trap.is_null()).then(|| Trap::from(raw_trap)); + + let store_mut = store.as_store_mut(); + if let Some(callback) = store_mut.inner.on_called.take() { +@@ -74,12 +131,10 @@ + + + +- if !trap.is_null() { +- unsafe { +- let trap: Trap = trap.into(); +- return Err(RuntimeError::from(trap)); +- } +- } ++ store.as_store_ref().inner.store.as_v8().ensure_running()?; ++ if let Some(trap) = trap { ++ return Err(RuntimeError::from(trap)); ++ } + + unsafe { + let mut rets_list_array = Rets::empty_array(); +@@ -89,13 +144,13 @@ + 0 => {}, + 1 => { + let val = (*results.data.wrapping_add(0)).clone(); +- let val = val.into_wv(); ++ let val = into_call_value(val, Rets::wasm_types()[0], &mut store); + *mut_rets = val.as_raw(&mut store); + } + _n => { + for (i, ret_type) in Rets::wasm_types().iter().enumerate() { + let val = (*results.data.wrapping_add(i)).clone(); +- let val = val.into_wv(); ++ let val = into_call_value(val, *ret_type, &mut store); + let slot = mut_rets.add(i); + *slot = val.as_raw(&mut store); + } +diff --git a/src/backend/v8/entities/global.rs b/src/backend/v8/entities/global.rs +--- a/src/backend/v8/entities/global.rs ++++ b/src/backend/v8/entities/global.rs +@@ -60,9 +60,17 @@ + let v8_global_type = + unsafe { wasm_globaltype_new(wasm_valtype_new(v8_type), v8_mutability) }; + +- Ok(Self { +- handle: unsafe { wasm_global_new(v8_store.inner, v8_global_type, &v8_value) }, +- }) ++ let handle = unsafe { wasm_global_new(v8_store.inner, v8_global_type, &v8_value) }; ++ unsafe { bindings::wasm_globaltype_delete(v8_global_type) }; ++ store ++ .inner ++ .store ++ .as_v8_mut() ++ .owned_refs ++ .push(super::store::OwnedRef(unsafe { ++ bindings::wasm_global_as_ref(handle) ++ })); ++ Ok(Self { handle }) + } + + pub fn ty(&self, store: &impl AsStoreRef) -> GlobalType { +@@ -73,14 +81,16 @@ + let valtype = unsafe { wasm_globaltype_content(r#type) }; + let wasmer_type = valtype.into_wt(); + +- GlobalType::new( ++ let result = GlobalType::new( + wasmer_type, + if mutability == bindings::wasm_mutability_enum_WASM_VAR as u8 { + Mutability::Var + } else { + Mutability::Const + }, +- ) ++ ); ++ unsafe { bindings::wasm_globaltype_delete(r#type) }; ++ result + } + + pub fn get(&self, store: &mut impl AsStoreMut) -> Value { +diff --git a/src/backend/v8/entities/instance.rs b/src/backend/v8/entities/instance.rs +--- a/src/backend/v8/entities/instance.rs ++++ b/src/backend/v8/entities/instance.rs +@@ -26,20 +26,15 @@ + let externs: Vec<_> = externs.into_iter().map(|v| v.unwrap_v_8()).collect(); + + let instance = unsafe { +- let mut imports = unsafe { +- let mut vec = Default::default(); +- wasm_extern_vec_new(&mut vec, externs.len(), externs.as_ptr()); +- vec +- }; +- +- std::mem::forget(externs); +- +- wasm_instance_new( +- store, +- wasm_module_obtain(store, module), +- &mut imports, +- &mut trap, +- ) ++ // The vector consumes C wrappers: copy borrowed reusable imports. ++ let copied: Vec<_> = externs.iter().map(|p| wasm_extern_copy(*p)).collect(); ++ let mut imports = wasm_extern_vec_t::default(); ++ wasm_extern_vec_new(&mut imports, copied.len(), copied.as_ptr()); ++ let local_module = wasm_module_obtain(store, module); ++ let instance = wasm_instance_new(store, local_module, &imports, &mut trap); ++ wasm_module_delete(local_module); ++ wasm_extern_vec_delete(&mut imports); ++ instance + }; + + if instance.is_null() { +@@ -65,7 +60,7 @@ + }; + + let exports_ty = module.exports().collect::>(); +- exports_ty ++ let result = exports_ty + .iter() + .zip(wasm_exports.iter()) + .map(|(export_type, wasm_export)| { +@@ -77,14 +72,18 @@ + let extern_ = Extern::from_vm_extern(&mut store, VMExtern::V8(*wasm_export)); + (name.to_string(), extern_) + }) +- .collect::() ++ .collect::(); ++ store ++ .as_store_mut() ++ .inner ++ .store ++ .as_v8_mut() ++ .owned_exports ++ .push(super::store::OwnedExports(exports)); ++ result + } + } +-impl Drop for InstanceHandle { +- fn drop(&mut self) { +- unsafe { wasm_instance_delete(self.0) } +- } +-} ++// Store owns the C wrapper; a Rust alias may outlive its Store. + + #[derive(Clone, PartialEq, Eq)] + /// A WebAssembly `instance` in the `v8` runtime. +@@ -117,6 +116,12 @@ + ) -> Result<(Self, Exports), InstantiationError> { + check_isolate(store); + let store_ref = store.as_store_ref(); ++ store_ref ++ .inner ++ .store ++ .as_v8() ++ .ensure_running() ++ .map_err(InstantiationError::Start)?; + let externs: Vec = externs + .iter() + .map(|extern_| extern_.to_vm_extern()) +@@ -126,6 +131,22 @@ + module.as_v8().handle.v8_shared_module_handle, + externs, + )?; ++ // C instance wrappers belong to the Store, like their export aliases. ++ let owned = unsafe { wasm_instance_as_ref(instance.0) }; ++ store ++ .as_store_mut() ++ .inner ++ .store ++ .as_v8_mut() ++ .owned_refs ++ .push(super::store::OwnedRef(owned)); ++ store ++ .as_store_ref() ++ .inner ++ .store ++ .as_v8() ++ .ensure_running() ++ .map_err(InstantiationError::Start)?; + let exports = instance.get_exports(store, module); + + Ok(( +diff --git a/src/backend/v8/entities/table.rs b/src/backend/v8/entities/table.rs +--- a/src/backend/v8/entities/table.rs ++++ b/src/backend/v8/entities/table.rs +@@ -26,13 +26,12 @@ + pub(crate) fn type_to_v8(ty: TableType) -> *mut wasm_tabletype_t { + let valtype = unsafe { wasm_valtype_new(ty.ty.into_ct()) }; + +- let limits = Box::into_raw(Box::new(wasm_limits_t { ++ let limits = wasm_limits_t { + min: ty.minimum, + max: ty.maximum.unwrap_or(wasm_limits_max_default), + shared: false, +- })); +- +- unsafe { wasm_tabletype_new(valtype, limits) } ++ }; ++ unsafe { wasm_tabletype_new(valtype, &limits) } + } + + pub fn new( +@@ -49,9 +48,15 @@ + let wasm_tablety = Self::type_to_v8(ty); + let init: wasm_val_t = init.into_cv(); + +- Ok(Self { +- handle: unsafe { wasm_table_new(v8_store.inner, wasm_tablety, init.of.ref_) }, +- }) ++ let handle = unsafe { wasm_table_new(v8_store.inner, wasm_tablety, init.of.ref_) }; ++ unsafe { wasm_tabletype_delete(wasm_tablety) }; ++ store_mut ++ .inner ++ .store ++ .as_v8_mut() ++ .owned_refs ++ .push(super::store::OwnedRef(unsafe { wasm_table_as_ref(handle) })); ++ Ok(Self { handle }) + } + + pub fn to_vm_extern(&self) -> VMExtern { +@@ -65,10 +70,10 @@ + + let table_type: *mut wasm_tabletype_t = unsafe { wasm_table_type(self.handle) }; + let table_limits = unsafe { wasm_tabletype_limits(table_type) }; +- let table_type = unsafe { wasm_tabletype_element(table_type) }; +- +- TableType { +- ty: table_type.into_wt(), ++ let element_type = unsafe { wasm_tabletype_element(table_type) }; ++ ++ let result = TableType { ++ ty: element_type.into_wt(), + minimum: unsafe { (*table_limits).min }, + maximum: unsafe { + if (*table_limits).max == wasm_limits_max_default { +@@ -78,7 +83,9 @@ + } + }, + readonly: false, +- } ++ }; ++ unsafe { wasm_tabletype_delete(table_type) }; ++ result + } + + pub fn get(&self, store: &mut impl AsStoreMut, index: u32) -> Option { +diff --git a/src/backend/v8/entities/tag.rs b/src/backend/v8/entities/tag.rs +--- a/src/backend/v8/entities/tag.rs ++++ b/src/backend/v8/entities/tag.rs +@@ -21,6 +21,9 @@ + + unsafe impl Send for Tag {} + unsafe impl Sync for Tag {} ++ ++// C bridge uses ExternType's virtual implementation destructor. ++use crate::backend::v8::bindings::oliphaunt_externtype_delete; + + impl Tag { + pub fn new>>(store: &mut impl AsStoreMut, params: P) -> Self { +@@ -49,6 +52,13 @@ + } + + let handle = unsafe { wasm_tag_new(v8_store.inner, tag_type) }; ++ unsafe { oliphaunt_externtype_delete(tag_type) }; ++ store_mut ++ .inner ++ .store ++ .as_v8_mut() ++ .owned_refs ++ .push(super::store::OwnedRef(unsafe { wasm_tag_as_ref(handle) })); + if handle.is_null() { + panic!("failed to create new tag: returned tag is null"); + } +@@ -69,6 +79,7 @@ + res + }; + ++ unsafe { oliphaunt_externtype_delete(type_) }; + TagType::new(wasmer_types::TagKind::Exception, params) + } + +diff --git a/src/backend/v8/error.rs b/src/backend/v8/error.rs +--- a/src/backend/v8/error.rs ++++ b/src/backend/v8/error.rs +@@ -1,7 +1,4 @@ +-use std::{ +- error::Error, +- ffi::{CStr, c_char}, +-}; ++use std::error::Error; + + use crate::{ + AsStoreMut, +@@ -11,7 +8,7 @@ + #[derive(Debug)] + enum InnerTrap { + User(Box), +- CApi(*mut wasm_trap_t), ++ CApi(String), + } + + /// A struct representing a Trap +@@ -69,7 +66,14 @@ + #[allow(clippy::unnecessary_mut_passed)] + pub unsafe fn into_wasm_trap(self, store: &mut impl AsStoreMut) -> *mut wasm_trap_t { + match self.inner { +- InnerTrap::CApi(t) => t, ++ InnerTrap::CApi(message) => { ++ let mut data = wasm_byte_vec_t::default(); ++ unsafe { wasm_byte_vec_new(&mut data, message.len(), message.as_ptr() as _) }; ++ let store = store.as_store_mut(); ++ let trap = unsafe { wasm_trap_new(store.inner.store.as_v8().inner, &data) }; ++ unsafe { wasm_byte_vec_delete(&mut data) }; ++ trap ++ } + InnerTrap::User(err) => { + let err_ptr = Box::leak(Box::new(err)); + // UNSAFE: The boxed error is propagated by passing the string +@@ -90,13 +94,20 @@ + + impl From<*mut wasm_trap_t> for Trap { + fn from(value: *mut wasm_trap_t) -> Self { ++ // Snapshot the message while the Store is live. RuntimeError can outlive ++ // its Store, so it must not retain an isolate-owned C API trap handle. + let message = unsafe { +- let mut message = unsafe { std::mem::zeroed() }; +- unsafe { wasm_trap_message(value, &mut message) }; +- +- CStr::from_ptr(message.data as *const c_char) +- .to_str() +- .unwrap() ++ let mut bytes = wasm_byte_vec_t::default(); ++ wasm_trap_message(value, &mut bytes); ++ let text = String::from_utf8_lossy(std::slice::from_raw_parts( ++ bytes.data.cast::(), ++ bytes.size, ++ )) ++ .trim_end_matches('\0') ++ .to_owned(); ++ wasm_byte_vec_delete(&mut bytes); ++ wasm_trap_delete(value); ++ text + }; + + // User traps are encoded by `into_wasm_trap` as a leaked `Box` pointer +@@ -104,8 +115,8 @@ + if let Some((_, ptr_str)) = message.split_once("🐛") { + let ptr: Box = unsafe { + let r = ptr_str.trim_start_matches("0x"); +- std::ptr::read(usize::from_str_radix(r, 16).unwrap() +- as *const Box) ++ *Box::from_raw(usize::from_str_radix(r, 16).unwrap() ++ as *mut Box) + }; + + Self { +@@ -113,7 +124,7 @@ + } + } else { + Self { +- inner: InnerTrap::CApi(value), ++ inner: InnerTrap::CApi(message), + } + } + } +@@ -132,18 +143,7 @@ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.inner { + InnerTrap::User(e) => write!(f, "{e}"), +- InnerTrap::CApi(value) => { +- // let message: wasm_message_t; +- // wasm_trap_message(value, &mut message); +- let mut out = unsafe { +- let mut vec: wasm_byte_vec_t = Default::default(); +- wasm_byte_vec_new_empty(&mut vec); +- &mut vec as *mut _ +- }; +- unsafe { wasm_trap_message(*value, out) }; +- let cstr = unsafe { CStr::from_ptr((*out).data) }; +- write!(f, "wasm-c-api trap: {}", cstr.to_str().unwrap()) +- } ++ InnerTrap::CApi(message) => write!(f, "wasm-c-api trap: {message}"), + } + } + } diff --git a/src/wasix/runtime/engine/patches/wasmer/0003-v8-native-cache-profiles.patch b/src/wasix/runtime/engine/patches/wasmer/0003-v8-native-cache-profiles.patch new file mode 100644 index 000000000..a4f8483a7 --- /dev/null +++ b/src/wasix/runtime/engine/patches/wasmer/0003-v8-native-cache-profiles.patch @@ -0,0 +1,309 @@ +From: Sid Jain +Date: Thu, 8 Oct 2026 00:00:00 +0000 +Subject: [PATCH 3/4] v8: load compatible producer-built native cache profiles + +Keep module sharing independent of the original Store. Serialize genuine native +CPU profiles for the same guest and reject incomplete or incompatible caches +without falling back to guest compilation. Include the Windows cache contracts. + +The complete ordered series is the build and qualification unit. + +diff --git a/src/backend/v8/entities/module.rs b/src/backend/v8/entities/module.rs +--- a/src/backend/v8/entities/module.rs ++++ b/src/backend/v8/entities/module.rs +@@ -15,22 +15,97 @@ + }; + use wasmparser::{Parser, Payload}; + ++#[cfg(all(test, windows))] ++mod oliphaunt_contract { ++ use super::*; ++ ++ const GUEST: &[u8] = b"\0asm\x01\0\0\0\x01\x05\x01\x60\0\x01\x7f\x03\x02\x01\0\x07\x0a\x01\x06answer\0\0\x0a\x06\x01\x04\0\x41\x2a\x0b"; ++ ++ fn native_offset(cache: &[u8]) -> usize { ++ assert!(cache[0] < 128, "the fixture has a one-byte wire length"); ++ 1 + usize::from(cache[0]) ++ } ++ ++ fn encoded(info: &V8ModuleInfo) -> Vec { ++ rkyv::to_bytes::(info) ++ .unwrap() ++ .to_vec() ++ } ++ ++ #[test] ++ fn rejects_wire_only_and_incompatible_caches_without_compilation() { ++ let engine: crate::Engine = crate::v8::V8::try_new().unwrap().into(); ++ let module = Module::from_binary(&engine, GUEST).unwrap(); ++ let mut info = module.info.clone(); ++ let offset = native_offset(&info.v8_module_bytes[0]); ++ info.v8_module_bytes[0].truncate(offset); ++ let error = unsafe { crate::Module::deserialize(&engine, encoded(&info)) }.unwrap_err(); ++ assert!(error.to_string().contains("guest compilation is disabled")); ++ ++ let mut info = module.info.clone(); ++ // Deliberately incompatible test bytes, never a producer workaround. ++ info.v8_module_bytes[0][offset + 8..offset + 12].copy_from_slice(&u32::MAX.to_le_bytes()); ++ assert!(unsafe { crate::Module::deserialize(&engine, encoded(&info)) }.is_err()); ++ } ++ ++ #[test] ++ fn skips_an_incompatible_cpu_profile_and_executes_the_next_native_cache() { ++ let engine: crate::Engine = crate::v8::V8::try_new().unwrap().into(); ++ let module = Module::from_binary(&engine, GUEST).unwrap(); ++ let mut info = module.info.clone(); ++ let original = info.v8_module_bytes[0].clone(); ++ let offset = native_offset(&original); ++ assert_eq!( ++ u32::from_le_bytes(original[offset + 8..offset + 12].try_into().unwrap()), ++ 0x1000e, ++ "the native runner must qualify the CET profile" ++ ); ++ info.v8_module_bytes[0][offset + 8..offset + 12].copy_from_slice(&u32::MAX.to_le_bytes()); ++ info.v8_module_bytes.push(original); ++ let loaded = unsafe { crate::Module::deserialize(&engine, encoded(&info)) }.unwrap(); ++ let mut store = crate::Store::new(engine); ++ let instance = crate::Instance::new(&mut store, &loaded, &crate::imports! {}).unwrap(); ++ let answer = instance ++ .exports ++ .get_typed_function::<(), i32>(&store, "answer") ++ .unwrap(); ++ assert_eq!(answer.call(&mut store).unwrap(), 42); ++ } ++ ++ #[test] ++ fn rejects_variants_for_another_guest() { ++ let engine: crate::Engine = crate::v8::V8::try_new().unwrap().into(); ++ let module = Module::from_binary(&engine, GUEST).unwrap(); ++ let mut different = GUEST.to_vec(); ++ let position = different.len() - 2; ++ different[position] = 43; ++ let other = Module::from_binary(&engine, &different).unwrap(); ++ assert!( ++ module ++ .serialize_with_native_variants(&other.info.v8_module_bytes) ++ .is_err() ++ ); ++ } ++} ++ + #[derive(Debug)] + pub(crate) struct ModuleHandle { + pub(crate) v8_shared_module_handle: *mut wasm_shared_module_t, +- pub(crate) orig_store: Store, ++ pub(crate) engine: crate::Engine, + } + + impl PartialEq for ModuleHandle { + fn eq(&self, other: &Self) -> bool { ++ // Compare in one isolate; each obtained wrapper is unique. + unsafe { +- wasm_module_same( +- wasm_module_obtain(self.orig_store.as_v8().inner, self.v8_shared_module_handle), +- wasm_module_obtain( +- other.orig_store.as_v8().inner, +- other.v8_shared_module_handle, +- ), +- ) ++ let transient = Store::new(self.engine.clone()); ++ let store = transient.as_v8().inner; ++ let left = wasm_module_obtain(store, self.v8_shared_module_handle); ++ let right = wasm_module_obtain(store, other.v8_shared_module_handle); ++ let same = wasm_module_same(left, right); ++ wasm_module_delete(right); ++ wasm_module_delete(left); ++ same + } + } + } +@@ -46,7 +121,6 @@ + + let engine = engine.as_engine_ref().engine().clone(); + let store = Store::new(engine.clone()); +- let engine = engine.as_v8().inner.engine; + + let inner = unsafe { wasm_module_new(store.as_v8().inner, &bytes as *const _) }; + +@@ -56,7 +130,11 @@ + )); + } + +- let inner = unsafe { wasm_module_share(inner) }; ++ let inner = unsafe { ++ let shared = wasm_module_share(inner); ++ wasm_module_delete(inner); ++ shared ++ }; + + if inner.is_null() { + return Err(CompileError::Validate( +@@ -66,11 +144,39 @@ + + Ok(Self { + v8_shared_module_handle: inner, +- orig_store: store, ++ engine, + }) + } + + fn deserialize(engine: &impl AsEngineRef, binary: &[u8]) -> Result { ++ // The pinned C API compiles wire-only serialized modules. Deserialization ++ // of an AOT artifact must reject that case before entering V8. ++ let mut wire_len = 0u64; ++ let mut prefix_len = None; ++ for (index, byte) in binary.iter().copied().take(10).enumerate() { ++ if index == 9 && byte > 1 { ++ break; ++ } ++ wire_len |= u64::from(byte & 0x7f) << (index * 7); ++ if byte & 0x80 == 0 { ++ prefix_len = Some(index + 1); ++ break; ++ } ++ } ++ let native_start = prefix_len.and_then(|n| { ++ usize::try_from(wire_len) ++ .ok() ++ .and_then(|wire| n.checked_add(wire)) ++ }); ++ if native_start ++ .and_then(|n| binary.len().checked_sub(n)) ++ .is_none_or(|n| n < 20) ++ { ++ return Err(CompileError::Validate( ++ "V8 AOT artifact has no complete native payload; guest compilation is disabled" ++ .into(), ++ )); ++ } + let bytes = wasm_byte_vec_t { + size: binary.len(), + data: binary.as_ptr() as _, +@@ -86,7 +192,11 @@ + )); + } + +- let inner = unsafe { wasm_module_share(inner) }; ++ let inner = unsafe { ++ let shared = wasm_module_share(inner); ++ wasm_module_delete(inner); ++ shared ++ }; + + if inner.is_null() { + return Err(CompileError::Validate( +@@ -96,15 +206,16 @@ + + Ok(Self { + v8_shared_module_handle: inner, +- orig_store: store, ++ engine, + }) + } + + #[tracing::instrument(skip(self))] + pub fn serialize(&self) -> Result, SerializeError> { ++ let transient = Store::new(self.engine.clone()); + let handle = unsafe { + wasm_module_obtain( +- self.orig_store.as_v8().inner, ++ transient.as_v8().inner, + self.v8_shared_module_handle as *const _, + ) + }; +@@ -147,7 +258,7 @@ + // (store) than the one V8 associated with the module, which can crash. + imports: Vec, + exports: Vec, +- v8_module_bytes: Vec, ++ v8_module_bytes: Vec>, + // Copy of the custom sections, since the current API cannot retrieve them + // later from the handle. Use a BTreeMap to keep entries in deterministic order, + // producing stable serialized bytes. +@@ -205,7 +316,7 @@ + name: info.name, + imports, + exports, +- v8_module_bytes, ++ v8_module_bytes: vec![v8_module_bytes], + custom_sections, + }, + }) +@@ -240,6 +351,50 @@ + .map_err(|err| SerializeError::Generic(format!("{err:?}")))? + .to_vec(); + Ok(info.into()) ++ } ++ ++ /// Serialize this module with additional producer-built native CPU profiles. ++ /// Every profile must contain the same WebAssembly wire bytes. ++ pub fn serialize_with_native_variants( ++ &self, ++ variants: &[Vec], ++ ) -> Result { ++ fn wire(bytes: &[u8]) -> Option<&[u8]> { ++ let mut length = 0u64; ++ for (index, byte) in bytes.iter().copied().take(10).enumerate() { ++ if index == 9 && byte > 1 { ++ return None; ++ } ++ length |= u64::from(byte & 0x7f) << (index * 7); ++ if byte & 0x80 == 0 { ++ let end = (index + 1).checked_add(usize::try_from(length).ok()?)?; ++ if bytes.len().checked_sub(end)? < 20 { ++ return None; ++ } ++ return bytes.get(index + 1..end); ++ } ++ } ++ None ++ } ++ let original = self ++ .info ++ .v8_module_bytes ++ .first() ++ .ok_or_else(|| SerializeError::Generic("module has no native cache".into()))?; ++ let expected = wire(original) ++ .ok_or_else(|| SerializeError::Generic("module has no complete native cache".into()))?; ++ let mut info = self.info.clone(); ++ for variant in variants { ++ if wire(variant) != Some(expected) { ++ return Err(SerializeError::Generic( ++ "native cache wire identity mismatch".into(), ++ )); ++ } ++ info.v8_module_bytes.push(variant.clone()); ++ } ++ rkyv::to_bytes::(&info) ++ .map(|bytes| bytes.to_vec().into()) ++ .map_err(|error| SerializeError::Generic(format!("{error:?}"))) + } + + #[allow(clippy::arc_with_non_send_sync)] +@@ -252,7 +407,25 @@ + let info = rkyv::access::(&binary) + .and_then(rkyv::deserialize::) + .map_err(|err| DeserializeError::CorruptedBinary(format!("{err:?}")))?; +- let module = ModuleHandle::deserialize(engine, &info.v8_module_bytes)?; ++ // Native caches retain their original CPU/flag headers. Try the producer's ++ // profiles without compiling a guest when no cache is compatible. ++ let mut failures = Vec::new(); ++ let module = info ++ .v8_module_bytes ++ .iter() ++ .find_map(|cache| match ModuleHandle::deserialize(engine, cache) { ++ Ok(module) => Some(module), ++ Err(error) => { ++ failures.push(error.to_string()); ++ None ++ } ++ }) ++ .ok_or_else(|| { ++ DeserializeError::CorruptedBinary(format!( ++ "no compatible V8 native cache: {}", ++ failures.join("; ") ++ )) ++ })?; + + Ok(Self { + handle: Arc::new(module), diff --git a/src/wasix/runtime/engine/patches/wasmer/0004-v8-memory-and-terminal-lifetimes.patch b/src/wasix/runtime/engine/patches/wasmer/0004-v8-memory-and-terminal-lifetimes.patch new file mode 100644 index 000000000..e78cbb601 --- /dev/null +++ b/src/wasix/runtime/engine/patches/wasmer/0004-v8-memory-and-terminal-lifetimes.patch @@ -0,0 +1,546 @@ +From: Sid Jain +Date: Thu, 8 Oct 2026 00:00:00 +0000 +Subject: [PATCH 4/4] v8: coordinate shared memory and terminal Store lifetimes + +Preserve memory copies across growth and attach shared memory with its owning +Store interruption state. Retire isolate pointers before Store destruction and +reject attachment and guest reentry after terminal cancellation. The ownership +wrappers introduced by the preceding patch are supplied here. + +The complete ordered series is the build and qualification unit. + +diff --git a/src/backend/v8/entities/memory/mod.rs b/src/backend/v8/entities/memory/mod.rs +--- a/src/backend/v8/entities/memory/mod.rs ++++ b/src/backend/v8/entities/memory/mod.rs +@@ -53,14 +53,23 @@ + }); + } + +- let limits = Box::into_raw(Box::new(wasm_limits_t { ++ let limits = wasm_limits_t { + min, + max, + shared: ty.shared, +- })); +- +- let memorytype = unsafe { wasm_memorytype_new(limits) }; ++ }; ++ ++ let memorytype = unsafe { wasm_memorytype_new(&limits) }; + let c_memory = unsafe { wasm_memory_new(v8_store.inner, memorytype) }; ++ unsafe { wasm_memorytype_delete(memorytype) }; ++ store_mut ++ .inner ++ .store ++ .as_v8_mut() ++ .owned_refs ++ .push(super::store::OwnedRef(unsafe { ++ wasm_memory_as_ref(c_memory) ++ })); + + Ok(Self { + handle: VMMemory(c_memory), +@@ -84,7 +93,7 @@ + let memory_type: *mut wasm_memorytype_t = unsafe { wasm_memory_type(self.handle.0) }; + let limits: *const wasm_limits_t = unsafe { wasm_memorytype_limits(memory_type) }; + +- MemoryType { ++ let result = MemoryType { + shared: unsafe { (*limits).shared }, + minimum: unsafe { wasmer_types::Pages((*limits).min) }, + maximum: unsafe { +@@ -94,7 +103,9 @@ + Some(wasmer_types::Pages((*limits).max)) + } + }, +- } ++ }; ++ unsafe { wasm_memorytype_delete(memory_type) }; ++ result + } + + pub fn size(&self, store: &impl AsStoreRef) -> Pages { +@@ -198,6 +209,8 @@ + )); + } + ++ // Own the temporary wrapper on success and every early-return path. ++ let _copied_owner = super::store::OwnedRef(unsafe { wasm_memory_as_ref(copied_memory) }); + let copied = Self { + handle: VMMemory(copied_memory), + }; +@@ -226,9 +239,14 @@ + .copy_to_memory(src_view.data_size(), &dst_view) + .map_err(|err| MemoryError::Generic(format!("failed to copy V8 memory: {err}")))?; + +- Ok(SharedMemory::new(crate::vm::VMSharedMemory::V8( +- copied.handle.as_shared()?, +- ))) ++ let shared = copied ++ .handle ++ .as_shared(super::store::interrupt::MemoryInterrupt::detached())?; ++ let ops = shared.1.clone(); ++ Ok(SharedMemory::new_with_ops( ++ crate::vm::VMSharedMemory::V8(shared), ++ ops, ++ )) + } + + pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { +@@ -238,9 +256,16 @@ + + pub fn as_shared(&self, store: &impl AsStoreRef) -> Result { + check_isolate(store); +- Ok(SharedMemory::new(crate::vm::VMSharedMemory::V8( +- self.handle.as_shared()?, +- ))) ++ let store = store.as_store_ref(); ++ let v8_store = store.inner.store.as_v8(); ++ let shared = self ++ .handle ++ .as_shared(v8_store.shared_memory_ops(self.handle.0))?; ++ let ops = shared.1.clone(); ++ Ok(SharedMemory::new_with_ops( ++ crate::vm::VMSharedMemory::V8(shared), ++ ops, ++ )) + } + } + +diff --git a/src/backend/v8/entities/store/interrupt.rs b/src/backend/v8/entities/store/interrupt.rs +new file mode 100644 +--- /dev/null ++++ b/src/backend/v8/entities/store/interrupt.rs +@@ -0,0 +1,164 @@ ++use std::{ ++ cell::Cell, ++ ffi::c_void, ++ sync::{ ++ Arc, Mutex, Weak, ++ atomic::{AtomicBool, Ordering}, ++ }, ++}; ++ ++use crate::backend::v8::bindings::*; ++use crate::{error::AtomicsError, location::SharedMemoryOps}; ++ ++#[cfg(not(windows))] ++unsafe extern "C" { ++ #[link_name = "_ZN2v87Isolate10GetCurrentEv"] ++ fn current_isolate() -> *mut c_void; ++ #[link_name = "_ZN2v87Isolate18TerminateExecutionEv"] ++ fn terminate_execution(isolate: *mut c_void); ++} ++#[cfg(windows)] ++use crate::backend::v8::bindings::{ ++ oliphaunt_v8_current_isolate as current_isolate, ++ oliphaunt_v8_terminate_execution as terminate_execution, ++}; ++ ++thread_local! { static CAPTURED: Cell = const { Cell::new(0) }; } ++ ++unsafe extern "C" fn capture( ++ _args: *const wasm_val_vec_t, ++ _results: *mut wasm_val_vec_t, ++) -> *mut wasm_trap_t { ++ // The pinned C API enters the Store's isolate before a direct host call. ++ CAPTURED.with(|slot| slot.set(unsafe { current_isolate() } as usize)); ++ std::ptr::null_mut() ++} ++ ++pub(super) struct StoreInterrupt { ++ isolate: Mutex>, ++ terminated: AtomicBool, ++} ++ ++impl StoreInterrupt { ++ pub(super) fn new() -> Arc { ++ Arc::new(Self { ++ isolate: Mutex::new(None), ++ terminated: AtomicBool::new(false), ++ }) ++ } ++ ++ pub(super) fn capture(&self, store: *mut wasm_store_t) { ++ unsafe { ++ let mut params = std::mem::zeroed(); ++ let mut results = std::mem::zeroed(); ++ let ty = wasm_functype_new(&mut params, &mut results); ++ assert!(!ty.is_null(), "cannot create V8 interrupt capture type"); ++ let function = wasm_func_new(store, ty, Some(capture)); ++ wasm_functype_delete(ty); ++ assert!( ++ !function.is_null(), ++ "cannot create V8 interrupt capture function" ++ ); ++ let owner = super::OwnedRef(wasm_func_as_ref(function)); ++ let mut args = std::mem::zeroed(); ++ let mut results = std::mem::zeroed(); ++ CAPTURED.with(|slot| slot.set(0)); ++ let trap = wasm_func_call(function, &mut args, &mut results); ++ let pointer = CAPTURED.with(Cell::get); ++ if !trap.is_null() { ++ wasm_trap_delete(trap); ++ } ++ drop(owner); ++ assert!( ++ trap.is_null() && pointer != 0, ++ "cannot capture V8 Store interrupt" ++ ); ++ *self.isolate.lock().unwrap_or_else(|e| e.into_inner()) = Some(pointer); ++ } ++ } ++ ++ pub(super) fn is_terminated(&self) -> bool { ++ self.terminated.load(Ordering::Acquire) ++ } ++ ++ fn terminate(&self) { ++ self.terminated.store(true, Ordering::Release); ++ // Keep the lock through termination. Store teardown takes this same ++ // lock before retiring the pointer and deleting the native Store. ++ let pointer = self.isolate.lock().unwrap_or_else(|e| e.into_inner()); ++ if let Some(pointer) = *pointer { ++ unsafe { terminate_execution(pointer as *mut c_void) }; ++ } ++ } ++ ++ pub(super) fn retire(&self) { ++ *self.isolate.lock().unwrap_or_else(|e| e.into_inner()) = None; ++ } ++} ++ ++#[derive(Default)] ++struct MemoryState { ++ terminal: bool, ++ stores: Vec>, ++} ++ ++pub(crate) struct MemoryInterrupt(Mutex); ++ ++impl MemoryInterrupt { ++ pub(crate) fn detached() -> Arc { ++ Arc::new(Self(Mutex::new(MemoryState::default()))) ++ } ++ ++ pub(super) fn new(store: &Arc) -> Arc { ++ Arc::new(Self(Mutex::new(MemoryState { ++ terminal: false, ++ stores: vec![Arc::downgrade(store)], ++ }))) ++ } ++ ++ pub(crate) fn attach( ++ self: &Arc, ++ store: &mut super::Store, ++ memory: *mut wasm_shared_memory_t, ++ ) -> Result<*mut wasm_memory_t, crate::MemoryError> { ++ let mut state = self.0.lock().unwrap_or_else(|e| e.into_inner()); ++ if state.terminal { ++ return Err(crate::MemoryError::Generic( ++ "cannot attach V8 shared memory after execution contexts are terminal".to_owned(), ++ )); ++ } ++ let handle = unsafe { wasm_memory_obtain(store.inner, memory) }; ++ if handle.is_null() { ++ return Err(crate::MemoryError::Generic( ++ "cannot obtain V8 shared memory".to_owned(), ++ )); ++ } ++ state.stores.retain(|entry| entry.strong_count() != 0); ++ let interrupt = Arc::downgrade(&store.interrupt); ++ if !state ++ .stores ++ .iter() ++ .any(|entry| Weak::ptr_eq(entry, &interrupt)) ++ { ++ state.stores.push(interrupt); ++ } ++ store.remember_shared_memory(handle, self.clone()); ++ Ok(handle) ++ } ++} ++ ++impl SharedMemoryOps for MemoryInterrupt { ++ fn terminate_execution_contexts(&self) -> Result<(), AtomicsError> { ++ let mut state = self.0.lock().unwrap_or_else(|e| e.into_inner()); ++ state.terminal = true; ++ state.stores.retain(|entry| { ++ if let Some(store) = entry.upgrade() { ++ store.terminate(); ++ true ++ } else { ++ false ++ } ++ }); ++ Ok(()) ++ } ++} +diff --git a/src/backend/v8/entities/store/mod.rs b/src/backend/v8/entities/store/mod.rs +--- a/src/backend/v8/entities/store/mod.rs ++++ b/src/backend/v8/entities/store/mod.rs +@@ -1,5 +1,5 @@ + //! Data types, functions and traits for `v8` runtime's `Store` implementation. +-use std::thread::ThreadId; ++use std::{cell::RefCell, sync::Arc, thread::ThreadId}; + + use crate::{ + AsStoreRef, BackendStore, StoreRef, +@@ -7,14 +7,36 @@ + engine::{AsEngineRef, Engine, EngineRef}, + }; + ++pub(crate) mod interrupt; + mod obj; + pub use obj::*; ++ ++#[derive(Debug)] ++pub(crate) struct OwnedExports(pub(crate) crate::backend::v8::bindings::wasm_extern_vec_t); ++ ++impl Drop for OwnedExports { ++ fn drop(&mut self) { ++ unsafe { crate::backend::v8::bindings::wasm_extern_vec_delete(&mut self.0) }; ++ } ++} ++ ++#[derive(Debug)] ++pub(crate) struct OwnedRef(pub(crate) *mut crate::backend::v8::bindings::wasm_ref_t); ++impl Drop for OwnedRef { ++ fn drop(&mut self) { ++ unsafe { crate::backend::v8::bindings::wasm_ref_delete(self.0) }; ++ } ++} + + /// A WebAssembly `store` in the `v8` runtime. + pub struct Store { + pub(crate) engine: Engine, + pub(crate) inner: *mut wasm_store_t, + pub(crate) thread_id: ThreadId, ++ pub(crate) owned_exports: Vec, ++ pub(crate) owned_refs: Vec, ++ interrupt: Arc, ++ shared_memories: RefCell)>>, + } + + impl std::fmt::Debug for Store { +@@ -29,10 +51,62 @@ + pub(crate) fn new(engine: crate::engine::Engine) -> Self { + let inner: *mut wasm_store_t = unsafe { wasm_store_new(engine.as_v8().inner.engine) }; + let thread_id = std::thread::current().id(); +- Self { ++ let store = Self { + inner, + engine, + thread_id, ++ owned_exports: Vec::new(), ++ owned_refs: Vec::new(), ++ interrupt: interrupt::StoreInterrupt::new(), ++ shared_memories: RefCell::new(Vec::new()), ++ }; ++ store.interrupt.capture(inner); ++ store ++ } ++ ++ pub(crate) fn shared_memory_ops( ++ &self, ++ memory: *mut crate::backend::v8::bindings::wasm_memory_t, ++ ) -> Arc { ++ use crate::backend::v8::bindings::*; ++ let mut memories = self.shared_memories.borrow_mut(); ++ if let Some((_, ops)) = memories ++ .iter() ++ .find(|(entry, _)| unsafe { wasm_memory_same(wasm_ref_as_memory(entry.0), memory) }) ++ { ++ return ops.clone(); ++ } ++ let ops = interrupt::MemoryInterrupt::new(&self.interrupt); ++ memories.push(( ++ OwnedRef(unsafe { wasm_memory_as_ref(wasm_memory_copy(memory)) }), ++ ops.clone(), ++ )); ++ ops ++ } ++ ++ pub(crate) fn remember_shared_memory( ++ &self, ++ memory: *mut crate::backend::v8::bindings::wasm_memory_t, ++ ops: Arc, ++ ) { ++ use crate::backend::v8::bindings::*; ++ let mut memories = self.shared_memories.borrow_mut(); ++ if !memories ++ .iter() ++ .any(|(entry, _)| unsafe { wasm_memory_same(wasm_ref_as_memory(entry.0), memory) }) ++ { ++ memories.push(( ++ OwnedRef(unsafe { wasm_memory_as_ref(wasm_memory_copy(memory)) }), ++ ops, ++ )); ++ } ++ } ++ ++ pub(crate) fn ensure_running(&self) -> Result<(), crate::RuntimeError> { ++ if self.interrupt.is_terminated() { ++ Err(crate::RuntimeError::new("V8 execution context terminated")) ++ } else { ++ Ok(()) + } + } + +@@ -47,6 +121,12 @@ + + impl Drop for Store { + fn drop(&mut self) { ++ // These vectors uniquely own the export wrappers. Rust export clones ++ // borrow them through their Store, so retain them until Store teardown. ++ self.interrupt.retire(); ++ self.shared_memories.get_mut().clear(); ++ self.owned_exports.clear(); ++ self.owned_refs.clear(); + unsafe { wasm_store_delete(self.inner) } + } + } +diff --git a/src/backend/v8/vm/mod.rs b/src/backend/v8/vm/mod.rs +--- a/src/backend/v8/vm/mod.rs ++++ b/src/backend/v8/vm/mod.rs +@@ -154,7 +154,10 @@ + #[derive(Debug, Clone)] + pub struct VMMemory(pub(super) *mut wasm_memory_t); + struct SharedMemoryPointer(*mut wasm_shared_memory_t); +-pub struct VMSharedMemory(Arc); ++pub struct VMSharedMemory( ++ Arc, ++ pub(crate) Arc, ++); + pub(crate) type VMExternMemory = *mut wasm_memory_t; + + impl SharedMemoryPointer { +@@ -182,7 +185,10 @@ + + impl VMMemory { + /// Created a shared detached memory that can be attached in another Store. +- pub(crate) fn as_shared(&self) -> Result { ++ pub(crate) fn as_shared( ++ &self, ++ ops: Arc, ++ ) -> Result { + let memory_type = unsafe { wasm_memory_type(self.0) }; + let limits = unsafe { wasm_memorytype_limits(memory_type) }; + if !unsafe { (*limits).shared } { +@@ -202,23 +208,36 @@ + )); + } + +- Ok(VMSharedMemory(Arc::new(SharedMemoryPointer::new(shared)))) ++ Ok(VMSharedMemory( ++ Arc::new(SharedMemoryPointer::new(shared)), ++ ops, ++ )) + } + } + + impl VMSharedMemory { + /// We can clone a shared memory handle as we want. + pub(crate) fn clone(&self) -> Self { +- Self(self.0.clone()) ++ Self(self.0.clone(), self.1.clone()) + } + + /// Attach the shared memory handle to a new Store and convert it to VMMemory + pub(crate) fn into_vm_memory(self, store: &mut Store) -> VMMemory { +- let memory = unsafe { wasm_memory_obtain(store.inner, self.0.0) }; ++ self.try_into_vm_memory(store) ++ .expect("cannot attach V8 shared memory") ++ } ++ ++ pub(crate) fn try_into_vm_memory(self, store: &mut Store) -> Result { ++ let memory = self.1.attach(store, self.0.0)?; ++ store ++ .owned_refs ++ .push(crate::backend::v8::store::OwnedRef(unsafe { ++ wasm_memory_as_ref(memory) ++ })); + assert!( + !memory.is_null(), + "Failed to obtain V8 shared memory: wasm_memory_obtain returned null" + ); +- VMMemory(memory) +- } +-} ++ Ok(VMMemory(memory)) ++ } ++} +diff --git a/src/entities/memory/location.rs b/src/entities/memory/location.rs +--- a/src/entities/memory/location.rs ++++ b/src/entities/memory/location.rs +@@ -24,6 +24,10 @@ + + /// See [`crate::SharedMemory`]. + pub(crate) trait SharedMemoryOps { ++ fn terminate_execution_contexts(&self) -> Result<(), AtomicsError> { ++ self.disable_atomics() ++ } ++ + /// See [`crate::SharedMemory::disable_atomics`]. + fn disable_atomics(&self) -> Result<(), AtomicsError> { + Err(AtomicsError::Unimplemented) +diff --git a/src/entities/memory/shared.rs b/src/entities/memory/shared.rs +--- a/src/entities/memory/shared.rs ++++ b/src/entities/memory/shared.rs +@@ -68,6 +68,23 @@ + Memory::new_from_existing(store, memory) + } + ++ /// Attach shared memory, returning an error after V8 terminal cancellation. ++ /// ++ /// This permits task creation to race with terminal cancellation without ++ /// unwinding through the task or linker. Other backends retain their ++ /// existing attachment behavior. ++ pub fn try_attach(self, store: &mut impl AsStoreMut) -> Result { ++ let memory = match self.memory { ++ #[cfg(feature = "v8")] ++ VMSharedMemory::V8(memory) => { ++ let mut store = store.as_store_mut(); ++ VMMemory::V8(memory.try_into_vm_memory(store.inner.store.as_v8_mut())?) ++ } ++ memory => memory.into_vm_memory(store), ++ }; ++ Ok(Memory::new_from_existing(store, memory)) ++ } ++ + /// Create an operations handle that does not keep the underlying memory alive. + pub fn ops(&self) -> MemoryOps { + MemoryOps { +@@ -122,6 +139,16 @@ + } + + impl MemoryOps { ++ /// Terminally cancel execution contexts using this memory. ++ /// ++ /// V8 cancels whole Stores and rejects guest reentry; this is not a ++ /// memory-specific atomic operation. Hosts must retire these contexts. ++ /// Other backends retain their existing shutdown through atomics disabling. ++ #[doc(hidden)] ++ pub fn terminate_execution_contexts(&self) -> Result<(), AtomicsError> { ++ self.shared_ops()?.terminate_execution_contexts() ++ } ++ + #[inline] + fn shared_ops(&self) -> Result<&(dyn SharedMemoryOps + Send + Sync), AtomicsError> { + self.ops +diff --git a/src/vm/impls.rs b/src/vm/impls.rs +--- a/src/vm/impls.rs ++++ b/src/vm/impls.rs +@@ -59,7 +59,11 @@ + #[cfg(feature = "sys")] + Self::Sys(s) => s.0.as_shared().map(VMSharedMemory::Sys), + #[cfg(feature = "v8")] +- Self::V8(s) => s.as_shared().map(VMSharedMemory::V8), ++ // V8 sharing needs its owning Store to preserve interruption state. ++ // The V8 Memory API supplies it; this store-free helper is Sys-only. ++ Self::V8(_) => Err(wasmer_types::MemoryError::Generic( ++ "V8 memory sharing requires its owning Store".to_owned(), ++ )), + #[cfg(feature = "js")] + Self::Js(s) => s.try_clone().map(VMSharedMemory::Js), + } diff --git a/src/wasix/runtime/engine/patches/wasmer/series b/src/wasix/runtime/engine/patches/wasmer/series index 3cc7774f1..9f8b482d6 100644 --- a/src/wasix/runtime/engine/patches/wasmer/series +++ b/src/wasix/runtime/engine/patches/wasmer/series @@ -1 +1,4 @@ -0001-native-ownership-and-terminal-lifetimes.patch +0001-v8-engine-delivery.patch +0002-v8-handle-and-call-ownership.patch +0003-v8-native-cache-profiles.patch +0004-v8-memory-and-terminal-lifetimes.patch diff --git a/src/wasix/runtime/engine/prepare-sources.mts b/src/wasix/runtime/engine/prepare-sources.mts index bc3c4887a..ec77d897f 100644 --- a/src/wasix/runtime/engine/prepare-sources.mts +++ b/src/wasix/runtime/engine/prepare-sources.mts @@ -22,11 +22,35 @@ function inputs(name) { : []; } +export function applyEnginePatches(directory: string, patches: string[]) { + // Match PostgreSQL's patch runner: ignore the enclosing Oliphaunt worktree. + // Otherwise Git-format patches can succeed while silently skipping hunks. + const options = { + cwd: directory, + env: { ...process.env, GIT_CEILING_DIRECTORIES: path.dirname(directory) }, + }; + for (const patch of patches) { + const checked = Bun.spawnSync( + ['git', 'apply', '--check', '--whitespace=error-all', patch], + options, + ); + assert.equal(checked.exitCode, 0, checked.stderr.toString()); + const applied = Bun.spawnSync(['git', 'apply', '--whitespace=error-all', patch], options); + assert.equal(applied.exitCode, 0, applied.stderr.toString()); + } +} + function prepare(name) { const source = path.join(root, 'target/oliphaunt-sources/checkouts', `sdk-${name}`); const directory = path.join(owner, 'crates', name); const output = path.join(directory, 'upstream'); - const signature = JSON.stringify([pins[name].sha256, ...inputs(name).map(sha256File)]); + const patches = inputs(name); + const series = path.join(owner, 'patches', name, 'series'); + const signature = JSON.stringify([ + pins[name].sha256, + existsSync(series) ? sha256File(series) : null, + ...patches.map(sha256File), + ]); const stamp = path.join(directory, '.prepared.json'); if (existsSync(stamp) && existsSync(output)) { const previous = JSON.parse(readFileSync(stamp, 'utf8')); @@ -37,12 +61,7 @@ function prepare(name) { try { cpSync(source, stage, { recursive: true }); rmSync(path.join(stage, '.oliphaunt-source-pin')); - for (const patch of inputs(name)) { - const result = Bun.spawnSync(['git', 'apply', '--check', patch], { cwd: stage }); - assert.equal(result.exitCode, 0, result.stderr.toString()); - const applied = Bun.spawnSync(['git', 'apply', patch], { cwd: stage }); - assert.equal(applied.exitCode, 0, applied.stderr.toString()); - } + applyEnginePatches(stage, patches); promotePathTransactional(stage, output); writeFileSync(stamp, JSON.stringify({ signature, tree: archiveTreeDigest(output) }) + '\n'); } finally { @@ -50,30 +69,32 @@ function prepare(name) { } } -const [operation, plan] = Bun.argv.slice(2); -if (operation === 'plan' && plan) { - mkdirSync(plan, { recursive: true }); - for (const name of ENGINE_SOURCE_CRATES) { - const pin = pins[name]; +if (import.meta.main) { + const [operation, plan] = Bun.argv.slice(2); + if (operation === 'plan' && plan) { + mkdirSync(plan, { recursive: true }); + for (const name of ENGINE_SOURCE_CRATES) { + const pin = pins[name]; + writeFileSync( + path.join(plan, `${name}.json`), + JSON.stringify({ + name: `sdk-${name}`, + kind: 'archive', + url: pin.url, + branch: 'oliphaunt-pinned', + commit: pin.sha256, + sha256: pin.sha256, + stripPrefix: `${name}-${pin.version}`, + }), + ); + } writeFileSync( - path.join(plan, `${name}.json`), - JSON.stringify({ - name: `sdk-${name}`, - kind: 'archive', - url: pin.url, - branch: 'oliphaunt-pinned', - commit: pin.sha256, - sha256: pin.sha256, - stripPrefix: `${name}-${pin.version}`, - }), + path.join(plan, 'pins'), + ENGINE_SOURCE_CRATES.map((name) => path.resolve(plan, `${name}.json`) + '\0').join(''), ); + } else if (operation === 'prepare' && !plan) { + for (const name of ENGINE_SOURCE_CRATES) prepare(name); + } else { + throw new Error('usage: prepare-sources.mts plan DIRECTORY | prepare'); } - writeFileSync( - path.join(plan, 'pins'), - ENGINE_SOURCE_CRATES.map((name) => path.resolve(plan, `${name}.json`) + '\0').join(''), - ); -} else if (operation === 'prepare' && !plan) { - for (const name of ENGINE_SOURCE_CRATES) prepare(name); -} else { - throw new Error('usage: prepare-sources.mts plan DIRECTORY | prepare'); } diff --git a/src/wasix/runtime/engine/prepare-sources.test.mts b/src/wasix/runtime/engine/prepare-sources.test.mts new file mode 100644 index 000000000..b895741e7 --- /dev/null +++ b/src/wasix/runtime/engine/prepare-sources.test.mts @@ -0,0 +1,27 @@ +import assert from 'node:assert/strict'; +import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import test from 'node:test'; +import { applyEnginePatches } from './prepare-sources.mts'; + +test('ordered Git patches apply inside an ignored directory of the enclosing worktree', (t) => { + const root = path.resolve(import.meta.dir, '../../../..'); + const scratch = mkdtempSync(path.join(root, 'target/engine-patch-test-')); + t.after(() => rmSync(scratch, { recursive: true, force: true })); + const source = path.join(scratch, 'source'); + mkdirSync(source); + writeFileSync(path.join(source, 'value.txt'), 'original\n'); + const patches = [ + ['original', 'first'], + ['first', 'second'], + ].map(([before, after], index) => { + const file = path.join(scratch, `${index}.patch`); + writeFileSync( + file, + `diff --git a/value.txt b/value.txt\n--- a/value.txt\n+++ b/value.txt\n@@ -1 +1 @@\n-${before}\n+${after}\n`, + ); + return file; + }); + applyEnginePatches(source, patches); + assert.equal(readFileSync(path.join(source, 'value.txt'), 'utf8'), 'second\n'); +}); diff --git a/src/wasix/runtime/moon.yml b/src/wasix/runtime/moon.yml index 892215cf6..f97f56392 100644 --- a/src/wasix/runtime/moon.yml +++ b/src/wasix/runtime/moon.yml @@ -64,10 +64,10 @@ fileGroups: - engine/crates/**/Cargo.toml - "!engine/crates/*/upstream/**" engine-source: - - engine/**/* - - "!engine/crates/*/upstream/**" - - "!engine/crates/*/prebuilt/**" - - "!engine/crates/*/.prepared.json" + - engine/source.toml + - engine/contract.mts + - engine/prepare-sources.{mts,sh} + - engine/patches/**/* extension-smoke-inputs: - /src/wasix/pgwire-server/Cargo.toml - /src/wasix/pgwire-server/tests/extensions.rs @@ -102,7 +102,19 @@ tasks: bun src/wasix/runtime/engine/test-packaged.mts ;; esac - inputs: ["@group(engine-source)", "@group(cargo-workspace)", "/.config/nextest.toml"] + inputs: + - "@group(cargo-workspace)" + - "@group(packaging-source)" + - "@group(legal-files)" + - "@group(upstream-licenses)" + - engine/package.mts + - engine/test-packaged.mts + - engine/crates/**/Cargo.toml + - engine/crates/**/*.rs + - "!engine/crates/*/upstream/**" + - "!engine/crates/*/prebuilt/**" + - /src/third-party/tools/source-fetch-core.mts + - /tools/release/release-artifact-targets.mts options: cache: false runFromWorkspaceRoot: true @@ -113,6 +125,9 @@ tasks: - liboliphaunt-wasix:engine-sources inputs: - "@group(engine-source)" + - engine/windows/*.{cpp,c,mts,rs,sh} + - "!engine/windows/*.test.mts" + - "!engine/windows/build-caches.mts" - /tools/dev/acquisition.sh outputs: - /target/oliphaunt-wasix/engine/windows-x64-msvc/oliphaunt_wee8.dll diff --git a/src/wasix/runtime/tools/package-carriers.mts b/src/wasix/runtime/tools/package-carriers.mts index ee8cb6cc2..abce97d3a 100644 --- a/src/wasix/runtime/tools/package-carriers.mts +++ b/src/wasix/runtime/tools/package-carriers.mts @@ -36,8 +36,7 @@ import { } from './wasix-extension-cargo-artifact-inventory.mts'; import { packWasixRuntimeNpmCarrier } from './wasix-runtime-npm-carrier.mts'; -import { ENGINE_PAYLOAD_PACKAGE } from '../engine/contract.mts'; -import { isEnginePayloadPart } from '../engine/package.mts'; +import { ENGINE_PAYLOAD_PACKAGE, isEnginePayloadPart } from '../engine/contract.mts'; export const WASIX_PRODUCT = 'liboliphaunt-wasix'; diff --git a/tools/packaging/moon.yml b/tools/packaging/moon.yml index 9801426f8..3194f3922 100644 --- a/tools/packaging/moon.yml +++ b/tools/packaging/moon.yml @@ -36,7 +36,8 @@ tasks: - /src/extensions/tools/extension-upstream-licenses.mts - /src/extensions/external/**/upstream-license-data.json - "@group(upstream-licenses)" - - "/src/{native,wasix}/runtime/THIRD_PARTY_NOTICES.md" + - "/src/native/runtime/THIRD_PARTY_NOTICES.md" + - "/src/wasix/sdks/rust/THIRD_PARTY_NOTICES.md" - "/src/third-party/{icu,openssl}/source.toml" - "**/*.mts" - "**/*.sh"