diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 42def2f64a2..6580a06d54b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -258,7 +258,7 @@ jobs: - uses: Swatinem/rust-cache@v2 if: matrix.use_sccache != true - name: Install LLVM (macOS Apple Silicon) - if: matrix.os == 'macos-12' && !matrix.llvm_url + if: matrix.os == 'macos-13' && !matrix.llvm_url run: | brew install llvm - name: Install LLVM @@ -406,7 +406,7 @@ jobs: if: matrix.use_llvm != true && !endsWith(matrix.build, 'v8') && !endsWith(matrix.build, 'wamr') && !endsWith(matrix.build, 'wasmi') shell: bash run: | - make build-wasmer + ENABLE_LLVM=0 make build-wasmer #- name: Build Wasmer binary on Wasm32-WASI without LLVM # if: matrix.build_wasm # shell: bash @@ -517,7 +517,7 @@ jobs: darwin_aarch64_jsc: name: macOS aarch64 (JSC) - runs-on: macos-12 + runs-on: macos-13 steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable @@ -550,7 +550,7 @@ jobs: darwin_x86_64_jsc: name: macOS x86_64 (JSC) - runs-on: macos-12 + runs-on: macos-13 steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable diff --git a/.github/workflows/cloudcompiler.yaml b/.github/workflows/cloudcompiler.yaml index 6f8765d947a..11a8eb03800 100644 --- a/.github/workflows/cloudcompiler.yaml +++ b/.github/workflows/cloudcompiler.yaml @@ -24,19 +24,19 @@ jobs: with: toolchain: ${{ env.MSRV }} target: ${{ matrix.target }} - - name: Install wasm32-wasi target + - name: Install wasm32-wasip1 target shell: bash run: | - rustup target add wasm32-wasi + rustup target add wasm32-wasip1 - name: Setup Wasmer uses: wasmerio/setup-wasmer@v1 - name: Build cloudcompiler.wasm shell: bash run: | make build-wasmer-wasm && - mkdir target/wasm32-wasi/release/cloudcompiler - cp target/wasm32-wasi/release/wasmer-compiler.wasm target/wasm32-wasi/release/cloudcompiler/cloudcompiler.wasm && - cat << EOF > target/wasm32-wasi/release/cloudcompiler/wasmer.toml + mkdir target/wasm32-wasip1/release/cloudcompiler + cp target/wasm32-wasip1/release/wasmer-compiler.wasm target/wasm32-wasip1/release/cloudcompiler/cloudcompiler.wasm && + cat << EOF > target/wasm32-wasip1/release/cloudcompiler/wasmer.toml [package] name = "${{ secrets.WAPM_DEV_USERNAME }}/cloudcompiler" version = "0.1.0" @@ -61,18 +61,18 @@ jobs: echo $(git tag | tail -n1) > ./version.txt v=$(git describe --tags --abbrev=0) && \ echo "version = ${v}" && - sed -i "s/version = \".*\"/version = \"${v}\"/g" target/wasm32-wasi/release/cloudcompiler/wasmer.toml \ + sed -i "s/version = \".*\"/version = \"${v}\"/g" target/wasm32-wasip1/release/cloudcompiler/wasmer.toml \ - name: Build cloudcompiler.wasm shell: bash run: | git tag && - cat target/wasm32-wasi/release/cloudcompiler/wasmer.toml && + cat target/wasm32-wasip1/release/cloudcompiler/wasmer.toml && echo "ls" && - ls target/wasm32-wasi/release/cloudcompiler + ls target/wasm32-wasip1/release/cloudcompiler - name: Publish to WAPM uses: wasmerio/wapm-publish@v1 with: registry: https://registry.wapm.dev - directory: target/wasm32-wasi/release/cloudcompiler + directory: target/wasm32-wasip1/release/cloudcompiler username: ${{ secrets.WAPM_DEV_USERNAME }} password: ${{ secrets.WAPM_DEV_PASSWORD }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 1c41776f6b4..eafde2d414b 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -131,7 +131,7 @@ jobs: uses: dtolnay/rust-toolchain@stable with: toolchain: nightly - targets: "wasm32-wasi" + targets: "wasm32-wasip1" - name: Install wasm-pack run: | curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh @@ -207,8 +207,8 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - - name: rustup target add wasm32-wasi - run: rustup target add wasm32-wasi + - name: rustup target add wasm32-wasip1 + run: rustup target add wasm32-wasip1 - name: make build-wasmer-wasm run: make build-wasmer-wasm @@ -263,10 +263,10 @@ jobs: build: macos-arm, os: macos-14, }, - { - build: windows-x64, - os: windows-2022, - } + #{ + # build: windows-x64, + # os: windows-2022, + #} ] container: ${{ matrix.metadata.container }} steps: @@ -283,17 +283,19 @@ jobs: - name: Install Nextest uses: taiki-e/install-action@nextest - - name: Install `ninja`, clang` and `mold` on Ubuntu + - name: Install `ninja`, clang`, `binutils` and `mold` on Ubuntu if: startsWith(matrix.metadata.build, 'linux-') shell: bash run: | - sudo apt-get update -y && sudo apt-get install ninja-build clang mold -y + sudo apt-get update -y && sudo apt-get install ninja-build clang mold binutils -y - - name: Install `ninja` on macOS + - name: Install `ninja` and (brew's) `llvm` on macOS if: startsWith(matrix.metadata.build, 'macos-') shell: bash run: | - brew install ninja + brew install ninja llvm@18 + echo "/opt/homebrew/opt/llvm@18/bin" >> $GITHUB_PATH + ls -laR /opt/homebrew/opt/llvm@18/bin/ - name: Install `ninja` on Windows if: startsWith(matrix.metadata.build, 'windows-') @@ -308,7 +310,7 @@ jobs: - name: Test WAMR API if: ${{ matrix.build-what.key == 'wamr' }} - run: ${{ matrix.build-what.build-cmd }} + run: echo $GITHUB_PATH && ${{ matrix.build-what.build-cmd }} - name: Test wasmi API if: ${{ matrix.build-what.key == 'wasmi' }} @@ -500,7 +502,7 @@ jobs: }, { build: macos-x64, - os: macos-12, + os: macos-13, target: x86_64-apple-darwin, exe: '', llvm_url: 'https://github.com/wasmerio/llvm-custom-builds/releases/download/18.x/llvm-darwin-amd64.tar.xz' @@ -560,7 +562,7 @@ jobs: # using gnu-tar is a workaround for https://github.com/actions/cache/issues/403 brew install gnu-tar echo PATH="/usr/local/opt/gnu-tar/libexec/gnubin:$PATH" >> $GITHUB_ENV - if: matrix.metadata.os == 'macos-12' + if: matrix.metadata.os == 'macos-13' - name: Install Rust uses: dtolnay/rust-toolchain@stable with: @@ -608,7 +610,7 @@ jobs: mkdir -p package/winsdk cp -r /tmp/winsdk/* package/winsdk - name: Install LLVM (macOS Apple Silicon) - if: matrix.metadata.os == 'macos-12' && !matrix.metadata.llvm_url + if: matrix.metadata.os == 'macos-13' && !matrix.metadata.llvm_url run: | brew install llvm - name: Install LLVM @@ -754,7 +756,7 @@ jobs: }, { build: macos-x64, - os: macos-12, + os: macos-13, target: x86_64-apple-darwin, exe: '', llvm_url: 'https://github.com/wasmerio/llvm-custom-builds/releases/download/18.x/llvm-darwin-amd64.tar.xz' @@ -815,7 +817,7 @@ jobs: # using gnu-tar is a workaround for https://github.com/actions/cache/issues/403 brew install gnu-tar echo PATH="/usr/local/opt/gnu-tar/libexec/gnubin:$PATH" >> $GITHUB_ENV - if: matrix.metadata.os == 'macos-12' + if: matrix.metadata.os == 'macos-13' - name: Install Rust uses: dtolnay/rust-toolchain@stable with: @@ -824,9 +826,10 @@ jobs: - name: Install Nextest uses: taiki-e/install-action@nextest - name: Install LLVM (macOS Apple Silicon) - if: matrix.metadata.os == 'macos-12' && !matrix.metadata.llvm_url + if: matrix.metadata.os == 'macos-13' && !matrix.metadata.llvm_url run: | brew install llvm + echo "/opt/homebrew/opt/llvm/bin" >> GITHUB_PATH - name: Install LLVM shell: bash if: matrix.metadata.llvm_url @@ -891,11 +894,11 @@ jobs: target: x86_64-unknown-linux-gnu llvm_url: 'https://github.com/wasmerio/llvm-custom-builds/releases/download/18.x/llvm-linux-amd64.tar.xz' - build: macos-x64 - os: macos-12 + os: macos-13 target: x86_64-apple-darwin # we only build the integration-test CLI, we don't run tests - build: macos-arm - os: macos-12 + os: macos-14 target: aarch64-apple-darwin, - build: linux-musl target: x86_64-unknown-linux-musl @@ -1096,47 +1099,16 @@ jobs: - uses: actions/download-artifact@v4 with: name: wasmer-cli-linux-x64 - - name: Cargo Registry Cache - uses: actions/cache@v3 - with: - path: | - ~/.cargo/advisory-db - ~/.cargo/git - ~/.cargo/registry - key: cargo-registry-${{ hashFiles('**/Cargo.lock') }} - - name: Cargo target cache - uses: actions/cache@v3 - with: - path: | - target/ - key: cargo-release-${{ hashFiles('**/Cargo.lock') }} + - uses: wasmerio/setup-wasmer@v2 + - uses: denoland/setup-deno@v1 - run: | - # install rust toolchain - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - . "$HOME/.cargo/env" - - # add wasmer cli to PATH tar -xzf build-wasmer.tar.gz - - docker build -t tmp . - docker run -v $PWD:/app -w /app tmp bash -c " \ - cp ./bin/wasmer /root/.wasmer/bin/wasmer &&\ - export MYSQL_HOST='${{ vars.INTEGRATION_TEST_MYSQL_HOST }}' &&\ - export MYSQL_DBNAME='${{ vars.INTEGRATION_TEST_MYSQL_DBNAME }}' &&\ - export MYSQL_USERNAME='${{ secrets.INTEGRATION_TEST_MYSQL_USERNAME }}' &&\ - export MYSQL_PASSWORD='${{ secrets.INTEGRATION_TEST_MYSQL_PASSWORD }}' &&\ - export MYSQL_PORT='${{ vars.INTEGRATION_TEST_MYSQL_PORT }}' &&\ - export MYSQL_CERT='${{ secrets.INTEGRATION_TEST_MYSQL_CERT }}' &&\ - export PG_HOST='${{ vars.INTEGRATION_TEST_PG_HOST }}' &&\ - export PG_DBNAME='${{ vars.INTEGRATION_TEST_PG_DBNAME }}' &&\ - export PG_USERNAME='${{ secrets.INTEGRATION_TEST_PG_USERNAME }}' &&\ - export PG_PASSWORD='${{ secrets.INTEGRATION_TEST_PG_PASSWORD }}' &&\ - export PG_PORT='${{ vars.INTEGRATION_TEST_PG_PORT }}' &&\ - wasmer config set registry.url https://registry.wasmer.io/graphql &&\ - wasmer login '${{ secrets.WAPM_PROD_TOKEN }}' &&\ - wasmer config set registry.url https://registry.wasmer.wtf/graphql &&\ - wasmer login '${{ secrets.WAPM_DEV_TOKEN }}' &&\ - cargo test --no-fail-fast" + cp ./bin/wasmer ~/.wasmer/bin/wasmer + - name: test + env: + WASMER_REGISTRY: https://registry.wasmer.wtf/graphql + WASMER_TOKEN: ${{ secrets.WAPM_DEV_TOKEN }} + run: deno test --allow-all --parallel - name: notify failure in slack if: failure() run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index a964d818803..5f637093122 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ Looking for changes that affect our C API? See the [C API Changelog](lib/c-api/C ## **Unreleased** +## 5.0.2 - 22/11/2024 + +This release mostly consists of bug fixes and clean ups. ## 5.1.0 - 22/11/2024 ## Added @@ -31,6 +34,7 @@ Looking for changes that affect our C API? See the [C API Changelog](lib/c-api/C ## Fixed + - [#5268](https://github.com/wasmerio/wasmer/pull/5268) Fix musl builds - [#5231](https://github.com/wasmerio/wasmer/pull/5231) Fix LLVM detection - [#5235](https://github.com/wasmerio/wasmer/pull/5235) c-api/README.md: Fix double include in usage section - [#5238](https://github.com/wasmerio/wasmer/pull/5238) Revert "Fix handling of the root dir in `path_create_directory` and `get_inode_at_path_inner`" diff --git a/Cargo.lock b/Cargo.lock index a3d06444a67..7f541e8ed4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,9 +89,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.17" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -104,9 +104,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" @@ -144,15 +144,15 @@ checksum = "70033777eb8b5124a81a1889416543dddef2de240019b674c81285a2635a7e1e" [[package]] name = "anyhow" -version = "1.0.91" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" dependencies = [ "derive_arbitrary", ] @@ -198,7 +198,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -237,7 +237,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" dependencies = [ "anstyle", - "bstr 1.10.0", + "bstr 1.11.0", "doc-comment", "libc", "predicates 3.1.2", @@ -279,7 +279,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -368,7 +368,7 @@ dependencies = [ "bitflags 2.6.0", "cexpr", "clang-sys", - "itertools 0.12.1", + "itertools 0.13.0", "log", "prettyplease", "proc-macro2", @@ -376,7 +376,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -426,12 +426,12 @@ dependencies = [ [[package]] name = "bstr" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" dependencies = [ "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "serde", ] @@ -492,7 +492,7 @@ checksum = "523363cbe1df49b68215efdf500b103ac3b0fb4836aed6d15689a076eadb8fff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -569,7 +569,7 @@ dependencies = [ "semver 1.0.23", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -591,16 +591,16 @@ dependencies = [ "quote", "serde", "serde_json", - "syn 2.0.85", + "syn 2.0.87", "tempfile", "toml 0.8.19", ] [[package]] name = "cc" -version = "1.1.31" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" dependencies = [ "jobserver", "libc", @@ -628,6 +628,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.38" @@ -730,7 +736,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -787,14 +793,14 @@ dependencies = [ [[package]] name = "comfy-table" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" +checksum = "24f165e7b643266ea80cb858aed492ad9280e3e05ce24d4a99d7d7b889b6a4d9" dependencies = [ "crossterm", "strum", "strum_macros", - "unicode-width", + "unicode-width 0.2.0", ] [[package]] @@ -848,7 +854,7 @@ dependencies = [ "encode_unicode", "lazy_static", "libc", - "unicode-width", + "unicode-width 0.1.14", "windows-sys 0.52.0", ] @@ -909,20 +915,11 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "counter" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d458e66999348f56fd3ffcfbb7f7951542075ca8359687c703de6500c1ddccd" -dependencies = [ - "num-traits", -] - [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" dependencies = [ "libc", ] @@ -1129,14 +1126,14 @@ checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crossterm" -version = "0.27.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ "bitflags 2.6.0", "crossterm_winapi", - "libc", "parking_lot", + "rustix", "winapi", ] @@ -1167,9 +1164,9 @@ dependencies = [ [[package]] name = "csv" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" dependencies = [ "csv-core", "itoa", @@ -1198,9 +1195,9 @@ dependencies = [ [[package]] name = "cynic" -version = "3.8.0" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "394a4797bda697d5e028f3bc3fcb8c8c374a2b72cf8d22e0efc0e127c95fd11f" +checksum = "9a41762e03383d7bf4213b9b4e5fb15e232853400b0b738b764d1d2df9649400" dependencies = [ "cynic-proc-macros", "ref-cast", @@ -1208,16 +1205,15 @@ dependencies = [ "serde", "serde_json", "static_assertions", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "cynic-codegen" -version = "3.8.0" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "408ad3d9c8d729e4d89e1aaed76eab918f76169f1bb3aa017c9fc1b39dac3066" +checksum = "3b9e35b6ceec97e397c422585aa7d8c057158c63d2727f15d4ddc47563862798" dependencies = [ - "counter", "cynic-parser", "darling 0.20.10", "once_cell", @@ -1225,15 +1221,15 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.85", - "thiserror", + "syn 2.0.87", + "thiserror 1.0.69", ] [[package]] name = "cynic-parser" -version = "0.4.5" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718f6cd8c54ae5249fd42b0c86639df0100b8a86eea2e5f1b915cde2e1481453" +checksum = "421220154aa57cc1a72c886d6dc068c8fd9fdb81c860cbc299b0a9fce9ce8f08" dependencies = [ "indexmap 2.6.0", "lalrpop-util", @@ -1242,14 +1238,14 @@ dependencies = [ [[package]] name = "cynic-proc-macros" -version = "3.8.0" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f72af69ebf914a975468c4f7e3f81ddc5482e140dd98032d90e7602c535681d" +checksum = "beb9f1756c97b93a7e2faf9c5d58cc21655de6a32984dd8aa7aa076c7e246601" dependencies = [ "cynic-codegen", "darling 0.20.10", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1297,7 +1293,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1319,7 +1315,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1372,13 +1368,13 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1422,7 +1418,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1442,7 +1438,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "unicode-xid", ] @@ -1455,7 +1451,7 @@ dependencies = [ "console", "shell-words", "tempfile", - "thiserror", + "thiserror 1.0.69", "zeroize", ] @@ -1531,7 +1527,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1614,7 +1610,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1666,7 +1662,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1687,7 +1683,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1763,9 +1759,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "fern" @@ -1807,9 +1803,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", @@ -1934,9 +1930,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ "fastrand", "futures-core", @@ -1953,7 +1949,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2008,7 +2004,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" dependencies = [ - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -2032,7 +2028,7 @@ checksum = "b0e085ded9f1267c32176b40921b9754c474f7dd96f7e808d4a982e48aa1e854" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2065,9 +2061,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" dependencies = [ "aho-corasick", - "bstr 1.10.0", + "bstr 1.11.0", "log", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -2084,9 +2080,9 @@ dependencies = [ [[package]] name = "graphql-ws-client" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1d67ba08667c527d817fefa59feb12e87c7045261bcaecab3bcfb02d4a0102" +checksum = "1aa2e818b9e62654f2039d524c92a19495fcc2b0139ffc0354956865b3c7e939" dependencies = [ "async-channel", "cynic", @@ -2097,7 +2093,7 @@ dependencies = [ "pin-project", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tungstenite 0.24.0", ] @@ -2201,9 +2197,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "heapless" @@ -2275,6 +2271,15 @@ dependencies = [ "digest", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "http" version = "1.1.0" @@ -2339,9 +2344,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" dependencies = [ "bytes", "futures-channel", @@ -2367,7 +2372,7 @@ dependencies = [ "http", "hyper", "hyper-util", - "rustls 0.23.15", + "rustls 0.23.19", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -2408,9 +2413,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -2448,6 +2453,124 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "id-arena" version = "2.2.1" @@ -2473,12 +2596,23 @@ dependencies = [ [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -2491,7 +2625,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "same-file", "walkdir", "winapi-util", @@ -2515,27 +2649,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.1", "serde", ] -[[package]] -name = "indexmap-nostd" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" - [[package]] name = "indicatif" -version = "0.17.8" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281" dependencies = [ "console", - "instant", "number_prefix", "portable-atomic", - "unicode-width", + "unicode-width 0.2.0", + "web-time", ] [[package]] @@ -2549,7 +2677,7 @@ dependencies = [ "libc", "llvm-sys", "once_cell", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2560,7 +2688,7 @@ checksum = "9dd28cfd4cfba665d47d31c08a6ba637eed16770abca2eccbbc3ca831fef1e44" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2602,9 +2730,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.40.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6593a41c7a73841868772495db7dc1e8ecab43bb5c0b6da2059246c4b506ab60" +checksum = "7e9ffc4d4892617c50a928c52b2961cb5174b6fc6ebf252b2fac9d21955c48b8" dependencies = [ "console", "lazy_static", @@ -2614,15 +2742,6 @@ dependencies = [ "similar", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "interfaces" version = "0.0.9" @@ -2690,11 +2809,20 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "7a73e9fe3c49d7afb2ace819fa181a287ce54a0983eda4e0eb05c22f82ffe534" [[package]] name = "jobserver" @@ -2716,11 +2844,11 @@ dependencies = [ [[package]] name = "lalrpop-util" -version = "0.20.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" +checksum = "feee752d43abd0f4807a921958ab4131f692a44d4d599733d4419c5d586176ce" dependencies = [ - "regex-automata 0.4.8", + "rustversion", ] [[package]] @@ -2746,19 +2874,18 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.161" +version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" [[package]] name = "libfuzzer-sys" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +checksum = "9b9569d2f74e257076d8c6bfa73fb505b46b851e51ddaecc825944aa3bed17fa" dependencies = [ "arbitrary", "cc", - "once_cell", ] [[package]] @@ -2773,9 +2900,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libredox" @@ -2819,6 +2946,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "litrs" version = "0.4.1" @@ -2882,7 +3015,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.8.5", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2966,11 +3099,11 @@ dependencies = [ [[package]] name = "macro-wasmer-universal-test" -version = "5.0.1" +version = "5.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3070,9 +3203,9 @@ dependencies = [ [[package]] name = "minicov" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "def6d99771d7c499c26ad4d40eb6645eafd3a1553b35fc26ea5a489a45e82d9a" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" dependencies = [ "cc", "walkdir", @@ -3144,7 +3277,7 @@ checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3185,7 +3318,7 @@ checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ "bitflags 2.6.0", "cfg-if", - "cfg_aliases", + "cfg_aliases 0.1.1", "libc", "memoffset 0.9.1", ] @@ -3328,7 +3461,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c62dcb6174f9cb326eac248f07e955d5d559c272730b6c03e396b443b562788" dependencies = [ - "bstr 1.10.0", + "bstr 1.11.0", "normpath", "winapi", ] @@ -3356,7 +3489,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3399,7 +3532,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3488,7 +3621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ "memchr", - "thiserror", + "thiserror 1.0.69", "ucd-trie", ] @@ -3512,7 +3645,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3553,7 +3686,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3690,7 +3823,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3746,7 +3879,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3766,7 +3899,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "version_check", "yansi", ] @@ -3808,7 +3941,7 @@ checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3830,45 +3963,49 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quinn" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" dependencies = [ "bytes", "pin-project-lite", "quinn-proto", "quinn-udp", "rustc-hash 2.0.0", - "rustls 0.23.15", + "rustls 0.23.19", "socket2", - "thiserror", + "thiserror 2.0.3", "tokio", "tracing", ] [[package]] name = "quinn-proto" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" dependencies = [ "bytes", + "getrandom", "rand", "ring", "rustc-hash 2.0.0", - "rustls 0.23.15", + "rustls 0.23.19", + "rustls-pki-types", "slab", - "thiserror", + "thiserror 2.0.3", "tinyvec", "tracing", + "web-time", ] [[package]] name = "quinn-udp" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" +checksum = "7d5a626c6807713b15cac82a6acaccd6043c9a5408c24baae07611fec3f243da" dependencies = [ + "cfg_aliases 0.2.1", "libc", "once_cell", "socket2", @@ -3961,7 +4098,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -3981,7 +4118,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -4005,7 +4142,7 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -4020,9 +4157,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -4076,9 +4213,9 @@ checksum = "e3a8614ee435691de62bcffcf4a66d91b3594bf1428a5722e79103249a095690" [[package]] name = "reqwest" -version = "0.12.8" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "async-compression", "base64", @@ -4103,7 +4240,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.15", + "rustls 0.23.19", "rustls-pemfile", "rustls-pki-types", "serde", @@ -4167,7 +4304,7 @@ checksum = "09cb82b74b4810f07e460852c32f522e979787691b0b7b7439fe473e49d49b2f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -4248,9 +4385,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ "bitflags 2.6.0", "errno", @@ -4275,9 +4412,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.15" +version = "0.23.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" +checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" dependencies = [ "log", "once_cell", @@ -4315,6 +4452,9 @@ name = "rustls-pki-types" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +dependencies = [ + "web-time", +] [[package]] name = "rustls-webpki" @@ -4404,9 +4544,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -4433,7 +4573,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -4465,7 +4605,7 @@ checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -4483,9 +4623,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" dependencies = [ "core-foundation-sys", "libc", @@ -4517,9 +4657,9 @@ dependencies = [ [[package]] name = "semver-parser" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" dependencies = [ "pest", ] @@ -4532,9 +4672,9 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" [[package]] name = "serde" -version = "1.0.213" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -4571,13 +4711,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.213" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -4588,14 +4728,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -4671,7 +4811,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -4829,16 +4969,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "string-interner" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c6a0d765f5807e98a091107bae0a56ea3799f66a5de47b2c84c94a39c09974e" -dependencies = [ - "cfg-if", - "hashbrown 0.14.5", -] - [[package]] name = "strsim" version = "0.10.0" @@ -4867,7 +4997,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -4889,9 +5019,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.85" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -4900,18 +5030,29 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "tar" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ff6c40d3aedb5e06b57c6f669ad17ab063dd1e63d977c6a88e7f4dfa4f04020" +checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" dependencies = [ "filetime", "libc", @@ -4938,9 +5079,9 @@ checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" [[package]] name = "tempfile" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", @@ -5020,7 +5161,7 @@ checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -5047,22 +5188,42 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.65" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.3", ] [[package]] name = "thiserror-impl" -version = "1.0.65" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] @@ -5117,6 +5278,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -5152,15 +5323,15 @@ dependencies = [ "log", "regex", "serde_json", - "thiserror", + "thiserror 1.0.69", "url", ] [[package]] name = "tokio" -version = "1.41.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", @@ -5181,7 +5352,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -5211,7 +5382,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.15", + "rustls 0.23.19", "rustls-pki-types", "tokio", ] @@ -5242,7 +5413,7 @@ checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -5282,7 +5453,7 @@ checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" dependencies = [ "futures-util", "log", - "rustls 0.23.15", + "rustls 0.23.19", "rustls-native-certs", "rustls-pki-types", "tokio", @@ -5425,7 +5596,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -5498,7 +5669,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" dependencies = [ "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -5549,7 +5720,7 @@ dependencies = [ "rustls 0.22.4", "rustls-pki-types", "sha1", - "thiserror", + "thiserror 1.0.69", "url", "utf-8", ] @@ -5567,10 +5738,10 @@ dependencies = [ "httparse", "log", "rand", - "rustls 0.23.15", + "rustls 0.23.19", "rustls-pki-types", "sha1", - "thiserror", + "thiserror 1.0.69", "utf-8", ] @@ -5634,9 +5805,9 @@ checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-normalization" @@ -5659,6 +5830,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -5719,7 +5896,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5c400339a9d1d17be34257d0b407e91d64af335e5b4fa49f4bf28467fc8d635" dependencies = [ "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -5750,7 +5927,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.85", + "syn 2.0.87", "toml 0.5.11", "uniffi_meta", ] @@ -5815,7 +5992,7 @@ dependencies = [ "flate2", "log", "once_cell", - "rustls 0.23.15", + "rustls 0.23.19", "rustls-pki-types", "url", "webpki-roots", @@ -5823,12 +6000,12 @@ dependencies = [ [[package]] name = "url" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna 1.0.3", "percent-encoding", "serde", ] @@ -5845,6 +6022,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -5880,7 +6069,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "virtual-fs" -version = "0.19.0" +version = "0.20.0" dependencies = [ "anyhow", "async-trait", @@ -5902,7 +6091,7 @@ dependencies = [ "shared-buffer", "slab", "tempfile", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "tracing-test", @@ -5913,7 +6102,7 @@ dependencies = [ [[package]] name = "virtual-mio" -version = "0.5.0" +version = "0.6.0" dependencies = [ "async-trait", "bytes", @@ -5921,13 +6110,13 @@ dependencies = [ "mio", "serde", "socket2", - "thiserror", + "thiserror 1.0.69", "tracing", ] [[package]] name = "virtual-net" -version = "0.11.0" +version = "0.12.0" dependencies = [ "anyhow", "async-trait", @@ -5948,7 +6137,7 @@ dependencies = [ "serial_test", "smoltcp", "socket2", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-serde", "tokio-tungstenite 0.21.0", @@ -6024,13 +6213,13 @@ dependencies = [ [[package]] name = "wai-bindgen-wasmer" -version = "0.31.0" +version = "0.32.0" dependencies = [ "anyhow", "async-trait", "bitflags 1.3.2", "once_cell", - "thiserror", + "thiserror 1.0.69", "tracing", "wai-bindgen-wasmer-impl", "wasmer", @@ -6109,14 +6298,14 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi-test-generator" -version = "5.0.1" +version = "5.1.0" dependencies = [ "glob", "gumdrop", "serde", "serde_json", "tempfile", - "wast", + "wast 216.0.0", ] [[package]] @@ -6141,7 +6330,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -6175,7 +6364,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6209,14 +6398,14 @@ checksum = "c97b2ef2c8d627381e51c071c2ab328eac606d3f69dd82bcbca20a9e389d95f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] name = "wasm-coredump-builder" -version = "0.1.22" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ca262b320e4530a60946ba16a1cbf987d3f7d4aa6a953bfcc96e179e3e7458" +checksum = "e67c1c448581871272b2fb7b1a2ee373a4bd295b4d214c92b9938047e7bb867a" dependencies = [ "wasm-coredump-encoder", "wasm-coredump-types", @@ -6225,9 +6414,9 @@ dependencies = [ [[package]] name = "wasm-coredump-encoder" -version = "0.1.22" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f36ccfe604720ce093fce7d7b0d609c086c646ec4bb9bba58cb9f4dc2c5623" +checksum = "45c2fc6340ab2c342bbe706c321e46af41ebef4a0253d134e551abb4780b6d53" dependencies = [ "leb128", "wasm-coredump-types", @@ -6235,9 +6424,9 @@ dependencies = [ [[package]] name = "wasm-coredump-types" -version = "0.1.22" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2763d9807903c461b41275a13489396d04695d7bc365743b8ea430cfd72f336" +checksum = "ce6930c202f9dabb7effcb8105a425ee68be0c2ab1d2a0d12fef107b17b92f7c" [[package]] name = "wasm-encoder" @@ -6266,6 +6455,16 @@ dependencies = [ "leb128", ] +[[package]] +name = "wasm-encoder" +version = "0.220.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebf48234b389415b226a4daef6562933d38c7b28a8b8f64c5c4130dad1561ab7" +dependencies = [ + "leb128", + "wasmparser 0.220.0", +] + [[package]] name = "wasm-smith" version = "0.4.5" @@ -6293,19 +6492,21 @@ dependencies = [ [[package]] name = "wasmer" -version = "5.0.1" +version = "5.1.0" dependencies = [ "anyhow", "bindgen", "bytes", "cfg-if", "cmake", + "derive_more 1.0.0", "hashbrown 0.11.2", "indexmap 1.9.3", "js-sys", "loupe", "macro-wasmer-universal-test", "more-asserts", + "paste", "rustc-demangle", "rusty_jsc", "seq-macro", @@ -6315,7 +6516,7 @@ dependencies = [ "tar", "target-lexicon 0.12.16", "tempfile", - "thiserror", + "thiserror 1.0.69", "tracing", "ureq", "wasm-bindgen", @@ -6330,6 +6531,7 @@ dependencies = [ "wasmi_c_api_impl", "wasmparser 0.216.0", "wat", + "which", "windows-sys 0.59.0", "xz", "zip", @@ -6337,7 +6539,7 @@ dependencies = [ [[package]] name = "wasmer-argus" -version = "5.0.1" +version = "5.1.0" dependencies = [ "anyhow", "async-trait", @@ -6407,7 +6609,7 @@ dependencies = [ [[package]] name = "wasmer-c-api" -version = "5.0.1" +version = "5.1.0" dependencies = [ "cbindgen", "cfg-if", @@ -6417,7 +6619,7 @@ dependencies = [ "lazy_static", "libc", "paste", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "tracing-subscriber", @@ -6437,7 +6639,7 @@ dependencies = [ [[package]] name = "wasmer-c-api-test-runner" -version = "5.0.1" +version = "5.1.0" dependencies = [ "cc", "regex", @@ -6447,7 +6649,7 @@ dependencies = [ [[package]] name = "wasmer-cache" -version = "5.0.1" +version = "5.1.0" dependencies = [ "blake3", "clap", @@ -6458,14 +6660,14 @@ dependencies = [ "hex", "rand", "tempfile", - "thiserror", + "thiserror 1.0.69", "wasmer", "wasmer-compiler-singlepass", ] [[package]] name = "wasmer-capi-examples-runner" -version = "5.0.1" +version = "5.1.0" dependencies = [ "cc", "regex", @@ -6475,7 +6677,7 @@ dependencies = [ [[package]] name = "wasmer-cli" -version = "5.0.1" +version = "5.1.0" dependencies = [ "anyhow", "assert_cmd 2.0.16", @@ -6534,7 +6736,7 @@ dependencies = [ "tar", "target-lexicon 0.12.16", "tempfile", - "thiserror", + "thiserror 1.0.69", "time 0.1.45", "time 0.3.36", "tldextract", @@ -6569,7 +6771,7 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "5.0.1" +version = "5.1.0" dependencies = [ "backtrace", "bytes", @@ -6592,7 +6794,7 @@ dependencies = [ "shared-buffer", "smallvec", "target-lexicon 0.12.16", - "thiserror", + "thiserror 1.0.69", "wasmer-types", "wasmer-vm", "wasmparser 0.216.0", @@ -6602,7 +6804,7 @@ dependencies = [ [[package]] name = "wasmer-compiler-cli" -version = "5.0.1" +version = "5.1.0" dependencies = [ "anyhow", "bytesize", @@ -6623,7 +6825,7 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "5.0.1" +version = "5.1.0" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -6643,7 +6845,7 @@ dependencies = [ [[package]] name = "wasmer-compiler-llvm" -version = "5.0.1" +version = "5.1.0" dependencies = [ "byteorder", "cc", @@ -6665,7 +6867,7 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "5.0.1" +version = "5.1.0" dependencies = [ "byteorder", "dynasm", @@ -6699,14 +6901,14 @@ dependencies = [ "serde_json", "serde_yml", "tempfile", - "thiserror", + "thiserror 1.0.69", "toml 0.8.19", "url", ] [[package]] name = "wasmer-derive" -version = "5.0.1" +version = "5.1.0" dependencies = [ "compiletest_rs", "proc-macro-error2", @@ -6746,7 +6948,7 @@ dependencies = [ [[package]] name = "wasmer-integration-tests-cli" -version = "5.0.1" +version = "5.1.0" dependencies = [ "anyhow", "assert_cmd 2.0.16", @@ -6773,11 +6975,11 @@ dependencies = [ [[package]] name = "wasmer-integration-tests-ios" -version = "5.0.1" +version = "5.1.0" [[package]] name = "wasmer-journal" -version = "0.13.0" +version = "0.14.0" dependencies = [ "anyhow", "async-trait", @@ -6793,7 +6995,7 @@ dependencies = [ "serde_json", "shared-buffer", "tempfile", - "thiserror", + "thiserror 1.0.69", "tracing", "tracing-test", "virtual-fs", @@ -6804,7 +7006,7 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "5.0.1" +version = "5.1.0" dependencies = [ "wasmer", "wasmer-types", @@ -6813,7 +7015,7 @@ dependencies = [ [[package]] name = "wasmer-package" -version = "0.2.0" +version = "0.3.0" dependencies = [ "anyhow", "bytes", @@ -6831,7 +7033,7 @@ dependencies = [ "shared-buffer", "tar", "tempfile", - "thiserror", + "thiserror 1.0.69", "toml 0.8.19", "ureq", "url", @@ -6841,9 +7043,9 @@ dependencies = [ [[package]] name = "wasmer-swift" -version = "0.3.0" +version = "0.4.0" dependencies = [ - "thiserror", + "thiserror 1.0.69", "tokio", "uniffi", "virtual-fs", @@ -6855,7 +7057,7 @@ dependencies = [ [[package]] name = "wasmer-sys-utils" -version = "0.31.0" +version = "0.32.0" dependencies = [ "libc", "region", @@ -6869,7 +7071,7 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "5.0.1" +version = "5.1.0" dependencies = [ "bytecheck 0.6.12", "enum-iterator", @@ -6885,13 +7087,13 @@ dependencies = [ "serde_bytes", "sha2", "target-lexicon 0.12.16", - "thiserror", + "thiserror 1.0.69", "xxhash-rust", ] [[package]] name = "wasmer-vm" -version = "5.0.1" +version = "5.1.0" dependencies = [ "backtrace", "cc", @@ -6911,7 +7113,7 @@ dependencies = [ "region", "scopeguard", "serde", - "thiserror", + "thiserror 1.0.69", "tracing", "wasmer-types", "windows-sys 0.59.0", @@ -6919,7 +7121,7 @@ dependencies = [ [[package]] name = "wasmer-wasix" -version = "0.31.0" +version = "0.32.0" dependencies = [ "anyhow", "async-trait", @@ -6969,7 +7171,7 @@ dependencies = [ "tempfile", "terminal_size", "termios", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "toml 0.8.19", @@ -7006,7 +7208,7 @@ dependencies = [ [[package]] name = "wasmer-wasix-types" -version = "0.31.0" +version = "0.32.0" dependencies = [ "anyhow", "bitflags 1.3.2", @@ -7029,24 +7231,24 @@ dependencies = [ [[package]] name = "wasmer-wast" -version = "5.0.1" +version = "5.1.0" dependencies = [ "anyhow", "futures", "serde", "tempfile", - "thiserror", + "thiserror 1.0.69", "tokio", "virtual-fs", "wasmer", "wasmer-types", "wasmer-wasix", - "wast", + "wast 216.0.0", ] [[package]] name = "wasmer-workspace" -version = "5.0.1" +version = "5.1.0" dependencies = [ "anyhow", "build-deps", @@ -7085,9 +7287,9 @@ dependencies = [ [[package]] name = "wasmi" -version = "0.38.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b07e84e3bcdab2f4301827623260ada2557596ca462f7470b60f5182a25270b1" +checksum = "a19af97fcb96045dd1d6b4d23e2b4abdbbe81723dbc5c9f016eb52145b320063" dependencies = [ "arrayvec 0.7.6", "multi-stash", @@ -7096,14 +7298,14 @@ dependencies = [ "wasmi_collections", "wasmi_core", "wasmi_ir", - "wasmparser-nostd", + "wasmparser 0.221.2", ] [[package]] name = "wasmi_c_api_impl" -version = "0.38.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf549ed7d6488b41acb6950afb07bd84be1cf655ae50b4bc2f24f1b30aba554" +checksum = "45e45f29eb7b0a2c0789c3c8075fc9c2c05182d6be2222702c6c848f72a2c2df" dependencies = [ "wasmi", "wasmi_c_api_macros", @@ -7111,9 +7313,9 @@ dependencies = [ [[package]] name = "wasmi_c_api_macros" -version = "0.38.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f96d000c5be4f27470f7d89550aef550ffd971273469a35fa01216f165528c57" +checksum = "e03aa7908b941120f347018583d474de0950ca1eae5bc3f6cb680e0f9fbd7695" dependencies = [ "proc-macro2", "quote", @@ -7121,32 +7323,25 @@ dependencies = [ [[package]] name = "wasmi_collections" -version = "0.38.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d0fd5f4f2c4fe0c98554bb7293108ed2b1d0c124dce0974f999de7d517d37bc" -dependencies = [ - "ahash 0.8.11", - "hashbrown 0.14.5", - "string-interner", -] +checksum = "e80d6b275b1c922021939d561574bf376613493ae2b61c6963b15db0e8813562" [[package]] name = "wasmi_core" -version = "0.38.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a5f7bbd933a0fb3bac6c541f8bd90c0c8adcd91bb3ac088a2088995325b3d9" +checksum = "3a8c51482cc32d31c2c7ff211cd2bedd73c5bd057ba16a2ed0110e7a96097c33" dependencies = [ "downcast-rs", "libm", - "num-traits", - "paste", ] [[package]] name = "wasmi_ir" -version = "0.38.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3345445247388df2b5b35250a30c9209c27c8d2c6db1bf4c89b65636264bf9" +checksum = "6e431a14c186db59212a88516788bd68ed51f87aa1e08d1df742522867b5289a" dependencies = [ "wasmi_core", ] @@ -7176,12 +7371,22 @@ dependencies = [ ] [[package]] -name = "wasmparser-nostd" -version = "0.100.2" +name = "wasmparser" +version = "0.220.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e246c2772ce3ebc83f89a2d4487ac5794cad6c309b2071818a88c7db7c36d87b" +dependencies = [ + "bitflags 2.6.0", + "indexmap 2.6.0", +] + +[[package]] +name = "wasmparser" +version = "0.221.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" +checksum = "9845c470a2e10b61dd42c385839cdd6496363ed63b5c9e420b5488b77bd22083" dependencies = [ - "indexmap-nostd", + "bitflags 2.6.0", ] [[package]] @@ -7203,17 +7408,30 @@ dependencies = [ "bumpalo", "leb128", "memchr", - "unicode-width", + "unicode-width 0.1.14", "wasm-encoder 0.216.0", ] +[[package]] +name = "wast" +version = "220.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e708c8de08751fd66e70961a32bae9d71901f14a70871e181cb8461a3bb3165" +dependencies = [ + "bumpalo", + "leb128", + "memchr", + "unicode-width 0.2.0", + "wasm-encoder 0.220.0", +] + [[package]] name = "wat" -version = "1.216.0" +version = "1.220.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac0409090fb5154f95fb5ba3235675fd9e579e731524d63b6a2f653e1280c82a" +checksum = "de4f1d7d59614ba690541360102b995c4eb1b9ed373701d5102cc1a968b1c5a3" dependencies = [ - "wast", + "wast 220.0.0", ] [[package]] @@ -7251,6 +7469,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 = "7.0.0-rc.2" @@ -7275,7 +7503,7 @@ dependencies = [ "serde_json", "sha2", "shared-buffer", - "thiserror", + "thiserror 1.0.69", "url", ] @@ -7303,6 +7531,18 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" +[[package]] +name = "which" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9cad3279ade7346b96e38731a641d7343dd6a53d55083dd54eadfa5a1b38c6b" +dependencies = [ + "either", + "home", + "rustix", + "winsafe", +] + [[package]] name = "winapi" version = "0.3.9" @@ -7539,6 +7779,24 @@ dependencies = [ "memchr", ] +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "xattr" version = "1.3.1" @@ -7580,6 +7838,30 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -7598,7 +7880,28 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure", ] [[package]] @@ -7618,7 +7921,29 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] @@ -7643,7 +7968,7 @@ dependencies = [ "pbkdf2", "rand", "sha1", - "thiserror", + "thiserror 1.0.69", "time 0.3.36", "zeroize", "zopfli", diff --git a/Cargo.toml b/Cargo.toml index ed603d8d636..534eef3a67a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,7 +87,7 @@ version = "5.1.0" [workspace.dependencies] # Repo-local crates -wasmer-package = { version = "0.2.0", path = "lib/package" } +wasmer-package = { version = "0.3.0", path = "lib/package" } wasmer-config = { path = "./lib/config" } wasmer-wasix = { path = "./lib/wasix" } diff --git a/Makefile b/Makefile index 09ac693f124..4af167a9703 100644 --- a/Makefile +++ b/Makefile @@ -383,7 +383,7 @@ check-wasmer: $(CARGO_BINARY) check $(CARGO_TARGET_FLAG) --manifest-path lib/cli/Cargo.toml $(compiler_features) --bin wasmer --locked check-wasmer-wasm: - $(CARGO_BINARY) check --manifest-path lib/cli-compiler/Cargo.toml --target wasm32-wasi --features singlepass,cranelift --bin wasmer-compiler --locked + $(CARGO_BINARY) check --manifest-path lib/cli-compiler/Cargo.toml --target wasm32-wasip1 --features singlepass,cranelift --bin wasmer-compiler --locked check-capi: RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) check $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml \ @@ -414,7 +414,7 @@ bench: $(CARGO_BINARY) bench $(CARGO_TARGET_FLAG) $(compiler_features) build-wasmer-wasm: - $(CARGO_BINARY) build --release --manifest-path lib/cli-compiler/Cargo.toml --target wasm32-wasi --features singlepass,cranelift --bin wasmer-compiler --locked + $(CARGO_BINARY) build --release --manifest-path lib/cli-compiler/Cargo.toml --target wasm32-wasip1 --features singlepass,cranelift --bin wasmer-compiler --locked # For best results ensure the release profile looks like the following # in Cargo.toml: @@ -672,20 +672,20 @@ test-wasix: build-wasmer test-integration-cli: build-wasmer build-capi package-capi-headless package distribution cp ./dist/wasmer.tar.gz ./link.tar.gz - rustup target add wasm32-wasi + rustup target add wasm32-wasip1 WASMER_DIR=`pwd`/package $(CARGO_BINARY) test $(CARGO_TARGET_FLAG) --features webc_runner --no-fail-fast -p wasmer-integration-tests-cli --locked # Before running this in the CI, we need to set up link.tar.gz and /cache/wasmer-[target].tar.gz test-integration-cli-ci: require-nextest - rustup target add wasm32-wasi + rustup target add wasm32-wasip1 $(CARGO_BINARY) nextest run $(CARGO_TARGET_FLAG) --features webc_runner -p wasmer-integration-tests-cli --locked test-integration-cli-wamr-ci: require-nextest build-wasmer-wamr - rustup target add wasm32-wasi + rustup target add wasm32-wasip1 $(CARGO_BINARY) nextest run $(CARGO_TARGET_FLAG) --features webc_runner,wamr -p wasmer-integration-tests-cli --locked --no-fail-fast -E "not (test(deploy) | test(snapshot) | test(login) | test(init) | test(gen_c_header) | test(up_to_date) | test(publish) | test(create) | test(whoami) | test(config) | test(c_flags))" test-integration-cli-wasmi-ci: require-nextest build-wasmer-wasmi - rustup target add wasm32-wasi + rustup target add wasm32-wasip1 $(CARGO_BINARY) nextest run $(CARGO_TARGET_FLAG) --features webc_runner,wamr -p wasmer-integration-tests-cli --locked --no-fail-fast -E "not (test(deploy) | test(snapshot) | test(login) | test(init) | test(gen_c_header) | test(up_to_date) | test(publish) | test(create) | test(whoami) | test(config) | test(c_flags))" diff --git a/benches/run.rs b/benches/run.rs index c3fbe08cf08..f1f0617ade2 100644 --- a/benches/run.rs +++ b/benches/run.rs @@ -1,8 +1,9 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use wasmer::*; +use wasmer::{sys::*, *}; static BENCHMARKS_ARTIFACTS_BASE_URL: &str = "https://pub-083d1a0568d446d1aa5b2e07bd16983b.r2.dev"; +#[allow(unreachable_code)] fn get_engine() -> Engine { #[cfg(feature = "llvm")] return LLVM::new().into(); @@ -30,7 +31,7 @@ pub fn run_fn(c: &mut Criterion, module: &[u8], name: &str, input: i64) { .unwrap(); b.iter(|| { - func.call(&mut store, input); + _ = func.call(&mut store, input); }) }); } diff --git a/deny.toml b/deny.toml index 35a9d17436f..5987e8f6307 100644 --- a/deny.toml +++ b/deny.toml @@ -100,6 +100,7 @@ allow = [ "OpenSSL", "BSD-3-Clause", "Unicode-DFS-2016", + "Unicode-3.0", # BOOST source license # NOTE: not to be confused with business source license! "BSL-1.0", diff --git a/examples/features.rs b/examples/features.rs index 53830eace4f..c732afbfb89 100644 --- a/examples/features.rs +++ b/examples/features.rs @@ -10,7 +10,11 @@ //! //! Ready? -use wasmer::{imports, sys::EngineBuilder, wat2wasm, Features, Instance, Module, Store, Value}; +use wasmer::{ + imports, + sys::{EngineBuilder, Features}, + wat2wasm, Instance, Module, Store, Value, +}; use wasmer_compiler_cranelift::Cranelift; fn main() -> anyhow::Result<()> { diff --git a/examples/metering.rs b/examples/metering.rs index 2b5e78a0193..a9551cb96f7 100644 --- a/examples/metering.rs +++ b/examples/metering.rs @@ -17,8 +17,11 @@ use anyhow::bail; use std::sync::Arc; use wasmer::wasmparser::Operator; -use wasmer::CompilerConfig; -use wasmer::{imports, sys::EngineBuilder, wat2wasm, Instance, Module, Store, TypedFunction}; +use wasmer::{ + imports, + sys::{CompilerConfig, EngineBuilder}, + wat2wasm, Instance, Module, Store, TypedFunction, +}; use wasmer_compiler_cranelift::Cranelift; use wasmer_middlewares::{ metering::{get_remaining_points, set_remaining_points, MeteringPoints}, diff --git a/examples/tunables_limit_memory.rs b/examples/tunables_limit_memory.rs index da2c7a4ece1..33e3b69e351 100644 --- a/examples/tunables_limit_memory.rs +++ b/examples/tunables_limit_memory.rs @@ -1,14 +1,16 @@ use std::ptr::NonNull; +use wasmer_compiler_cranelift::Cranelift; +// This is to be able to set the tunables use wasmer::{ imports, - vm::{self, MemoryError, MemoryStyle, TableStyle, VMMemoryDefinition, VMTableDefinition}, - wat2wasm, BaseTunables, Engine, Instance, Memory, MemoryType, Module, Pages, Store, TableType, - Target, Tunables, + sys::{ + vm::{VMMemory, VMMemoryDefinition, VMTable, VMTableDefinition}, + BaseTunables, NativeEngineExt, Target, Tunables, + }, + wat2wasm, Engine, Instance, Memory, MemoryError, MemoryStyle, MemoryType, Module, Pages, Store, + TableStyle, TableType, }; -use wasmer_compiler_cranelift::Cranelift; -// This is to be able to set the tunables -use wasmer::NativeEngineExt; /// A custom tunables that allows you to set a memory limit. /// @@ -86,7 +88,7 @@ impl Tunables for LimitingTunables { &self, ty: &MemoryType, style: &MemoryStyle, - ) -> Result { + ) -> Result { let adjusted = self.adjust_memory(ty); self.validate_memory(&adjusted)?; self.base.create_host_memory(&adjusted, style) @@ -100,7 +102,7 @@ impl Tunables for LimitingTunables { ty: &MemoryType, style: &MemoryStyle, vm_definition_location: NonNull, - ) -> Result { + ) -> Result { let adjusted = self.adjust_memory(ty); self.validate_memory(&adjusted)?; self.base @@ -110,7 +112,7 @@ impl Tunables for LimitingTunables { /// Create a table owned by the host given a [`TableType`] and a [`TableStyle`]. /// /// Delegated to base. - fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result { + fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result { self.base.create_host_table(ty, style) } @@ -122,7 +124,7 @@ impl Tunables for LimitingTunables { ty: &TableType, style: &TableStyle, vm_definition_location: NonNull, - ) -> Result { + ) -> Result { self.base.create_vm_table(ty, style, vm_definition_location) } } diff --git a/lib/api/Cargo.toml b/lib/api/Cargo.toml index fa03051239e..5f6dfce014d 100644 --- a/lib/api/Cargo.toml +++ b/lib/api/Cargo.toml @@ -40,12 +40,18 @@ tracing = { version = "0.1" } wat = { version = "1.216.0", optional = true } rustc-demangle = "0.1" shared-buffer = { workspace = true } -wasmi_c_api = { version = "0.38.0", package = "wasmi_c_api_impl", optional = true } + +wasmi_c_api = { version = "0.40.0", package = "wasmi_c_api_impl", optional = true, features = [ + "prefix-symbols", +] } + seq-macro = { version = "0.3.5", optional = true } loupe = { version = "0.1.3", optional = true, features = [ "indexmap", "enable-indexmap", ] } +paste = "1.0.15" +derive_more = { version = "1.0.0", features = ["from", "debug"] } # Dependencies and Development Dependencies for `sys`. [target.'cfg(not(target_arch = "wasm32"))'.dependencies] @@ -97,7 +103,6 @@ wat = "1.0" anyhow = "1.0" wasm-bindgen-test = "0.3.0" macro-wasmer-universal-test = { version = "5.1.0", path = "./macro-wasmer-universal-test" } - # Specific to `js`. # # `wasm-opt` is on by default in for the release profile, but it can be @@ -130,9 +135,12 @@ llvm = ["compiler", "wasmer-compiler-llvm"] # - Engines. engine = ["sys"] -wamr = ["wasm-c-api", "std", "wat"] +wamr = ["wasm-c-api", "std", "wat", "dep:which"] +wamr-default = ["wamr", "wat"] wasmi = ["wasm-c-api", "std", "wat", "dep:wasmi_c_api"] -v8 = ["wasm-c-api", "std", "wat", "dep:seq-macro"] +wasmi-default = ["wamr", "wat"] +v8 = ["wasm-c-api", "std", "wat", "dep:seq-macro", "dep:which"] +v8-default = ["wamr", "wat"] wasm-c-api = ["wasm-types-polyfill"] # - Deprecated features. @@ -145,7 +153,7 @@ js-default = ["js", "std", "wasm-types-polyfill"] wasm-types-polyfill = ["wasmparser"] jsc = ["rusty_jsc", "wasm-types-polyfill", "wasmparser"] - +jsc-default = ["jsc"] js-serializable-module = [] # Optional @@ -166,6 +174,7 @@ tar = "0.4.42" ureq = "2.10.1" xz = "0.1.0" zip = "2.2.0" +which = { version = "7.0.0", optional = true } [target.'cfg(target_env = "musl")'.build-dependencies] bindgen = { version = "0.70.1", default-features = false, features = [ diff --git a/lib/api/build.rs b/lib/api/build.rs index 74ac8a96dce..2ddbebd8ab2 100644 --- a/lib/api/build.rs +++ b/lib/api/build.rs @@ -1,178 +1,230 @@ -fn main() { - #[cfg(feature = "wamr")] - { - const WAMR_ZIP: &str = "https://github.com/bytecodealliance/wasm-micro-runtime/archive/refs/tags/WAMR-2.1.0.zip"; - const WAMR_DIR: &str = "wasm-micro-runtime-WAMR-2.1.0"; - - use cmake::Config; - use std::{env, path::PathBuf}; - - let crate_root = env::var("CARGO_MANIFEST_DIR").unwrap(); - - // Read target os from cargo env - // Transform from cargo value to valid wasm-micro-runtime os - let target_os = match env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() { - "linux" => "linux", - "windows" => "windows", - "macos" => "darwin", - "freebsd" => "freebsd", - "android" => "android", - "ios" => "ios", - other => panic!("Unsupported CARGO_CFG_TARGET_OS: {}", other), - }; +#[cfg(feature = "wamr")] +fn build_wamr() { + use bindgen::callbacks::ParseCallbacks; + const WAMR_ZIP: &str = "https://github.com/bytecodealliance/wasm-micro-runtime/archive/0e4dffc47922bb6fcdcaed7de2a6edfe8c48a7cd.zip"; + const ZIP_NAME: &str = "wasm-micro-runtime-0e4dffc47922bb6fcdcaed7de2a6edfe8c48a7cd"; - // Read target arch from cargo env - // Transform from cargo value to valid wasm-micro-runtime WAMR_BUILD_TARGET - let target_arch = match env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str() { - "x86" => "X86_32", - "x86_64" => "X86_64", - "arm" => "ARM", - "aarch64" => "AARCH64", - "mips" => "MIPS", - "powerpc" => "POWERPC", - "powerpc64" => "POWERPC64", - other => panic!("Unsupported CARGO_CFG_TARGET_ARCH: {}", other), - }; + use cmake::Config; + use std::{env, path::PathBuf}; - // Cleanup tmp data from prior builds - let wamr_dir = PathBuf::from(&crate_root).join("third_party/wamr"); - let zip_dir = PathBuf::from(&crate_root).join(WAMR_DIR); - let _ = std::fs::remove_dir_all(&wamr_dir); - let _ = std::fs::remove_dir_all(&zip_dir); - - // Fetch & extract wasm-micro-runtime source - let zip = ureq::get(WAMR_ZIP).call().expect("failed to download wamr"); - let mut zip_data = Vec::new(); - zip.into_reader() - .read_to_end(&mut zip_data) - .expect("failed to download wamr"); - zip::read::ZipArchive::new(std::io::Cursor::new(zip_data)) - .expect("failed to open wamr zip file") - .extract(&crate_root) - .expect("failed to extract wamr zip file"); - let _ = std::fs::remove_dir_all(&wamr_dir); - std::fs::rename(zip_dir, &wamr_dir).expect("failed to rename wamr dir"); - - let wamr_platform_dir = wamr_dir.join("product-mini/platforms").join(target_os); - let mut dst = Config::new(wamr_platform_dir.as_path()); - - dst.always_configure(true) - .generator("Ninja") - .no_build_target(true) - .define( - "CMAKE_BUILD_TYPE", - if cfg!(debug_assertions) { - "RelWithDebInfo" - } else { - "Release" - }, - ) - .define("WAMR_BUILD_AOT", "0") - //.define("WAMR_BUILD_TAIL_CALL", "1") - //.define("WAMR_BUILD_DUMP_CALL_STACK", "1") - // .define("WAMR_BUILD_CUSTOM_NAME_SECTION", "1") - // .define("WAMR_BUILD_LOAD_CUSTOM_SECTION", "1") - .define("WAMR_BUILD_BULK_MEMORY", "1") - .define("WAMR_BUILD_REF_TYPES", "1") - .define("WAMR_BUILD_SIMD", "1") - .define("WAMR_BUILD_FAST_INTERP", "1") - .define("WAMR_BUILD_LIB_PTHREAD", "1") - .define("WAMR_BUILD_LIB_WASI_THREADS", "0") - .define("WAMR_BUILD_LIBC_WASI", "0") - .define("WAMR_BUILD_LIBC_BUILTIN", "0") - .define("WAMR_BUILD_SHARED_MEMORY", "1") - .define("WAMR_BUILD_MULTI_MODULE", "0") - .define("WAMR_DISABLE_HW_BOUND_CHECK", "1") - .define("WAMR_BUILD_TARGET", target_arch); - - if target_os == "windows" { - dst.define("CMAKE_CXX_COMPILER", "cl.exe"); - dst.define("CMAKE_C_COMPILER", "cl.exe"); - dst.define("CMAKE_LINKER_TYPE", "MSVC"); - dst.define("WAMR_BUILD_PLATFORM", "windows"); - dst.define("WAMR_BUILD_LIBC_UVWASI", "0"); - } + let crate_root = env::var("OUT_DIR").unwrap(); + + // Read target os from cargo env + // Transform from cargo value to valid wasm-micro-runtime os + let target_os = match env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() { + "linux" => "linux", + "windows" => "windows", + "macos" => "darwin", + "freebsd" => "freebsd", + "android" => "android", + "ios" => "ios", + other => panic!("Unsupported CARGO_CFG_TARGET_OS: {}", other), + }; + + // Read target arch from cargo env + // Transform from cargo value to valid wasm-micro-runtime WAMR_BUILD_TARGET + let target_arch = match env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str() { + "x86" => "X86_32", + "x86_64" => "X86_64", + "arm" => "ARM", + "aarch64" => "AARCH64", + "mips" => "MIPS", + "powerpc" => "POWERPC", + "powerpc64" => "POWERPC64", + other => panic!("Unsupported CARGO_CFG_TARGET_ARCH: {}", other), + }; + + // Cleanup tmp data from prior builds + let wamr_dir = PathBuf::from(&crate_root).join("third_party/wamr"); + let zip_dir = PathBuf::from(&crate_root) + .join("third_party") + .join(ZIP_NAME); + let _ = std::fs::remove_dir_all(&wamr_dir); + let _ = std::fs::remove_dir_all(&zip_dir); + + // Fetch & extract wasm-micro-runtime source + let zip = ureq::get(WAMR_ZIP).call().expect("failed to download wamr"); + let mut zip_data = Vec::new(); + zip.into_reader() + .read_to_end(&mut zip_data) + .expect("failed to download wamr"); + zip::read::ZipArchive::new(std::io::Cursor::new(zip_data)) + .expect("failed to open wamr zip file") + .extract(&zip_dir) + .expect("failed to extract wamr zip file"); + let _ = std::fs::remove_dir_all(&wamr_dir); + std::fs::rename(zip_dir.join(ZIP_NAME), &wamr_dir).expect("failed to rename wamr dir"); - if target_os == "ios" { - // XXX: Hacky - // - // Compiling wamr targeting `aarch64-apple-ios` results in - // - // ``` - // clang: error: unsupported option '-mfloat-abi=' for target 'aarch64-apple-ios' - // ``` - // So, here, we simply remove that setting. - // - // See: https://github.com/bytecodealliance/wasm-micro-runtime/pull/3889 - let mut lines = vec![]; - let cmake_file_path = wamr_platform_dir.join("CMakeLists.txt"); - for line in std::fs::read_to_string(&cmake_file_path).unwrap().lines() { - if !line.contains("-mfloat-abi=hard") { - lines.push(line.to_string()) + let wamr_platform_dir = wamr_dir.join("product-mini/platforms").join(target_os); + let mut dst = Config::new(wamr_platform_dir.as_path()); + + dst.always_configure(true) + .generator("Ninja") + .no_build_target(true) + .define( + "CMAKE_BUILD_TYPE", + if cfg!(debug_assertions) { + "RelWithDebInfo" + } else { + "Release" + }, + ) + .define("WAMR_BUILD_AOT", "0") + //.define("WAMR_BUILD_TAIL_CALL", "1") + //.define("WAMR_BUILD_DUMP_CALL_STACK", "1") + // .define("WAMR_BUILD_CUSTOM_NAME_SECTION", "1") + // .define("WAMR_BUILD_LOAD_CUSTOM_SECTION", "1") + .define("WAMR_BUILD_BULK_MEMORY", "1") + .define("WAMR_BUILD_REF_TYPES", "1") + .define("WAMR_BUILD_SIMD", "1") + .define("WAMR_BUILD_FAST_INTERP", "1") + .define("WAMR_BUILD_LIB_PTHREAD", "1") + .define("WAMR_BUILD_LIB_WASI_THREADS", "0") + .define("WAMR_BUILD_LIBC_WASI", "0") + .define("WAMR_BUILD_LIBC_BUILTIN", "0") + .define("WAMR_BUILD_SHARED_MEMORY", "1") + .define("WAMR_BUILD_MULTI_MODULE", "0") + .define("WAMR_DISABLE_HW_BOUND_CHECK", "1") + .define("WAMR_BUILD_TARGET", target_arch); + + if target_os == "windows" { + dst.define("CMAKE_CXX_COMPILER", "cl.exe"); + dst.define("CMAKE_C_COMPILER", "cl.exe"); + dst.define("CMAKE_LINKER_TYPE", "MSVC"); + dst.define("WAMR_BUILD_PLATFORM", "windows"); + dst.define("WAMR_BUILD_LIBC_UVWASI", "0"); + } + + //if target_os == "ios" { + // // XXX: Hacky + // // + // // Compiling wamr targeting `aarch64-apple-ios` results in + // // + // // ``` + // // clang: error: unsupported option '-mfloat-abi=' for target 'aarch64-apple-ios' + // // ``` + // // So, here, we simply remove that setting. + // // + // // See: https://github.com/bytecodealliance/wasm-micro-runtime/pull/3889 + // let mut lines = vec![]; + // let cmake_file_path = wamr_platform_dir.join("CMakeLists.txt"); + // for line in std::fs::read_to_string(&cmake_file_path).unwrap().lines() { + // if !line.contains("-mfloat-abi=hard") { + // lines.push(line.to_string()) + // } + // } + // std::fs::write(cmake_file_path, lines.join("\n")).unwrap(); + //} + + let dst = dst.build(); + + // Check output of `cargo build --verbose`, should see something like: + // -L native=/path/runng/target/debug/build/runng-sys-abc1234/out + // That contains output from cmake + + // Rename the symbols created from wamr. + static mut WAMR_RENAMED: Vec<(String, String)> = vec![]; + + #[derive(Debug)] + struct WamrRenamer {} + impl ParseCallbacks for WamrRenamer { + /// This function will run for every extern variable and function. The returned value determines + /// the link name in the bindings. + fn generated_link_name_override( + &self, + item_info: bindgen::callbacks::ItemInfo<'_>, + ) -> Option { + if item_info.name.starts_with("wasm") { + let new_name = format!("wamr_{}", item_info.name); + unsafe { + WAMR_RENAMED.push((item_info.name.to_string(), new_name.clone())); } + Some(new_name) + } else { + None } - std::fs::write(cmake_file_path, lines.join("\n")).unwrap(); } + } - let dst = dst.build(); + let bindings = bindgen::Builder::default() + .header( + wamr_dir + .join("core/iwasm/include/wasm_c_api.h") + .to_str() + .unwrap(), + ) + .derive_default(true) + .derive_debug(true) + .parse_callbacks(Box::new(WamrRenamer {})) + .generate() + .expect("Unable to generate bindings"); + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("wamr_bindings.rs")) + .expect("Couldn't write bindings"); - // Check output of `cargo build --verbose`, should see something like: - // -L native=/path/runng/target/debug/build/runng-sys-abc1234/out - // That contains output from cmake - println!( - "cargo:rustc-link-search=native={}", - dst.join("build").display() + let objcopy_names = ["llvm-objcopy", "objcopy", "gobjcopy"]; + + let mut objcopy = None; + for n in objcopy_names { + if which::which(n).is_ok() { + objcopy = Some(n); + break; + } + } + + if objcopy.is_none() { + panic!( + "No program akin to `objcopy` found\nI searched for these programs in your path: {}", + objcopy_names.join(", ") ); - println!("cargo:rustc-link-lib=vmlib"); - - let bindings = bindgen::Builder::default() - .header( - wamr_dir - .join("core/iwasm/include/wasm_c_api.h") - .to_str() - .unwrap(), - ) - .header( - wamr_dir - .join("core/iwasm/include/wasm_export.h") - .to_str() - .unwrap(), - ) - .derive_default(true) - .derive_debug(true) - .generate() - .expect("Unable to generate bindings"); - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - bindings - .write_to_file(out_path.join("bindings.rs")) - .expect("Couldn't write bindings"); } - #[cfg(feature = "wasmi")] - { - use std::{env, path::PathBuf}; - - let bindings = bindgen::Builder::default() - .header( - PathBuf::from(std::env::var("DEP_WASMI_C_API_INCLUDE").unwrap()) - .join("wasm.h") - .to_string_lossy(), - ) - .derive_default(true) - .derive_debug(true) - .generate() - .expect("Unable to generate bindings for `wasmi`!"); - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - bindings - .write_to_file(out_path.join("bindings.rs")) - .expect("Couldn't write bindings"); + let objcopy = objcopy.unwrap(); + + unsafe { + let syms: Vec = WAMR_RENAMED + .iter() + .map(|(old, new)| + // A bit hacky: we need a way to figure out if we're going to target a Mach-O + // library or an ELF one to take care of the "_" in front of symbols. + { + if cfg!(any(target_os = "macos", target_os = "ios")) { + format!("--redefine-sym=_{old}={new}") + } else { + format!("--redefine-sym={old}={new}") + } + }) + .collect(); + let output = std::process::Command::new(objcopy) + .args(syms) + .arg(dst.join("build").join("libvmlib.a").display().to_string()) + .arg(dst.join("build").join("libwamr.a").display().to_string()) + .output() + .unwrap(); + + if !output.status.success() { + panic!( + "{objcopy} failed with error code {}: {}", + output.status, + String::from_utf8(output.stderr).unwrap() + ); + } } - #[cfg(feature = "v8")] - { - use std::{env, path::PathBuf}; + println!( + "cargo:rustc-link-search=native={}", + dst.join("build").display() + ); + println!("cargo:rustc-link-lib=wamr"); +} - let url = match ( +#[cfg(feature = "v8")] +fn build_v8() { + use bindgen::callbacks::ParseCallbacks; + use std::{env, path::PathBuf}; + + let url = match ( env::var("CARGO_CFG_TARGET_OS").unwrap().as_str(), env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str(), env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default().as_str(), @@ -185,63 +237,194 @@ fn main() { (os, arch, _) => panic!("target os + arch combination not supported: {os}, {arch}"), }; - let crate_root = env::var("CARGO_MANIFEST_DIR").unwrap(); - let v8_dir = PathBuf::from(&crate_root).join("third_party").join("v8"); - let out_dir = env::var("OUT_DIR").unwrap(); + let out_dir = env::var("OUT_DIR").unwrap(); + + let tar = ureq::get(url).call().expect("failed to download v8"); + + let mut tar_data = Vec::new(); + tar.into_reader() + .read_to_end(&mut tar_data) + .expect("failed to download v8 lib"); + + let tar = xz::read::XzDecoder::new(tar_data.as_slice()); + let mut archive = tar::Archive::new(tar); + + for entry in archive.entries().unwrap() { + eprintln!("entry: {:?}", entry.unwrap().path()); + } - let tar = ureq::get(url).call().expect("failed to download v8"); + let tar = xz::read::XzDecoder::new(tar_data.as_slice()); + let mut archive = tar::Archive::new(tar); - let mut tar_data = Vec::new(); - tar.into_reader() - .read_to_end(&mut tar_data) - .expect("failed to download v8 lib"); + archive.unpack(out_dir.clone()).unwrap(); - let tar = xz::read::XzDecoder::new(tar_data.as_slice()); - let mut archive = tar::Archive::new(tar); + println!("cargo:rustc-link-search=native={}", out_dir); - for entry in archive.entries().unwrap() { - eprintln!("entry: {:?}", entry.unwrap().path()); + println!("cargo:rustc-link-lib=v8_initializers"); + println!("cargo:rustc-link-lib=v8_libbase"); + println!("cargo:rustc-link-lib=v8_base_without_compiler"); + println!("cargo:rustc-link-lib=v8_compiler"); + println!("cargo:rustc-link-lib=v8_libplatform"); + println!("cargo:rustc-link-lib=v8_libsampler"); + println!("cargo:rustc-link-lib=v8_snapshot"); + println!("cargo:rustc-link-lib=v8_torque_generated"); + + if cfg!(any(target_os = "linux",)) { + println!("cargo:rustc-link-lib=stdc++"); + } else if cfg!(target_os = "windows") { + /* do nothing */ + println!("cargo:rustc-link-lib=winmm"); + println!("cargo:rustc-link-lib=dbghelp"); + println!("cargo:rustc-link-lib=shlwapi"); + } else { + println!("cargo:rustc-link-lib=c++"); + } + + // Rename the symbols created from wee8. + static mut WEE8_RENAMED: Vec<(String, String)> = vec![]; + + #[derive(Debug)] + struct Wee8Renamer {} + impl ParseCallbacks for Wee8Renamer { + /// This function will run for every extern variable and function. The returned value determines + /// the link name in the bindings. + fn generated_link_name_override( + &self, + item_info: bindgen::callbacks::ItemInfo<'_>, + ) -> Option { + if item_info.name.starts_with("wasm") { + let new_name = format!("wee8_{}", item_info.name); + unsafe { + WEE8_RENAMED.push((item_info.name.to_string(), new_name.clone())); + } + Some(new_name) + } else { + None + } + } + } + + let header_path = + PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("third-party/wee8/wasm.h"); + let bindings = bindgen::Builder::default() + .header(header_path.display().to_string()) + .derive_default(true) + .derive_debug(true) + .parse_callbacks(Box::new(Wee8Renamer {})) + .generate() + .expect("Unable to generate bindings for `v8`!"); + + let out_path = PathBuf::from(out_dir); + + bindings + .write_to_file(out_path.join("v8_bindings.rs")) + .expect("Couldn't write bindings"); + + let objcopy_names = ["llvm-objcopy", "objcopy", "gobjcopy"]; + + let mut objcopy = None; + for n in objcopy_names { + if which::which(n).is_ok() { + objcopy = Some(n); + break; } + } + + if objcopy.is_none() { + panic!( + "No program akin to `objcopy` found\nI searched for these programs in your path: {}", + objcopy_names.join(", ") + ); + } + + let objcopy = objcopy.unwrap(); - let tar = xz::read::XzDecoder::new(tar_data.as_slice()); - let mut archive = tar::Archive::new(tar); - - archive.unpack(out_dir.clone()).unwrap(); - - println!("cargo:rustc-link-search=native={}", out_dir); - - println!("cargo:rustc-link-lib=static=wee8"); - println!("cargo:rustc-link-lib=v8_initializers"); - println!("cargo:rustc-link-lib=v8_libbase"); - println!("cargo:rustc-link-lib=v8_base_without_compiler"); - println!("cargo:rustc-link-lib=v8_compiler"); - println!("cargo:rustc-link-lib=v8_libplatform"); - println!("cargo:rustc-link-lib=v8_libsampler"); - println!("cargo:rustc-link-lib=v8_snapshot"); - println!("cargo:rustc-link-lib=v8_torque_generated"); - - if cfg!(any(target_os = "linux",)) { - println!("cargo:rustc-link-lib=stdc++"); - } else if cfg!(target_os = "windows") { - /* do nothing */ - println!("cargo:rustc-link-lib=winmm"); - println!("cargo:rustc-link-lib=dbghelp"); - println!("cargo:rustc-link-lib=shlwapi"); - } else { - println!("cargo:rustc-link-lib=c++"); + unsafe { + let syms: Vec = WEE8_RENAMED + .iter() + .map(|(old, new)| + // A bit hacky: we need a way to figure out if we're going to target a Mach-O + // library or an ELF one to take care of the "_" in front of symbols. + { + if cfg!(any(target_os = "macos", target_os = "ios")) { + format!("--redefine-sym=_{old}={new}") + } else { + format!("--redefine-sym={old}={new}") + } + }) + .collect(); + let output = dbg!(std::process::Command::new(objcopy) + .args(syms) + .arg(out_path.join("libwee8.a").display().to_string()) + .arg(out_path.join("libwee8prefixed.a").display().to_string())) + .output() + .unwrap(); + + if !output.status.success() { + panic!( + "{objcopy} failed with error code {}: {}", + output.status, + String::from_utf8(output.stderr).unwrap() + ); } + } - let bindings = bindgen::Builder::default() - .header(v8_dir.join("wasm.h").to_str().unwrap()) - .derive_default(true) - .derive_debug(true) - .generate() - .expect("Unable to generate bindings for `v8`!"); + println!("cargo:rustc-link-lib=static=wee8prefixed"); +} - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); +#[cfg(feature = "wasmi")] +fn build_wasmi() { + use bindgen::callbacks::ParseCallbacks; + use std::{env, path::PathBuf}; - bindings - .write_to_file(out_path.join("bindings.rs")) - .expect("Couldn't write bindings"); + #[derive(Debug)] + struct WasmiRenamer {} + + impl ParseCallbacks for WasmiRenamer { + /// This function will run for every extern variable and function. The returned value determines + /// the link name in the bindings. + fn generated_link_name_override( + &self, + item_info: bindgen::callbacks::ItemInfo<'_>, + ) -> Option { + if item_info.name.starts_with("wasm") { + let new_name = if cfg!(any(target_os = "macos", target_os = "ios")) { + format!("_wasmi_{}", item_info.name) + } else { + format!("wasmi_{}", item_info.name) + }; + + Some(new_name) + } else { + None + } + } } + + let bindings = bindgen::Builder::default() + .header( + PathBuf::from(std::env::var("DEP_WASMI_C_API_INCLUDE").unwrap()) + .join("wasm.h") + .to_string_lossy(), + ) + .derive_default(true) + .derive_debug(true) + .parse_callbacks(Box::new(WasmiRenamer {})) + .generate() + .expect("Unable to generate bindings for `wasmi`!"); + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("wasmi_bindings.rs")) + .expect("Couldn't write bindings"); +} +#[allow(unused)] +fn main() { + #[cfg(feature = "wamr")] + build_wamr(); + + #[cfg(feature = "v8")] + build_v8(); + + #[cfg(feature = "wasmi")] + build_wasmi(); } diff --git a/lib/api/src/c_api/as_c.rs b/lib/api/src/c_api/as_c.rs deleted file mode 100644 index 59f74fa68b2..00000000000 --- a/lib/api/src/c_api/as_c.rs +++ /dev/null @@ -1,251 +0,0 @@ -use crate::bindings::{ - wasm_extern_as_ref, wasm_func_as_ref, wasm_val_t, wasm_val_t__bindgen_ty_1, - wasm_valkind_enum_WASM_F32, wasm_valkind_enum_WASM_F64, wasm_valkind_enum_WASM_FUNCREF, - wasm_valkind_enum_WASM_I32, wasm_valkind_enum_WASM_I64, wasm_valkind_t, wasm_valtype_kind, - wasm_valtype_t, -}; - -#[cfg(feature = "v8")] -use crate::bindings::wasm_valkind_enum_WASM_ANYREF as wasm_valkind_enum_WASM_EXTERNREF; - -//use crate::js::externals::Function; -// use crate::store::{Store, StoreObject}; -// use crate::js::RuntimeError; -use crate::imports::Imports; -use crate::instance::Instance; -use crate::store::{AsStoreMut, AsStoreRef}; -use crate::trap::Trap; -use crate::value::Value; -use crate::{externals, Type}; -use crate::{Extern, Function, Global, Memory, Table}; -use std::collections::HashMap; -use std::convert::TryInto; -use wasmer_types::ExternType; - -/// Convert the given type to a c-api value. -pub trait AsC: Sized { - /// The inner definition type from this c-api object - type DefinitionType; - type OutputType; - - /// Convert the given type to a [`Self::OutputType`]. - fn as_cvalue(&self, store: &impl AsStoreRef) -> Self::OutputType; - - /// Convert the given type to a [`Self::DefinitionType`]. - fn from_cvalue( - store: &mut impl AsStoreMut, - type_: &Self::DefinitionType, - value: &Self::OutputType, - ) -> Result; -} - -#[cfg(feature = "v8")] -#[inline] -pub fn param_from_c(value: &wasm_val_t) -> Value { - match value.kind as _ { - crate::bindings::wasm_valkind_enum_WASM_I32 => Value::I32(unsafe { value.of.i32_ }), - crate::bindings::wasm_valkind_enum_WASM_I64 => Value::I64(unsafe { value.of.i64_ }), - crate::bindings::wasm_valkind_enum_WASM_F32 => Value::F32(unsafe { value.of.f32_ }), - crate::bindings::wasm_valkind_enum_WASM_F64 => Value::F64(unsafe { value.of.f64_ }), - crate::bindings::wasm_valkind_enum_WASM_FUNCREF => { - Value::FuncRef(Some(Function(super::externals::function::Function { - handle: unsafe { value.of.ref_ as _ }, - }))) - } - crate::bindings::wasm_valkind_enum_WASM_ANYREF => { - panic!("ExternRefs are not currently supported through wasm_c_api") - } - _ => panic!("v8 curently does not support V128 values"), - } -} - -#[cfg(any(feature = "wamr", feature = "wasmi"))] -#[inline] -pub fn param_from_c(value: &wasm_val_t) -> Value { - match value.kind as _ { - crate::bindings::wasm_valkind_enum_WASM_I32 => Value::I32(unsafe { value.of.i32_ }), - crate::bindings::wasm_valkind_enum_WASM_I64 => Value::I64(unsafe { value.of.i64_ }), - crate::bindings::wasm_valkind_enum_WASM_F32 => Value::F32(unsafe { value.of.f32_ }), - crate::bindings::wasm_valkind_enum_WASM_F64 => Value::F64(unsafe { value.of.f64_ }), - crate::bindings::wasm_valkind_enum_WASM_FUNCREF => { - Value::FuncRef(Some(Function(super::externals::function::Function { - handle: unsafe { value.of.ref_ as _ }, - }))) - } - crate::bindings::wasm_valkind_enum_WASM_EXTERNREF => { - panic!("ExternRefs are not currently supported through wasm_c_api") - } - - _ => { - if cfg!(feature = "wamr") { - panic!("wamr currently does not support V128 values") - } else if cfg!(feature = "wamr") { - panic!("wasmi currently does not support V128 values") - } else { - panic!("this backend currently does not support V128 values") - } - } - } -} - -#[inline] -pub fn result_to_value(param: &Value) -> wasm_val_t { - #[cfg(feature = "wamr")] - { - match param { - Value::I32(val) => wasm_val_t { - kind: wasm_valkind_enum_WASM_I32 as _, - _paddings: Default::default(), - of: wasm_val_t__bindgen_ty_1 { i32_: *val }, - }, - Value::I64(val) => wasm_val_t { - kind: wasm_valkind_enum_WASM_I64 as _, - _paddings: Default::default(), - of: wasm_val_t__bindgen_ty_1 { i64_: *val }, - }, - Value::F32(val) => wasm_val_t { - kind: wasm_valkind_enum_WASM_F32 as _, - _paddings: Default::default(), - of: wasm_val_t__bindgen_ty_1 { f32_: *val }, - }, - Value::F64(val) => wasm_val_t { - kind: wasm_valkind_enum_WASM_F64 as _, - _paddings: Default::default(), - of: wasm_val_t__bindgen_ty_1 { f64_: *val }, - }, - Value::FuncRef(Some(val)) => wasm_val_t { - kind: wasm_valkind_enum_WASM_FUNCREF as _, - _paddings: Default::default(), - of: wasm_val_t__bindgen_ty_1 { - ref_: unsafe { wasm_func_as_ref(val.0.handle) }, - }, - }, - Value::FuncRef(None) => wasm_val_t { - kind: wasm_valkind_enum_WASM_FUNCREF as _, - _paddings: Default::default(), - of: wasm_val_t__bindgen_ty_1 { - ref_: unsafe { wasm_func_as_ref(std::ptr::null_mut()) }, - }, - }, - Value::ExternRef(_) => panic!("Creating host values from guest ExternRefs is not currently supported through wasm_c_api.") , - Value::V128(_) => panic!("Creating host values from guest V128s is not currently supported through wasm_c_api."), - } - } - - #[cfg(any(feature = "wasmi", feature = "v8"))] - { - match param { - Value::I32(val) => wasm_val_t { - kind: wasm_valkind_enum_WASM_I32 as _, - of: wasm_val_t__bindgen_ty_1 { i32_: *val }, - }, - Value::I64(val) => wasm_val_t { - kind: wasm_valkind_enum_WASM_I64 as _, - of: wasm_val_t__bindgen_ty_1 { i64_: *val }, - }, - Value::F32(val) => wasm_val_t { - kind: wasm_valkind_enum_WASM_F32 as _, - of: wasm_val_t__bindgen_ty_1 { f32_: *val }, - }, - Value::F64(val) => wasm_val_t { - kind: wasm_valkind_enum_WASM_F64 as _, - of: wasm_val_t__bindgen_ty_1 { f64_: *val }, - }, - Value::FuncRef(Some(val)) => wasm_val_t { - kind: wasm_valkind_enum_WASM_FUNCREF as _, - of: wasm_val_t__bindgen_ty_1 { - ref_: unsafe { wasm_func_as_ref(val.0.handle) }, - }, - }, - Value::FuncRef(None) => wasm_val_t { - kind: wasm_valkind_enum_WASM_FUNCREF as _, - of: wasm_val_t__bindgen_ty_1 { - ref_: unsafe { wasm_func_as_ref(std::ptr::null_mut()) }, - }, - }, - Value::ExternRef(_) => panic!("Creating host values from guest ExternRefs is not currently supported through wasm_c_api.") , - Value::V128(_) => panic!("Creating host values from guest V128s is not currently supported through wasm_c_api."), - } - } -} - -#[inline] -pub fn type_to_c(type_: &Type) -> wasm_valkind_t { - match type_ { - Type::I32 => wasm_valkind_enum_WASM_I32 as _, - Type::I64 => wasm_valkind_enum_WASM_I64 as _, - Type::F32 => wasm_valkind_enum_WASM_F32 as _, - Type::F64 => wasm_valkind_enum_WASM_F64 as _, - Type::FuncRef => wasm_valkind_enum_WASM_FUNCREF as _, - Type::ExternRef => { - #[cfg(any(feature = "wasmi", feature = "wamr"))] - { - crate::bindings::wasm_valkind_enum_WASM_EXTERNREF as _ - } - #[cfg(feature = "v8")] - { - crate::bindings::wasm_valkind_enum_WASM_ANYREF as _ - } - } - Type::V128 => { - #[cfg(feature = "wamr")] - { - crate::bindings::wasm_valkind_enum_WASM_V128 as _ - } - #[cfg(feature = "wasmi")] - { - panic!("wasmi does not support V128 kinds as of now"); - } - #[cfg(feature = "v8")] - { - panic!("v8 does not support V128 kinds as of now"); - } - } - } -} - -#[inline] -pub fn valtype_to_type(type_: *const wasm_valtype_t) -> Type { - let type_ = unsafe { wasm_valtype_kind(type_) }; - - #[cfg(feature = "wamr")] - match type_ as _ { - crate::bindings::wasm_valkind_enum_WASM_I32 => Type::I32, - crate::bindings::wasm_valkind_enum_WASM_I64 => Type::I64, - crate::bindings::wasm_valkind_enum_WASM_F32 => Type::F32, - crate::bindings::wasm_valkind_enum_WASM_F64 => Type::F64, - crate::bindings::wasm_valkind_enum_WASM_V128 => Type::V128, - crate::bindings::wasm_valkind_enum_WASM_EXTERNREF => Type::ExternRef, - crate::bindings::wasm_valkind_enum_WASM_FUNCREF => Type::FuncRef, - _ => unreachable!( - "valtype {:?} has no matching valkind and therefore no matching wasmer_types::Type", - type_ - ), - } - #[cfg(feature = "wasmi")] - match type_ as _ { - crate::bindings::wasm_valkind_enum_WASM_I32 => Type::I32, - crate::bindings::wasm_valkind_enum_WASM_I64 => Type::I64, - crate::bindings::wasm_valkind_enum_WASM_F32 => Type::F32, - crate::bindings::wasm_valkind_enum_WASM_F64 => Type::F64, - crate::bindings::wasm_valkind_enum_WASM_EXTERNREF => Type::ExternRef, - crate::bindings::wasm_valkind_enum_WASM_FUNCREF => Type::FuncRef, - _ => unreachable!( - "valtype {:?} has no matching valkind and therefore no matching wasmer_types::Type", - type_ - ), - } - #[cfg(feature = "v8")] - match type_ as _ { - crate::bindings::wasm_valkind_enum_WASM_I32 => Type::I32, - crate::bindings::wasm_valkind_enum_WASM_I64 => Type::I64, - crate::bindings::wasm_valkind_enum_WASM_F32 => Type::F32, - crate::bindings::wasm_valkind_enum_WASM_F64 => Type::F64, - crate::bindings::wasm_valkind_enum_WASM_ANYREF => Type::ExternRef, - crate::bindings::wasm_valkind_enum_WASM_FUNCREF => Type::FuncRef, - _ => unreachable!( - "valtype {:?} has no matching valkind and therefore no matching wasmer_types::Type", - type_ - ), - } -} diff --git a/lib/api/src/c_api/engine.rs b/lib/api/src/c_api/engine.rs deleted file mode 100644 index e8a933e00a2..00000000000 --- a/lib/api/src/c_api/engine.rs +++ /dev/null @@ -1,79 +0,0 @@ -use super::bindings::{wasm_engine_delete, wasm_engine_new, wasm_engine_t}; -use std::sync::Arc; - -#[derive(Debug)] -pub(crate) struct CApiEngine { - pub(crate) engine: *mut wasm_engine_t, -} - -#[cfg(feature = "v8")] -// A handle to an engine, which we want to unsafely mark as Sync. -struct EngineCapsule(*mut wasm_engine_t); - -#[cfg(feature = "v8")] -impl Drop for EngineCapsule { - fn drop(&mut self) { - unsafe { wasm_engine_delete(self.0) } - } -} - -#[cfg(feature = "v8")] -unsafe impl Sync for EngineCapsule {} - -#[cfg(feature = "v8")] -unsafe impl Send for EngineCapsule {} - -#[cfg(feature = "v8")] -static ENGINE: std::sync::OnceLock> = std::sync::OnceLock::new(); - -impl Default for CApiEngine { - #[cfg(not(feature = "v8"))] - fn default() -> Self { - let engine: *mut wasm_engine_t = unsafe { wasm_engine_new() }; - Self { engine } - } - - #[cfg(feature = "v8")] - 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 }; - Self { engine } - } -} - -impl Drop for CApiEngine { - #[cfg(feature = "v8")] - fn drop(&mut self) {} - - #[cfg(not(feature = "v8"))] - fn drop(&mut self) { - unsafe { wasm_engine_delete(self.engine) } - } -} - -/// A WebAssembly `Universal` Engine. -#[derive(Clone, Debug, Default)] -pub struct Engine { - pub(crate) inner: Arc, -} - -impl Engine { - pub(crate) fn deterministic_id(&self) -> &str { - "wasm-c-api" - } -} - -unsafe impl Send for Engine {} -unsafe impl Sync for Engine {} - -impl From<&crate::engine::Engine> for Engine { - fn from(engine: &crate::engine::Engine) -> Self { - unimplemented!(); - } -} - -/// Returns the default engine for the JS engine -pub(crate) fn default_engine() -> Engine { - Engine::default() -} diff --git a/lib/api/src/c_api/errors.rs b/lib/api/src/c_api/errors.rs deleted file mode 100644 index c9ef1059298..00000000000 --- a/lib/api/src/c_api/errors.rs +++ /dev/null @@ -1,13 +0,0 @@ -use crate::c_api::trap::Trap; -use crate::RuntimeError; - -impl From for RuntimeError { - fn from(trap: Trap) -> Self { - if trap.is::() { - return trap.downcast::().unwrap(); - } - let wasm_trace = vec![]; - let trap_code = None; - RuntimeError::new_from_source(trap, wasm_trace, trap_code) - } -} diff --git a/lib/api/src/c_api/externals/function.rs b/lib/api/src/c_api/externals/function.rs deleted file mode 100644 index 7192383843e..00000000000 --- a/lib/api/src/c_api/externals/function.rs +++ /dev/null @@ -1,1000 +0,0 @@ -use crate::as_c::{param_from_c, result_to_value, type_to_c, valtype_to_type}; -use crate::bindings::{ - wasm_byte_vec_new, wasm_byte_vec_new_empty, wasm_byte_vec_new_uninitialized, wasm_byte_vec_t, - wasm_extern_as_func, wasm_func_call, wasm_func_new, wasm_func_new_with_env, - wasm_func_param_arity, wasm_func_result_arity, wasm_func_t, wasm_func_type, wasm_functype_copy, - wasm_functype_new, wasm_functype_params, wasm_functype_results, wasm_functype_t, - wasm_trap_message, wasm_trap_new, wasm_trap_t, wasm_val_t, wasm_val_t__bindgen_ty_1, - wasm_val_vec_copy, wasm_val_vec_new, wasm_val_vec_new_empty, wasm_val_vec_new_uninitialized, - wasm_val_vec_t, wasm_valkind_enum_WASM_F32, wasm_valkind_enum_WASM_F64, - wasm_valkind_enum_WASM_I32, wasm_valkind_enum_WASM_I64, wasm_valtype_new, wasm_valtype_t, - wasm_valtype_vec_new, wasm_valtype_vec_new_empty, wasm_valtype_vec_t, -}; -use crate::c_api::store::{InternalStoreHandle, StoreHandle}; -use crate::StoreRef; -// use crate::c_api::trap::Trap; -// use crate::c_api::vm::{ -// VMExtern, VMFuncRef, VMFunction, VMFunctionCallback, VMFunctionEnvironment, -// }; -use crate::c_api::bindings::wasm_func_as_extern; -use crate::c_api::vm::{ - VMExtern, VMFuncRef, VMFunction, VMFunctionCallback, VMFunctionEnvironment, -}; -use crate::errors::RuntimeError; -use crate::externals::function::{HostFunction, HostFunctionKind, WithEnv, WithoutEnv}; -use crate::function_env::{FunctionEnv, FunctionEnvMut}; -use crate::native_type::{FromToNativeWasmType, IntoResult, NativeWasmTypeInto, WasmTypeList}; -use crate::store::{AsStoreMut, AsStoreRef, StoreInner, StoreMut}; -use crate::trap::Trap; -use crate::value::Value; -use std::ffi::{c_void, CStr}; -use std::fmt; -use std::io::Write; -use std::marker::PhantomData; -use std::panic::{self, AssertUnwindSafe}; -use std::ptr::null_mut; -use std::sync::Arc; - -use wasmer_types::{FunctionType, RawValue}; - -#[cfg(any(feature = "wamr", feature = "wasmi"))] -type CCallback = unsafe extern "C" fn( - *mut c_void, - *const wasm_val_vec_t, - *mut wasm_val_vec_t, -) -> *mut wasm_trap_t; - -#[cfg(feature = "v8")] -type CCallback = - unsafe extern "C" fn(*mut c_void, *const wasm_val_t, *mut wasm_val_t) -> *mut wasm_trap_t; - -#[derive(Clone, PartialEq)] -pub struct Function { - pub(crate) handle: VMFunction, -} - -unsafe impl Send for Function {} -unsafe impl Sync for Function {} - -impl From for Function { - fn from(handle: VMFunction) -> Self { - Self { handle } - } -} - -pub(crate) struct FunctionCallbackEnv<'a, F> { - store: StoreMut<'a>, - func: F, - env_handle: Option>, -} - -impl<'a, F> std::fmt::Debug for FunctionCallbackEnv<'a, F> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FunctionCallbackEnv") - .field("env_is_some", &self.env_handle.is_some()) - .finish() - } -} - -impl Function { - /// To `VMExtern`. - pub fn to_vm_extern(&self) -> VMExtern { - let extern_ = unsafe { wasm_func_as_extern(self.handle) }; - assert!( - !extern_.is_null(), - "Returned null Function extern from wasm-c-api" - ); - extern_ - } - - #[allow(clippy::cast_ptr_alignment)] - pub fn new_with_env( - store: &mut impl AsStoreMut, - env: &FunctionEnv, - ty: FT, - func: F, - ) -> Self - where - FT: Into, - F: Fn(FunctionEnvMut<'_, T>, &[Value]) -> Result, RuntimeError> - + 'static - + Send - + Sync, - { - let fn_ty: FunctionType = ty.into(); - let params = fn_ty.params(); - - let mut param_types = params - .into_iter() - .map(|param| { - let kind = type_to_c(param); - unsafe { wasm_valtype_new(kind) } - }) - .collect::>(); - - let mut wasm_param_types = unsafe { - let mut vec = Default::default(); - wasm_valtype_vec_new(&mut vec, param_types.len(), param_types.as_ptr()); - vec - }; - - let results = fn_ty.results(); - let mut result_types = results - .into_iter() - .map(|param| { - let kind = type_to_c(param); - unsafe { wasm_valtype_new(kind) } - }) - .collect::>(); - - let mut wasm_result_types = unsafe { - let mut vec = Default::default(); - wasm_valtype_vec_new(&mut vec, result_types.len(), result_types.as_ptr()); - vec - }; - - let wasm_functype = unsafe { - wasm_functype_new( - &mut wasm_param_types as *mut _, - &mut wasm_result_types as *mut _, - ) - }; - - let mut store = store.as_store_mut(); - let inner = store.inner.store.inner; - - let callback: CCallback = make_fn_callback(&func, param_types.len()); - - let mut callback_env: *mut FunctionCallbackEnv<'_, F> = - Box::leak(Box::new(FunctionCallbackEnv { - store, - func, - env_handle: Some(env.handle.clone()), - })); - - let wasm_function = unsafe { - wasm_func_new_with_env( - inner, - wasm_functype, - Some(callback), - callback_env as *mut _ as _, - None, - ) - }; - - if wasm_function.is_null() { - panic!("failed when creating new typed function"); - } - - Function { - handle: wasm_function, - } - } - - /// Creates a new host `Function` from a native function. - pub fn new_typed(store: &mut impl AsStoreMut, func: F) -> Self - where - F: HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync, - Args: WasmTypeList, - Rets: WasmTypeList, - { - let mut param_types = Args::wasm_types() - .into_iter() - .map(|param| { - let kind = type_to_c(param); - unsafe { wasm_valtype_new(kind) } - }) - .collect::>(); - - let mut wasm_param_types = unsafe { - let mut vec = Default::default(); - wasm_valtype_vec_new(&mut vec, param_types.len(), param_types.as_ptr()); - vec - }; - - let mut result_types = Rets::wasm_types() - .into_iter() - .map(|param| { - let kind = type_to_c(param); - unsafe { wasm_valtype_new(kind) } - }) - .collect::>(); - - let mut wasm_result_types = unsafe { - let mut vec = Default::default(); - wasm_valtype_vec_new(&mut vec, result_types.len(), result_types.as_ptr()); - vec - }; - - let wasm_functype = - unsafe { wasm_functype_new(&mut wasm_param_types, &mut wasm_result_types) }; - - let mut store = store.as_store_mut(); - let inner = store.inner.store.inner; - - let callback: CCallback = unsafe { std::mem::transmute(func.function_callback()) }; - - let mut callback_env: *mut FunctionCallbackEnv<'_, F> = - Box::into_raw(Box::new(FunctionCallbackEnv { - store, - func, - env_handle: None, - })); - - let wasm_function = unsafe { - wasm_func_new_with_env( - inner, - wasm_functype, - Some(callback), - callback_env as _, - None, - ) - }; - - if wasm_function.is_null() { - panic!("failed when creating new typed function"); - } - - Function { - handle: wasm_function, - } - } - - pub fn new_typed_with_env( - store: &mut impl AsStoreMut, - env: &FunctionEnv, - func: F, - ) -> Self - where - F: HostFunction, - Args: WasmTypeList, - Rets: WasmTypeList, - T: Send + 'static, - { - let mut param_types = Args::wasm_types() - .into_iter() - .map(|param| { - let kind = type_to_c(param); - unsafe { wasm_valtype_new(kind) } - }) - .collect::>(); - - let mut wasm_param_types = unsafe { - let mut vec = wasm_valtype_vec_t::default(); - wasm_valtype_vec_new(&mut vec, param_types.len(), param_types.as_ptr()); - vec - }; - - let mut result_types = Rets::wasm_types() - .into_iter() - .map(|param| { - let kind = type_to_c(param); - unsafe { wasm_valtype_new(kind) } - }) - .collect::>(); - - let mut wasm_result_types = unsafe { - let mut vec: wasm_valtype_vec_t = Default::default(); - wasm_valtype_vec_new(&mut vec, result_types.len(), result_types.as_ptr()); - vec - }; - - let wasm_functype = unsafe { - wasm_functype_new( - &mut wasm_param_types as *mut _, - &mut wasm_result_types as *mut _, - ) - }; - - let mut store = store.as_store_mut(); - let inner = store.inner.store.inner; - - let callback: CCallback = unsafe { std::mem::transmute(func.function_callback()) }; - - let mut callback_env: *mut FunctionCallbackEnv<'_, F> = - Box::into_raw(Box::new(FunctionCallbackEnv { - store, - func, - env_handle: Some(env.handle.clone()), - })); - - let wasm_function = unsafe { - wasm_func_new_with_env( - inner, - wasm_functype, - Some(callback), - callback_env as _, - None, - ) - }; - - if wasm_function.is_null() { - panic!("failed when creating new typed function"); - } - - Function { - handle: wasm_function, - } - } - - pub fn ty(&self, _store: &impl AsStoreRef) -> FunctionType { - let type_ = unsafe { wasm_func_type(self.handle) }; - let params: *const wasm_valtype_vec_t = unsafe { wasm_functype_params(type_) }; - let returns: *const wasm_valtype_vec_t = unsafe { wasm_functype_results(type_) }; - - let params: Vec = unsafe { - let mut res = vec![]; - for i in 0..(*params).size { - res.push(valtype_to_type(*(*params).data.wrapping_add(i))); - } - res - }; - - let returns: Vec = unsafe { - let mut res = vec![]; - for i in 0..(*returns).size { - res.push(valtype_to_type(*(*returns).data.wrapping_add(i))); - } - res - }; - - FunctionType::new(params, returns) - } - - pub fn call_raw( - &self, - _store: &mut impl AsStoreMut, - _params: Vec, - ) -> Result, RuntimeError> { - // There is no optimal call_raw in JSC, so we just - // simply rely the call - // self.call(store, params) - unimplemented!(); - } - - pub fn call( - &self, - store: &mut impl AsStoreMut, - params: &[Value], - ) -> Result, RuntimeError> { - // unimplemented!(); - let store_mut = store.as_store_mut(); - // let wasm_func_param_arity(self.handle) - - let mut args = { - #[cfg(any(feature = "wamr", feature = "wasmi"))] - unsafe { - let mut wasm_params = params - .iter() - .map(result_to_value) - .collect::>() - .into_boxed_slice(); - let mut vec = Default::default(); - wasm_val_vec_new(&mut vec, wasm_params.len(), wasm_params.as_ptr()); - vec - } - #[cfg(feature = "v8")] - { - let params = params.iter().map(result_to_value).collect::>(); - - let ptr = params.as_ptr(); - - std::mem::forget(params); - - ptr - } - }; - - let size = unsafe { wasm_func_result_arity(self.handle) }; - - let mut results = { - #[cfg(any(feature = "wamr", feature = "wasmi"))] - unsafe { - let mut vec = Default::default(); - wasm_val_vec_new_uninitialized(&mut vec, size); - vec - } - - #[cfg(feature = "v8")] - { - let mut vec = Vec::with_capacity(size); - let ptr = vec.as_mut_ptr() as *mut wasm_val_t; - - std::mem::forget(vec); - - ptr - } - }; - - #[cfg(any(feature = "wamr", feature = "wasmi"))] - let trap = unsafe { wasm_func_call(self.handle, &mut args as _, &mut results as *mut _) }; - - #[cfg(feature = "v8")] - let trap = unsafe { wasm_func_call(self.handle, args as *const _, results as *mut _) }; - - if !trap.is_null() { - return Err(Into::::into(trap).into()); - } - - #[cfg(any(feature = "wamr", feature = "wasmi"))] - unsafe { - let results = std::ptr::slice_from_raw_parts(results.data, results.size); - return Ok((*results) - .into_iter() - .map(param_from_c) - .collect::>() - .into_boxed_slice()); - } - - #[cfg(feature = "v8")] - unsafe { - let results = Vec::from_raw_parts(results, size, size); - - return Ok((results) - .iter() - .map(param_from_c) - .collect::>() - .into_boxed_slice()); - } - } - - pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, internal: VMFunction) -> Self { - Self { handle: internal } - } - - pub(crate) fn vm_funcref(&self, _store: &impl AsStoreRef) -> VMFuncRef { - unimplemented!(); - } - - pub(crate) unsafe fn from_vm_funcref( - _store: &mut impl AsStoreMut, - _funcref: VMFuncRef, - ) -> Self { - unimplemented!(); - } - - /// Checks whether this `Function` can be used with the given context. - pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { - true - } -} - -#[cfg(feature = "v8")] -macro_rules! gen_v8_callback { - ($args:expr) => {{ - unsafe extern "C" fn fn_callback( - env: *mut c_void, - args: *const wasm_val_t, - rets: *mut wasm_val_t, - ) -> *mut wasm_trap_t - where - F: Fn(FunctionEnvMut<'_, T>, &[Value]) -> Result, RuntimeError> - + 'static - + Send - + Sync, - { - let r: *mut (FunctionCallbackEnv<'_, F>) = env as _; - - let mut store = (*r).store.as_store_mut(); - let env_handle = (*r).env_handle.as_ref().unwrap().clone(); - let mut fn_env = FunctionEnv::from_handle(env_handle).into_mut(&mut store); - let func: &F = &(*r).func; - - let mut wasmer_args = vec![]; - - for i in 0..$args { - wasmer_args.push(param_from_c(&(*(args).wrapping_add(i)).clone())); - } - - let result = panic::catch_unwind(AssertUnwindSafe(|| unsafe { - func(fn_env, wasmer_args.as_slice()) - })); - - match result { - Ok(Ok(native_results)) => { - let mut c_results: Vec = native_results - .iter() - .map(|r| crate::as_c::result_to_value(r)) - .collect(); - - unsafe { - for i in 0..c_results.len() { - *((rets).wrapping_add(i)) = c_results[i] - } - } - - unsafe { std::ptr::null_mut() } - } - - Ok(Err(e)) => { - let trap: Trap = Trap::user(Box::new(e)); - unsafe { trap.into_wasm_trap(&mut store) } - } - - Err(e) => { - unimplemented!("host function panicked"); - } - } - } - - fn_callback:: - }}; -} - -fn make_fn_callback(func: &F, args: usize) -> CCallback -where - F: Fn(FunctionEnvMut<'_, T>, &[Value]) -> Result, RuntimeError> - + 'static - + Send - + Sync, -{ - #[cfg(any(feature = "wamr", feature = "wasmi"))] - { - unsafe extern "C" fn fn_callback( - env: *mut c_void, - args: *const wasm_val_vec_t, - rets: *mut wasm_val_vec_t, - ) -> *mut wasm_trap_t - where - F: Fn(FunctionEnvMut<'_, T>, &[Value]) -> Result, RuntimeError> - + 'static - + Send - + Sync, - { - let r: *mut (FunctionCallbackEnv<'_, F>) = env as _; - - let mut store = (*r).store.as_store_mut(); - let env_handle = (*r).env_handle.as_ref().unwrap().clone(); - let mut fn_env = FunctionEnv::from_handle(env_handle).into_mut(&mut store); - let func: &F = &(*r).func; - - let mut wasmer_args = vec![]; - - for i in 0..(*args).size { - wasmer_args.push(param_from_c(&(*(*args).data.wrapping_add(i)).clone())); - } - - let result = panic::catch_unwind(AssertUnwindSafe(|| unsafe { - func(fn_env, wasmer_args.as_slice()) - })); - - match result { - Ok(Ok(native_results)) => { - let mut c_results: Vec = native_results - .iter() - .map(|r| crate::as_c::result_to_value(r)) - .collect(); - - if c_results.len() != (*rets).size { - panic!("when calling host function: number of observed results differ from wanted results") - } - - unsafe { - for i in 0..(*rets).size { - *((*rets).data.wrapping_add(i)) = c_results[i] - } - } - - unsafe { std::ptr::null_mut() } - } - - Ok(Err(e)) => { - let trap: Trap = Trap::user(Box::new(e)); - unsafe { trap.into_wasm_trap(&mut store) } - } - - Err(e) => { - unimplemented!("host function panicked"); - } - } - } - - return fn_callback::; - } - - #[cfg(feature = "v8")] - unsafe { - // Hacky: v8 uses an older version of the `wasm_c_api` header in which the signature for - // callbacks is different from that of wamr and wasmi: in v8, args (and returns) are of - // type `*const wasm_val_t` (instead of `wasm_val_vec_t`, which also contains the length of - // the vector), so we can't generate a single callback that at runtime gets the number of - // arguments to perform the call to the underlying function. Therefore, we need to generate - // different concrete callbacks for different signatures. - - seq_macro::seq!(arg_num in 1..=26 { - let func = if args == 0 { - gen_v8_callback!(0) - } - #( - else if args == arg_num { - gen_v8_callback!(arg_num) - } - )* - else { - panic!("v8 callbacks for function with more than 26 parameters are not supported!"); - }; - - }); - return func; - } -} - -impl fmt::Debug for Function { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.debug_struct("Function").finish() - } -} - -macro_rules! impl_host_function { - ( [$c_struct_representation:ident] - $c_struct_name:ident, - $( $x:ident ),* ) => { - - // Implement `HostFunction` for a function with a [`FunctionEnvMut`] that has the same - // arity than the tuple. - #[allow(unused_parens)] - impl< $( $x, )* Rets, RetsAsResult, T, Func > - HostFunction - for - Func - where - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - RetsAsResult: IntoResult, - T: Send + 'static, - Func: Fn(FunctionEnvMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static, - { - #[allow(non_snake_case)] - fn function_callback(&self) -> VMFunctionCallback { - /// This is a function that wraps the real host - /// function. Its address will be used inside the - /// runtime. - #[cfg(any(feature = "wamr", feature = "wasmi"))] - unsafe extern "C" fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func, T>(env: *mut c_void, args: *const wasm_val_vec_t, results: *mut wasm_val_vec_t) -> *mut wasm_trap_t - where - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - RetsAsResult: IntoResult, - T: Send + 'static, - Func: Fn(FunctionEnvMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static, - { - - let r: *mut (FunctionCallbackEnv<'_, Func>) = env as _; - let store = &mut (*r).store.as_store_mut(); - - let mut i = 0; - - $( - let c_arg = (*(*args).data.wrapping_add(i)).clone(); - let wasmer_arg = crate::as_c::param_from_c(&c_arg); - let raw_arg : RawValue = wasmer_arg.as_raw(store); - let $x : $x = FromToNativeWasmType::from_native($x::Native::from_raw(store, raw_arg)); - - i += 1; - )* - - - - let env_handle = (*r).env_handle.as_ref().unwrap().clone(); - let mut fn_env = FunctionEnv::from_handle(env_handle).into_mut(store); - let func: &Func = &(*r).func; - - let result = panic::catch_unwind(AssertUnwindSafe(|| unsafe { - ((*r).func)(fn_env, $( $x, )* ).into_result() - })); - - - match result { - Ok(Ok(result)) => { - - let types = Rets::wasm_types(); - let mut native_results = result.into_array(store); - let native_results = native_results.as_mut(); - - let native_results: Vec = native_results.into_iter().enumerate() - .map(|(i, r)| Value::from_raw(store, types[i], r.clone())) - .collect(); - - let mut c_results: Vec = native_results.iter().map(|r| crate::as_c::result_to_value(r)).collect(); - - if c_results.len() != (*results).size { - panic!("when calling host function: number of observed results differ from wanted results") - } - - unsafe { - for i in 0..(*results).size { - *((*results).data.wrapping_add(i)) = c_results[i] - } - - } - - unsafe { std::ptr::null_mut() } - }, - Ok(Err(e)) => { - let trap: Trap = Trap::user(Box::new(e)); - unsafe { trap.into_wasm_trap(store) } - }, - Err(e) => { - unimplemented!("host function panicked"); - } - } - - - } - - #[cfg(feature = "v8")] - unsafe extern "C" fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func, T>(env: *mut c_void, args: *const wasm_val_t, results: *mut wasm_val_t) -> *mut wasm_trap_t - where - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - RetsAsResult: IntoResult, - T: Send + 'static, - Func: Fn(FunctionEnvMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static, - { - - let r: *mut (FunctionCallbackEnv<'_, Func>) = env as _; - let store = &mut (*r).store.as_store_mut(); - - let mut i = 0; - - $( - let c_arg = (*(args).wrapping_add(i)).clone(); - let wasmer_arg = crate::as_c::param_from_c(&c_arg); - let raw_arg : RawValue = wasmer_arg.as_raw(store); - let $x : $x = FromToNativeWasmType::from_native($x::Native::from_raw(store, raw_arg)); - - i += 1; - )* - - - - let env_handle = (*r).env_handle.as_ref().unwrap().clone(); - let mut fn_env = FunctionEnv::from_handle(env_handle).into_mut(store); - let func: &Func = &(*r).func; - - let result = panic::catch_unwind(AssertUnwindSafe(|| unsafe { - ((*r).func)(fn_env, $( $x, )* ).into_result() - })); - - - match result { - Ok(Ok(result)) => { - - let types = Rets::wasm_types(); - let size = types.len(); - let mut native_results = result.into_array(store); - let native_results = native_results.as_mut(); - - let native_results: Vec = native_results.into_iter().enumerate() - .map(|(i, r)| Value::from_raw(store, types[i], r.clone())) - .collect(); - - let mut c_results: Vec = native_results.iter().map(|r| crate::as_c::result_to_value(r)).collect(); - - if c_results.len() != size { - panic!("when calling host function: number of observed results differ from wanted results") - } - - unsafe { - for i in 0..size { - *((results).wrapping_add(i)) = c_results[i] - } - - } - - unsafe { std::ptr::null_mut() } - }, - Ok(Err(e)) => { - let trap: Trap = Trap::user(Box::new(e)); - unsafe { trap.into_wasm_trap(store) } - }, - Err(e) => { - unimplemented!("host function panicked"); - } - } - - - } - - func_wrapper::< $( $x, )* Rets, RetsAsResult, Self, T> as VMFunctionCallback - - } - } - - // Implement `HostFunction` for a function that has the same arity than the tuple. - #[allow(unused_parens)] - impl< $( $x, )* Rets, RetsAsResult, Func > - HostFunction<(), ( $( $x ),* ), Rets, WithoutEnv> - for - Func - where - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - RetsAsResult: IntoResult, - Func: Fn($( $x , )*) -> RetsAsResult + 'static, - { - - #[allow(non_snake_case)] - fn function_callback(&self) -> VMFunctionCallback { - - /// This is a function that wraps the real host - /// function. Its address will be used inside the - /// runtime. - #[cfg(any(feature = "wamr", feature = "wasmi"))] - unsafe extern "C" fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func>(env: *mut c_void, args: *const wasm_val_vec_t, results: *mut wasm_val_vec_t) -> *mut wasm_trap_t - where - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - RetsAsResult: IntoResult, - Func: Fn($( $x , )*) -> RetsAsResult + 'static, - { - let mut r: *mut FunctionCallbackEnv = unsafe {std::mem::transmute(env)}; - let store = &mut (*r).store.as_store_mut(); - let mut i = 0; - - $( - let c_arg = (*(*args).data.wrapping_add(i)).clone(); - let wasmer_arg = crate::as_c::param_from_c(&c_arg); - let raw_arg : RawValue = wasmer_arg.as_raw(store); - let $x : $x = FromToNativeWasmType::from_native($x::Native::from_raw(store, raw_arg)); - - i += 1; - )* - - let result = panic::catch_unwind(AssertUnwindSafe(|| unsafe { - ((*r).func)( $( $x, )* ).into_result() - })); - - match result { - Ok(Ok(result)) => { - - let types = Rets::wasm_types(); - let mut native_results = result.into_array(store); - let native_results = native_results.as_mut(); - - let native_results: Vec = native_results.into_iter().enumerate() - .map(|(i, r)| Value::from_raw(store, types[i], r.clone())) - .collect(); - - let mut c_results: Vec = native_results.iter().map(|r| crate::as_c::result_to_value(r)).collect(); - - if c_results.len() != (*results).size { - panic!("when calling host function: number of observed results differ from wanted results") - } - - unsafe { - for i in 0..(*results).size { - *((*results).data.wrapping_add(i)) = c_results[i] - } - } - - unsafe { std::ptr::null_mut() } - }, - - Ok(Err(e)) => { - let trap: Trap = Trap::user(Box::new(e)); - unsafe { trap.into_wasm_trap(store) } - // unimplemented!("host function panicked"); - }, - - Err(e) => { - unimplemented!("host function panicked"); - } - } - - - } - - #[cfg(feature = "v8")] - unsafe extern "C" fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func>(env: *mut c_void, args: *const wasm_val_t, results: *mut wasm_val_t) -> *mut wasm_trap_t - where - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - RetsAsResult: IntoResult, - Func: Fn($( $x , )*) -> RetsAsResult + 'static, - { - let mut r: *mut FunctionCallbackEnv = unsafe {std::mem::transmute(env)}; - let store = &mut (*r).store.as_store_mut(); - let mut i = 0; - - $( - let c_arg = (*(args).wrapping_add(i)).clone(); - let wasmer_arg = crate::as_c::param_from_c(&c_arg); - let raw_arg : RawValue = wasmer_arg.as_raw(store); - let $x : $x = FromToNativeWasmType::from_native($x::Native::from_raw(store, raw_arg)); - - i += 1; - )* - - let result = panic::catch_unwind(AssertUnwindSafe(|| unsafe { - ((*r).func)( $( $x, )* ).into_result() - })); - - match result { - Ok(Ok(result)) => { - - let types = Rets::wasm_types(); - let size = types.len(); - let mut native_results = result.into_array(store); - let native_results = native_results.as_mut(); - - let native_results: Vec = native_results.into_iter().enumerate() - .map(|(i, r)| Value::from_raw(store, types[i], r.clone())) - .collect(); - - let mut c_results: Vec = native_results.iter().map(|r| crate::as_c::result_to_value(r)).collect(); - - if c_results.len() != size { - panic!("when calling host function: number of observed results differ from wanted results") - } - - unsafe { - for i in 0..size { - *((results).wrapping_add(i)) = c_results[i] - } - } - - unsafe { std::ptr::null_mut() } - }, - - Ok(Err(e)) => { - let trap: Trap = Trap::user(Box::new(e)); - unsafe { trap.into_wasm_trap(store) } - // unimplemented!("host function panicked"); - }, - - Err(e) => { - unimplemented!("host function panicked"); - } - } - - - } - - func_wrapper::< $( $x, )* Rets, RetsAsResult, Self > as VMFunctionCallback - } - } - }; - } - -// Black-magic to count the number of identifiers at compile-time. -macro_rules! count_idents { - ( $($idents:ident),* ) => { - { - #[allow(dead_code, non_camel_case_types)] - enum Idents { $( $idents, )* __CountIdentsLast } - const COUNT: usize = Idents::__CountIdentsLast as usize; - COUNT - } - }; -} - -// Here we go! Let's generate all the C struct, `WasmTypeList` -// implementations and `HostFunction` implementations. -impl_host_function!([C] S0,); -impl_host_function!([transparent] S1, A1); -impl_host_function!([C] S2, A1, A2); -impl_host_function!([C] S3, A1, A2, A3); -impl_host_function!([C] S4, A1, A2, A3, A4); -impl_host_function!([C] S5, A1, A2, A3, A4, A5); -impl_host_function!([C] S6, A1, A2, A3, A4, A5, A6); -impl_host_function!([C] S7, A1, A2, A3, A4, A5, A6, A7); -impl_host_function!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8); -impl_host_function!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9); -impl_host_function!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); -impl_host_function!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); -impl_host_function!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); -impl_host_function!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); -impl_host_function!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); -impl_host_function!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); -impl_host_function!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16); -impl_host_function!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17); -impl_host_function!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18); -impl_host_function!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19); -impl_host_function!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20); -impl_host_function!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21); -impl_host_function!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22); -impl_host_function!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23); -impl_host_function!([C] S24, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24); -impl_host_function!([C] S25, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25); -impl_host_function!([C] S26, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26); diff --git a/lib/api/src/c_api/externals/mod.rs b/lib/api/src/c_api/externals/mod.rs deleted file mode 100644 index 3575fe23bb5..00000000000 --- a/lib/api/src/c_api/externals/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub(crate) mod function; -pub(crate) mod global; -pub(crate) mod memory; -pub(crate) mod memory_view; -pub(crate) mod table; diff --git a/lib/api/src/c_api/instance.rs b/lib/api/src/c_api/instance.rs deleted file mode 100644 index d7c498302be..00000000000 --- a/lib/api/src/c_api/instance.rs +++ /dev/null @@ -1,194 +0,0 @@ -use std::alloc::Layout; -use std::borrow::BorrowMut; -use std::mem::{size_of, size_of_val}; -use std::ops::DerefMut; -use std::sync::Arc; -use std::{mem, ptr}; - -use wasmer_types::CompileError; - -use crate::bindings::{ - wasm_extern_t, wasm_extern_vec_new, wasm_extern_vec_new_empty, - wasm_extern_vec_new_uninitialized, wasm_extern_vec_t, wasm_instance_delete, - wasm_instance_exports, wasm_instance_new, wasm_instance_t, wasm_module_imports, wasm_module_t, - wasm_store_t, wasm_trap_t, -}; -use crate::c_api::vm::VMInstance; -use crate::errors::InstantiationError; -use crate::exports::Exports; -use crate::imports::Imports; -use crate::module::Module; -use crate::store::AsStoreMut; -use crate::trap::Trap; -use crate::Extern; - -use super::vm::VMExtern; - -#[derive(PartialEq, Eq)] -pub(crate) struct InstanceHandle(pub(crate) *mut wasm_instance_t); - -unsafe impl Send for InstanceHandle {} -unsafe impl Sync for InstanceHandle {} - -impl InstanceHandle { - fn new( - store: *mut wasm_store_t, - module: *mut wasm_module_t, - mut externs: Vec, - ) -> Result { - #[cfg(feature = "wamr")] - { - // Check if the thread env was already initialised. - unsafe { - if !crate::bindings::wasm_runtime_thread_env_inited() { - if !crate::bindings::wasm_runtime_init_thread_env() { - panic!("Failed to initialize the thread environment!"); - } - } - } - } - - let mut trap: *mut wasm_trap_t = std::ptr::null_mut() as _; - - let instance = unsafe { - #[cfg(any(feature = "wasmi", feature = "wamr"))] - { - 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); - - #[cfg(feature = "wamr")] - { - let stack_size = 2 * 1024 * 1024; - let heap_size = 2 * 1024 * 1024; - - crate::bindings::wasm_instance_new_with_args( - store, - module, - &mut imports, - &mut trap, - stack_size, - heap_size, - ) - } - #[cfg(feature = "wasmi")] - { - wasm_instance_new(store, module, &mut imports, &mut trap) - } - } - - #[cfg(feature = "v8")] - { - wasm_instance_new( - store, - module, - externs.as_ptr() as *const *const _, - &mut trap, - ) - } - }; - - if instance.is_null() { - let trap = Trap::from(trap); - } - - Ok(InstanceHandle(instance)) - } - - fn get_exports(&self, mut store: &mut impl AsStoreMut, module: &Module) -> Exports { - let mut exports = unsafe { - let mut vec = Default::default(); - wasm_instance_exports(self.0, &mut vec); - vec - }; - - let wasm_exports: &[*mut wasm_extern_t] = - unsafe { std::slice::from_raw_parts(exports.data, exports.size) }; - - let exports_ty = module.exports().collect::>(); - let exports = exports_ty - .iter() - .zip(wasm_exports.into_iter()) - .map(|(export_type, wasm_export)| { - let name = export_type.name(); - let mut store = store.as_store_mut(); - let extern_type = export_type.ty(); - // Annotation is here to prevent spurious IDE warnings. - - let extern_ = Extern::from_vm_extern(&mut store, *wasm_export); - (name.to_string(), extern_) - }) - .collect::(); - exports - } -} -impl Drop for InstanceHandle { - fn drop(&mut self) { - unsafe { wasm_instance_delete(self.0) } - } -} - -#[derive(Clone, PartialEq, Eq)] -pub struct Instance { - pub(crate) handle: Arc, -} - -impl Instance { - pub(crate) fn new( - store: &mut impl AsStoreMut, - module: &Module, - imports: &Imports, - ) -> Result<(Self, Exports), InstantiationError> { - let externs = module - .imports() - .map(|import_ty| { - imports - .get_export(import_ty.module(), import_ty.name()) - .expect("Extern not found") - }) - .collect::>(); - - #[cfg(feature = "wamr")] - { - _ = store; - // Hacky: we need to tie a *module* to a store before instantiating it.. - let mut store_from_module = module.0.handle.store.lock().unwrap(); - let mut store = store_from_module.as_store_mut(); - - return Self::new_by_index(&mut store, module, &externs); - } - #[cfg(any(feature = "wasmi", feature = "v8"))] - { - return Self::new_by_index(&mut store.as_store_mut(), module, &externs); - } - } - - pub(crate) fn new_by_index( - store: &mut impl AsStoreMut, - module: &Module, - externs: &[Extern], - ) -> Result<(Self, Exports), InstantiationError> { - let store_ref = store.as_store_ref(); - let externs: Vec<*mut wasm_extern_t> = externs - .iter() - .map(|extern_| { - let vm_extern = extern_.to_vm_extern(); - vm_extern - }) - .collect::>(); - let instance = - InstanceHandle::new(store_ref.inner.store.inner, module.0.handle.inner, externs)?; - let exports = instance.get_exports(store, module); - - Ok(( - Self { - handle: Arc::new(instance), - }, - exports, - )) - } -} diff --git a/lib/api/src/c_api/mem_access.rs b/lib/api/src/c_api/mem_access.rs deleted file mode 100644 index f878ed6e5cf..00000000000 --- a/lib/api/src/c_api/mem_access.rs +++ /dev/null @@ -1,92 +0,0 @@ -use crate::access::{RefCow, SliceCow, WasmRefAccess, WasmSliceAccess}; -use crate::{MemoryAccessError, WasmRef, WasmSlice}; -use std::mem; - -impl<'a, T> WasmSliceAccess<'a, T> -where - T: wasmer_types::ValueType, -{ - pub(crate) fn new(slice: WasmSlice<'a, T>) -> Result { - let total_len = slice - .len - .checked_mul(mem::size_of::() as u64) - .ok_or(MemoryAccessError::Overflow)?; - let end = slice - .offset - .checked_add(total_len) - .ok_or(MemoryAccessError::Overflow)?; - if end > slice.buffer.0.len as u64 { - tracing::warn!( - "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", - total_len, - end, - slice.buffer.0.len - ); - return Err(MemoryAccessError::HeapOutOfBounds); - } - let buf = unsafe { - let buf_ptr: *mut u8 = slice.buffer.0.base.add(slice.offset as usize); - let buf_ptr: *mut T = std::mem::transmute(buf_ptr); - std::slice::from_raw_parts_mut(buf_ptr, slice.len as usize) - }; - Ok(Self { - slice, - buf: SliceCow::Borrowed(buf), - }) - } -} - -impl<'a, T> WasmRefAccess<'a, T> -where - T: wasmer_types::ValueType, -{ - pub(crate) fn new(ptr: WasmRef<'a, T>) -> Result { - let total_len = mem::size_of::() as u64; - let end = ptr - .offset - .checked_add(total_len) - .ok_or(MemoryAccessError::Overflow)?; - if end > ptr.buffer.0.len as u64 { - tracing::warn!( - "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", - total_len, - end, - ptr.buffer.0.len - ); - return Err(MemoryAccessError::HeapOutOfBounds); - } - let val = unsafe { - let val_ptr: *mut u8 = ptr.buffer.0.base.add(ptr.offset as usize); - let val_ptr: *mut T = std::mem::transmute(val_ptr); - &mut *val_ptr - }; - Ok(Self { - ptr, - buf: RefCow::Borrowed(val), - }) - } -} - -impl<'a, T> WasmRefAccess<'a, T> -where - T: wasmer_types::ValueType, -{ - /// Reads the address pointed to by this `WasmPtr` in a memory. - #[inline] - #[allow(clippy::clone_on_copy)] - pub fn read(&self) -> T - where - T: Clone, - { - self.as_ref().clone() - } - - /// Writes to the address pointed to by this `WasmPtr` in a memory. - #[inline] - pub fn write(&mut self, val: T) { - // Note: Zero padding is not required here as its a typed copy which does - // not leak the bytes into the memory - // https://stackoverflow.com/questions/61114026/does-stdptrwrite-transfer-the-uninitialized-ness-of-the-bytes-it-writes - *(self.as_mut()) = val; - } -} diff --git a/lib/api/src/c_api/mod.rs b/lib/api/src/c_api/mod.rs deleted file mode 100644 index b61a2f5b1fa..00000000000 --- a/lib/api/src/c_api/mod.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! The JavascriptCore integration in the Wasmer API - -#![allow( - unused, - dead_code, - non_upper_case_globals, - non_snake_case, - missing_docs -)] - -#[cfg(all(feature = "std", feature = "core"))] -compile_error!( - "The `std` and `core` features are both enabled, which is an error. Please enable only once." -); - -#[cfg(all(not(feature = "std"), not(feature = "core")))] -compile_error!("Both the `std` and `core` features are disabled. Please enable one of them."); - -#[cfg(all(not(feature = "wamr"), not(feature = "wasmi"), not(feature = "v8")))] -compile_error!("Features `wamr`, `wasmi` and `v8` are all disabled. Please enable one of them."); - -#[cfg(feature = "core")] -pub(crate) extern crate alloc; - -mod lib { - #[cfg(feature = "core")] - pub mod std { - pub use crate::alloc::{borrow, boxed, str, string, sync, vec}; - pub use core::fmt; - pub use hashbrown as collections; - } - - #[cfg(feature = "std")] - pub mod std { - pub use std::{borrow, boxed, collections, fmt, str, string, sync, vec}; - } -} - -pub(crate) mod as_c; -pub(crate) mod bindings; -pub(crate) mod engine; -pub(crate) mod errors; -pub(crate) mod extern_ref; -pub(crate) mod externals; -pub(crate) mod instance; -pub(crate) mod mem_access; -pub(crate) mod module; -pub(crate) mod store; -pub(crate) mod trap; -pub(crate) mod typed_function; -pub(crate) mod vm; diff --git a/lib/api/src/c_api/store.rs b/lib/api/src/c_api/store.rs deleted file mode 100644 index 9775c3dfd3f..00000000000 --- a/lib/api/src/c_api/store.rs +++ /dev/null @@ -1,296 +0,0 @@ -use super::bindings::{wasm_store_delete, wasm_store_new, wasm_store_t}; -use crate::{ - engine::{AsEngineRef, Engine, EngineRef}, - AsStoreRef, StoreRef, -}; -pub(crate) use objects::{InternalStoreHandle, StoreObject}; -pub use objects::{StoreHandle, StoreObjects}; - -#[derive(Debug)] -pub(crate) struct Store { - pub(crate) engine: Engine, - pub(crate) inner: *mut wasm_store_t, -} - -impl Store { - pub(crate) fn new(engine: crate::engine::Engine) -> Self { - let inner: *mut wasm_store_t = unsafe { wasm_store_new(engine.0.inner.engine) }; - Store { inner, engine } - } - - pub(crate) fn engine(&self) -> &Engine { - &self.engine - } -} - -impl Drop for Store { - fn drop(&mut self) { - unsafe { wasm_store_delete(self.inner) } - } -} - -impl AsEngineRef for Store { - fn as_engine_ref(&self) -> EngineRef<'_> { - EngineRef::new(&self.engine) - } -} - -mod objects { - use crate::c_api::vm::{VMFunctionEnvironment, VMGlobal}; - use std::{fmt, marker::PhantomData, num::NonZeroUsize}; - pub use wasmer_types::StoreId; - - /// Trait to represent an object managed by a context. This is implemented on - /// the VM types managed by the context. - pub trait StoreObject: Sized { - fn list(store: &StoreObjects) -> &Vec; - fn list_mut(store: &mut StoreObjects) -> &mut Vec; - } - - macro_rules! impl_store_object { - ($($field:ident => $ty:ty,)*) => { - $( - impl StoreObject for $ty { - fn list(store: &StoreObjects) -> &Vec { - &store.$field - } - fn list_mut(store: &mut StoreObjects) -> &mut Vec { - &mut store.$field - } - } - )* - }; -} - - impl_store_object! { - // Note: we store the globals in order to be able to access them later via - // `StoreObjects::iter_globals`. - globals => VMGlobal, - // functions => VMFunction, - // tables => VMTable, - // memories => VMMemory, - // The function environments are the only things attached to a store, - // since the other JS objects (table, globals, memory and functions) - // live in the JS VM Store by default - function_environments => VMFunctionEnvironment, - } - - /// Set of objects managed by a context. - #[derive(Default, Debug)] - pub struct StoreObjects { - id: StoreId, - globals: Vec, - function_environments: Vec, - } - - impl StoreObjects { - /// Returns the ID of this context. - pub fn id(&self) -> StoreId { - self.id - } - - /// Sets the ID of this store - pub fn set_id(&mut self, id: StoreId) { - self.id = id; - } - - /// Returns a pair of mutable references from two handles. - /// - /// Panics if both handles point to the same object. - pub fn get_2_mut( - &mut self, - a: InternalStoreHandle, - b: InternalStoreHandle, - ) -> (&mut T, &mut T) { - assert_ne!(a.index(), b.index()); - let list = T::list_mut(self); - if a.index() < b.index() { - let (low, high) = list.split_at_mut(b.index()); - (&mut low[a.index()], &mut high[0]) - } else { - let (low, high) = list.split_at_mut(a.index()); - (&mut high[0], &mut low[a.index()]) - } - } - - /// Return an immutable iterator over all globals - pub fn iter_globals(&self) -> core::slice::Iter { - self.globals.iter() - } - - /// Return an vector of all globals and converted to u128 - pub fn as_u128_globals(&self) -> Vec { - vec![] - // self.iter_globals() - // .map(|v| v.global.value().as_f64().unwrap() as u128) - // .collect() - } - - /// Set a global, at index idx. Will panic if idx is out of range - /// Safety: the caller should check taht the raw value is compatible - /// with destination VMGlobal type - pub fn set_global_unchecked(&self, idx: usize, new_val: u128) { - assert!(idx < self.globals.len()); - // let g = &self.globals[idx].global; - // let cur_val = g.value().as_f64().unwrap(); - // let new_val = new_val as f64; - // if cur_val != new_val { - // let new_value = JSValue::from(new_val); - // g.set_value(&new_value); - // } - } - } - - /// Handle to an object managed by a context. - /// - /// Internally this is just an integer index into a context. A reference to the - /// context must be passed in separately to access the actual object. - pub struct StoreHandle { - id: StoreId, - internal: InternalStoreHandle, - } - - impl core::cmp::PartialEq for StoreHandle { - fn eq(&self, other: &Self) -> bool { - self.id == other.id - } - } - - impl std::hash::Hash for StoreHandle { - fn hash(&self, state: &mut H) { - self.id.hash(state); - self.internal.idx.hash(state); - } - } - - impl Clone for StoreHandle { - fn clone(&self) -> Self { - Self { - id: self.id, - internal: self.internal, - } - } - } - - impl fmt::Debug for StoreHandle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StoreHandle") - .field("id", &self.id) - .field("internal", &self.internal.index()) - .finish() - } - } - - impl StoreHandle { - /// Moves the given object into a context and returns a handle to it. - pub fn new(store: &mut StoreObjects, val: T) -> Self { - Self { - id: store.id, - internal: InternalStoreHandle::new(store, val), - } - } - - /// Returns a reference to the object that this handle points to. - pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T { - assert_eq!(self.id, store.id, "object used with the wrong context"); - self.internal.get(store) - } - - /// Returns a mutable reference to the object that this handle points to. - pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T { - assert_eq!(self.id, store.id, "object used with the wrong context"); - self.internal.get_mut(store) - } - - /// Returns the internal handle contains within this handle. - pub fn internal_handle(&self) -> InternalStoreHandle { - self.internal - } - - /// Returns the ID of the context associated with the handle. - #[allow(unused)] - pub fn store_id(&self) -> StoreId { - self.id - } - - /// Overrides the store id with a new ID - #[allow(unused)] - pub fn set_store_id(&mut self, id: StoreId) { - self.id = id; - } - - /// Constructs a `StoreHandle` from a `StoreId` and an `InternalStoreHandle`. - /// - /// # Safety - /// Handling `InternalStoreHandle` values is unsafe because they do not track context ID. - pub unsafe fn from_internal(id: StoreId, internal: InternalStoreHandle) -> Self { - Self { id, internal } - } - } - - /// Internal handle to an object owned by the current context. - /// - /// Unlike `StoreHandle` this does not track the context ID: it is only - /// intended to be used within objects already owned by a context. - #[repr(transparent)] - pub struct InternalStoreHandle { - // Use a NonZero here to reduce the size of Option. - idx: NonZeroUsize, - marker: PhantomData T>, - } - - impl Clone for InternalStoreHandle { - fn clone(&self) -> Self { - *self - } - } - impl Copy for InternalStoreHandle {} - - impl fmt::Debug for InternalStoreHandle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("InternalStoreHandle") - .field("idx", &self.idx) - .finish() - } - } - impl PartialEq for InternalStoreHandle { - fn eq(&self, other: &Self) -> bool { - self.idx == other.idx - } - } - impl Eq for InternalStoreHandle {} - - impl InternalStoreHandle { - /// Moves the given object into a context and returns a handle to it. - pub fn new(store: &mut StoreObjects, val: T) -> Self { - let list = T::list_mut(store); - let idx = NonZeroUsize::new(list.len() + 1).unwrap(); - list.push(val); - Self { - idx, - marker: PhantomData, - } - } - - /// Returns a reference to the object that this handle points to. - pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T { - &T::list(store)[self.idx.get() - 1] - } - - /// Returns a mutable reference to the object that this handle points to. - pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T { - &mut T::list_mut(store)[self.idx.get() - 1] - } - - pub(crate) fn index(&self) -> usize { - self.idx.get() - } - - pub(crate) fn from_index(idx: usize) -> Option { - NonZeroUsize::new(idx).map(|idx| Self { - idx, - marker: PhantomData, - }) - } - } -} diff --git a/lib/api/src/c_api/vm.rs b/lib/api/src/c_api/vm.rs deleted file mode 100644 index 95a14056b92..00000000000 --- a/lib/api/src/c_api/vm.rs +++ /dev/null @@ -1,164 +0,0 @@ -use super::bindings::{ - wasm_extern_t, wasm_func_t, wasm_global_t, wasm_instance_t, wasm_memory_t, wasm_ref_t, - wasm_table_t, -}; -use std::any::Any; -/// This module is mainly used to create the `VM` types that will hold both -/// the JS values of the `Memory`, `Table`, `Global` and `Function` and also -/// it's types. -/// This module should not be needed any longer (with the exception of the memory) -/// once the type reflection is added to the WebAssembly JS API. -/// https://github.com/WebAssembly/js-types/ -// use crate::store::AsStoreRef; -use wasmer_types::RawValue; -// use std::any::Any; -// use std::fmt; -// use tracing::trace; -// use wasmer_types::RawValue; -// use wasmer_types::{ -// FunctionType, GlobalType, MemoryError, MemoryType, Pages, TableType, WASM_PAGE_SIZE, -// }; - -// pub(crate) type VMFunctionEnvironment = *mut ::std::os::raw::c_void; - -/// The VM Function type -pub type VMFunction = *mut wasm_func_t; -/// The VM Memory type -pub type VMMemory = *mut wasm_memory_t; -/// The VM Shared Memory type -pub type VMSharedMemory = VMMemory; -/// The VM Global type -pub type VMGlobal = *mut wasm_global_t; -/// The VM Table type -pub type VMTable = *mut wasm_table_t; -// pub(crate) type VMExternRef = *mut wasm_ref_t; -pub(crate) type VMExtern = *mut wasm_extern_t; -// pub(crate) type VMFuncRef = *mut wasm_ref_t; -pub(crate) type VMInstance = *mut wasm_instance_t; - -pub(crate) type VMExternTable = VMTable; -pub(crate) type VMExternMemory = VMMemory; -pub(crate) type VMExternGlobal = VMGlobal; -pub(crate) type VMExternFunction = VMFunction; - -pub type VMFunctionCallback = *mut ::std::os::raw::c_void; -// pub type VMTrampoline = *mut ::std::os::raw::c_void; - -use crate::bindings::{ - wasm_extern_as_func, wasm_extern_as_global, wasm_extern_as_memory, wasm_extern_as_table, - wasm_extern_kind, wasm_extern_type, wasm_externkind_enum_WASM_EXTERN_FUNC, - wasm_externkind_enum_WASM_EXTERN_GLOBAL, wasm_externkind_enum_WASM_EXTERN_MEMORY, -}; -use crate::externals::{Extern, Function, Global, Memory, Table, VMExternToExtern}; -use crate::store::AsStoreMut; - -impl VMExternToExtern for VMExtern { - fn to_extern(self, store: &mut impl AsStoreMut) -> Extern { - let kind = unsafe { wasm_extern_kind(&mut *self) }; - - match kind as u32 { - 0 => { - let func = unsafe { wasm_extern_as_func(&mut *self) }; - if func.is_null() { - panic!("The wasm-c-api reported extern as function, but is not"); - } - Extern::Function(Function::from_vm_extern(store, func)) - } - 1 => { - let global = unsafe { wasm_extern_as_global(&mut *self) }; - if global.is_null() { - panic!("The wasm-c-api reported extern as a global, but is not"); - } - Extern::Global(Global::from_vm_extern(store, global)) - } - 2 => { - let table = unsafe { wasm_extern_as_table(&mut *self) }; - if table.is_null() { - panic!("The wasm-c-api reported extern as a table, but is not"); - } - Extern::Table(Table::from_vm_extern(store, table)) - } - 3 => { - let memory = unsafe { wasm_extern_as_memory(&mut *self) }; - if memory.is_null() { - panic!("The wasm-c-api reported extern as a table, but is not"); - } - Extern::Memory(Memory::from_vm_extern(store, memory)) - } - _ => { - unimplemented!() - } - } - // match self { - // Self::Function(f) => Extern::Function(Function::from_vm_extern(store, f)), - // Self::Memory(m) => Extern::Memory(Memory::from_vm_extern(store, m)), - // Self::Global(g) => Extern::Global(Global::from_vm_extern(store, g)), - // Self::Table(t) => Extern::Table(Table::from_vm_extern(store, t)), - // } - } -} - -/// Underlying FunctionEnvironment used by a `VMFunction`. -#[derive(Debug)] -pub struct VMFunctionEnvironment { - contents: Box, -} - -impl VMFunctionEnvironment { - /// Wraps the given value to expose it to Wasm code as a function context. - pub fn new(val: impl Any + Send + 'static) -> Self { - Self { - contents: Box::new(val), - } - } - - #[allow(clippy::should_implement_trait)] - /// Returns a reference to the underlying value. - pub fn as_ref(&self) -> &(dyn Any + Send + 'static) { - &*self.contents - } - - #[allow(clippy::should_implement_trait)] - /// Returns a mutable reference to the underlying value. - pub fn as_mut(&mut self) -> &mut (dyn Any + Send + 'static) { - &mut *self.contents - } -} - -pub(crate) struct VMExternRef; - -impl VMExternRef { - /// Converts the `VMExternRef` into a `RawValue`. - pub fn into_raw(self) -> RawValue { - unimplemented!(); - } - - /// Extracts a `VMExternRef` from a `RawValue`. - /// - /// # Safety - /// `raw` must be a valid `VMExternRef` instance. - pub unsafe fn from_raw(_raw: RawValue) -> Option { - unimplemented!(); - } -} - -#[repr(transparent)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub(crate) struct VMFuncRef; - -impl VMFuncRef { - /// Converts the `VMFuncRef` into a `RawValue`. - pub fn into_raw(self) -> RawValue { - unimplemented!(); - } - - /// Extracts a `VMFuncRef` from a `RawValue`. - /// - /// # Safety - /// `raw.funcref` must be a valid pointer. - pub unsafe fn from_raw(_raw: RawValue) -> Option { - unimplemented!(); - } -} - -pub struct VMTrampoline; diff --git a/lib/api/src/engine.rs b/lib/api/src/engine.rs deleted file mode 100644 index 17312cb49a8..00000000000 --- a/lib/api/src/engine.rs +++ /dev/null @@ -1,185 +0,0 @@ -use core::ops::Deref; - -#[cfg(feature = "sys")] -use crate::sys::engine as engine_imp; -#[cfg(feature = "sys")] -pub(crate) use crate::sys::engine::default_engine; -#[cfg(feature = "sys")] -use crate::IntoBytes; -use crate::StoreRef; -#[cfg(feature = "sys")] -use shared_buffer::OwnedBuffer; -#[cfg(feature = "sys")] -use std::path::Path; -#[cfg(feature = "sys")] -use std::sync::Arc; -#[cfg(feature = "sys")] -#[allow(unused_imports)] -pub use wasmer_compiler::{Artifact, CompilerConfig, EngineInner, Features, Tunables}; -#[cfg(feature = "sys")] -use wasmer_types::DeserializeError; - -#[cfg(feature = "js")] -use crate::js::engine as engine_imp; -#[cfg(feature = "js")] -pub(crate) use crate::js::engine::default_engine; - -#[cfg(feature = "jsc")] -use crate::jsc::engine as engine_imp; -#[cfg(feature = "jsc")] -pub(crate) use crate::jsc::engine::default_engine; - -#[cfg(feature = "wasm-c-api")] -use crate::c_api::engine as engine_imp; -#[cfg(feature = "wasm-c-api")] -pub(crate) use crate::c_api::engine::default_engine; - -/// The engine type -#[derive(Clone, Debug)] -pub struct Engine(pub(crate) engine_imp::Engine); - -impl Engine { - /// Returns the deterministic id of this engine - pub fn deterministic_id(&self) -> &str { - self.0.deterministic_id() - } - - #[cfg(all(feature = "sys", not(target_arch = "wasm32")))] - /// Deserializes a WebAssembly module which was previously serialized with - /// `Module::serialize`. - /// - /// NOTE: you should almost always prefer [`Self::deserialize`]. - /// - /// # Safety - /// See [`Artifact::deserialize_unchecked`]. - pub unsafe fn deserialize_unchecked( - &self, - bytes: impl IntoBytes, - ) -> Result, DeserializeError> { - Ok(Arc::new(Artifact::deserialize_unchecked( - &self.0, - bytes.into_bytes().into(), - )?)) - } - - #[cfg(all(feature = "sys", not(target_arch = "wasm32")))] - /// Deserializes a WebAssembly module which was previously serialized with - /// `Module::serialize`. - /// - /// # Safety - /// See [`Artifact::deserialize`]. - pub unsafe fn deserialize( - &self, - bytes: impl IntoBytes, - ) -> Result, DeserializeError> { - Ok(Arc::new(Artifact::deserialize( - &self.0, - bytes.into_bytes().into(), - )?)) - } - - #[cfg(all(feature = "sys", not(target_arch = "wasm32")))] - /// Load a serialized WebAssembly module from a file and deserialize it. - /// - /// NOTE: you should almost always prefer [`Self::deserialize_from_file`]. - /// - /// # Safety - /// See [`Artifact::deserialize_unchecked`]. - pub unsafe fn deserialize_from_file_unchecked( - &self, - file_ref: &Path, - ) -> Result, DeserializeError> { - let file = std::fs::File::open(file_ref)?; - Ok(Arc::new(Artifact::deserialize_unchecked( - &self.0, - OwnedBuffer::from_file(&file) - .map_err(|e| DeserializeError::Generic(format!("{e:?}")))?, - )?)) - } - - #[cfg(all(feature = "sys", not(target_arch = "wasm32")))] - /// Load a serialized WebAssembly module from a file and deserialize it. - /// - /// # Safety - /// See [`Artifact::deserialize`]. - pub unsafe fn deserialize_from_file( - &self, - file_ref: &Path, - ) -> Result, DeserializeError> { - let bytes = std::fs::read(file_ref)?; - Ok(Arc::new(Artifact::deserialize(&self.0, bytes.into())?)) - } -} - -impl AsEngineRef for Engine { - #[inline] - fn as_engine_ref(&self) -> EngineRef { - EngineRef { inner: self } - } -} - -impl Default for Engine { - fn default() -> Self { - Self(default_engine()) - } -} - -impl> From for Engine { - fn from(t: T) -> Self { - Self(t.into()) - } -} - -/// A temporary handle to an [`Engine`] -/// EngineRef can be used to build a [`Module`][super::Module] -/// It can be created directly with an [`Engine`] -/// Or from anything implementing [`AsEngineRef`] -/// like from [`Store`][super::Store] typicaly. -pub struct EngineRef<'a> { - /// The inner engine - pub(crate) inner: &'a Engine, -} - -impl<'a> EngineRef<'a> { - /// Get inner [`Engine`] - pub fn engine(&self) -> &Engine { - self.inner - } - /// Create an EngineRef from an Engine - pub fn new(engine: &'a Engine) -> Self { - EngineRef { inner: engine } - } -} - -/// Helper trait for a value that is convertible to a [`EngineRef`]. -pub trait AsEngineRef { - /// Returns a `EngineRef` pointing to the underlying context. - fn as_engine_ref(&self) -> EngineRef<'_>; - - /// Returns an optional `StoreRef` is the passed reference is a `Store`. - fn maybe_as_store(&self) -> Option> { - None - } -} - -impl AsEngineRef for EngineRef<'_> { - #[inline] - fn as_engine_ref(&self) -> EngineRef<'_> { - EngineRef { inner: self.inner } - } -} - -impl

AsEngineRef for P -where - P: Deref, - P::Target: AsEngineRef, -{ - #[inline] - fn as_engine_ref(&self) -> EngineRef<'_> { - (**self).as_engine_ref() - } - - fn maybe_as_store(&self) -> Option> { - (**self).maybe_as_store() - } -} diff --git a/lib/api/src/entities/engine/engine_ref.rs b/lib/api/src/entities/engine/engine_ref.rs new file mode 100644 index 00000000000..d33eb04860f --- /dev/null +++ b/lib/api/src/entities/engine/engine_ref.rs @@ -0,0 +1,67 @@ +use crate::entities::store::StoreRef; + +use super::Engine; + +/// A temporary handle to an [`Engine`]. +/// +/// An [`EngineRef`] can be used to build a [`Module`][crate::entities::Module], and can be created directly +/// from an [`Engine`] or from anything implementing [`AsEngineRef`], like a +/// [`Store`][crate::entities::Store]. +pub struct EngineRef<'a> { + /// The inner engine + pub(crate) inner: &'a Engine, +} + +impl<'a> EngineRef<'a> { + /// Get inner [`Engine`] + pub fn engine(&self) -> &Engine { + self.inner + } + /// Create an EngineRef from an Engine + pub fn new(engine: &'a Engine) -> Self { + EngineRef { inner: engine } + } +} + +/// Helper trait for a value that is convertible to a [`EngineRef`]. +pub trait AsEngineRef { + /// Create an [`EngineRef`] pointing to the underlying context. + fn as_engine_ref(&self) -> EngineRef<'_>; + + /// Create a [`StoreRef`]. + /// + /// NOTE: this function will return [`None`] if the [`AsEngineRef`] implementor is not an + /// actual [`crate::Store`]. + fn maybe_as_store(&self) -> Option> { + None + } +} + +impl AsEngineRef for EngineRef<'_> { + #[inline] + fn as_engine_ref(&self) -> EngineRef<'_> { + EngineRef { inner: self.inner } + } +} + +impl

AsEngineRef for P +where + P: std::ops::Deref, + P::Target: AsEngineRef, +{ + #[inline] + fn as_engine_ref(&self) -> EngineRef<'_> { + (**self).as_engine_ref() + } + + fn maybe_as_store(&self) -> Option> { + (**self).maybe_as_store() + } +} + +impl AsEngineRef for Engine { + #[inline] + fn as_engine_ref(&self) -> EngineRef { + EngineRef { inner: self } + } +} diff --git a/lib/api/src/entities/engine/inner.rs b/lib/api/src/entities/engine/inner.rs new file mode 100644 index 00000000000..d4f960d7e23 --- /dev/null +++ b/lib/api/src/entities/engine/inner.rs @@ -0,0 +1,186 @@ +use bytes::Bytes; +use std::{path::Path, sync::Arc}; +use wasmer_types::DeserializeError; + +#[cfg(feature = "sys")] +use wasmer_compiler::Artifact; + +use crate::{ + macros::rt::{gen_rt_ty, match_rt}, + IntoBytes, Store, +}; + +gen_rt_ty!(Engine @derives Debug, Clone); + +impl RuntimeEngine { + /// Returns the deterministic id of this engine. + pub fn deterministic_id(&self) -> &str { + match_rt!(on self => s { + s.deterministic_id() + }) + } + + #[cfg(all(feature = "sys", not(target_arch = "wasm32")))] + /// Deserializes a WebAssembly module which was previously serialized with + /// `Module::serialize`, + /// + /// # Note + /// You should almost always prefer [`EngineLike::deserialize`]. + /// + /// # Errors + /// Not every implementer supports serializing and deserializing modules. + /// Currently, only the `sys` engines support it, and only when the target + /// architecture is not `wasm32`. + /// + /// # Safety + /// See [`Artifact::deserialize_unchecked`]. + pub(crate) unsafe fn deserialize_unchecked( + &self, + bytes: impl IntoBytes, + ) -> Result, DeserializeError> { + match self { + #[cfg(feature = "sys")] + Self::Sys(s) => s.deserialize_unchecked(bytes.into_bytes().to_owned().into()), + _ => Err(DeserializeError::Generic( + "The selected runtime does not support `deserialize_unchecked`".into(), + )), + } + } + + #[cfg(all(feature = "sys", not(target_arch = "wasm32")))] + /// Deserializes a WebAssembly module which was previously serialized with + /// `Module::serialize`, + /// + /// # Errors + /// Not every implementer supports serializing and deserializing modules. + /// Currently, only the `sys` engines support it, and only when the target + /// architecture is not `wasm32`. + pub(crate) unsafe fn deserialize( + &self, + bytes: impl IntoBytes, + ) -> Result, DeserializeError> { + match self { + #[cfg(feature = "sys")] + Self::Sys(s) => s.deserialize(bytes.into_bytes().to_owned().into()), + _ => Err(DeserializeError::Generic( + "The selected runtime does not support `deserialize`".into(), + )), + } + } + + #[cfg(all(feature = "sys", not(target_arch = "wasm32")))] + /// Load a serialized WebAssembly module from a file and deserialize it. + /// + /// # Note + /// You should almost always prefer [`Self::deserialize_from_file`]. + /// + /// # Errors + /// Not every implementer supports serializing and deserializing modules. + /// Currently, only the `sys` engines support it, and only when the target + /// architecture is not `wasm32`. + /// + /// # Safety + /// See [`Artifact::deserialize_unchecked`]. + pub(crate) unsafe fn deserialize_from_file_unchecked( + &self, + file_ref: &Path, + ) -> Result, DeserializeError> { + match self { + #[cfg(feature = "sys")] + Self::Sys(s) => s.deserialize_from_file_unchecked(file_ref), + _ => Err(DeserializeError::Generic( + "The selected runtime does not support `deserialize_from_file_unchecked`".into(), + )), + } + } + + #[cfg(all(feature = "sys", not(target_arch = "wasm32")))] + /// Load a serialized WebAssembly module from a file and deserialize it. + /// + /// # Errors + /// Not every implementer supports serializing and deserializing modules. + /// Currently, only the `sys` engines support it, and only when the target + /// architecture is not `wasm32`. + /// + /// # Safety + /// See [`Artifact::deserialize`]. + pub(crate) unsafe fn deserialize_from_file( + &self, + file_ref: &Path, + ) -> Result, DeserializeError> { + match self { + #[cfg(feature = "sys")] + Self::Sys(s) => s.deserialize_from_file(file_ref), + _ => Err(DeserializeError::Generic( + "The selected runtime does not support `deserialize_from_file`".into(), + )), + } + } +} + +impl Default for RuntimeEngine { + #[allow(unreachable_code)] + fn default() -> Self { + #[cfg(feature = "sys-default")] + { + return Self::Sys(crate::rt::sys::entities::engine::default_engine()); + } + + #[cfg(feature = "wamr-default")] + { + return Self::Wamr(crate::rt::wamr::entities::engine::default_engine()); + } + + #[cfg(feature = "wasmi-default")] + { + return Self::Wasmi(crate::rt::wasmi::entities::engine::default_engine()); + } + + #[cfg(feature = "v8-default")] + { + return Self::V8(crate::rt::v8::entities::engine::default_engine()); + } + + #[cfg(feature = "js-default")] + { + return Self::Js(crate::rt::js::entities::engine::default_engine()); + } + + #[cfg(feature = "jsc-default")] + { + return Self::Jsc(crate::rt::jsc::entities::engine::default_engine()); + } + + #[cfg(feature = "sys")] + { + return Self::Sys(crate::rt::sys::entities::engine::default_engine()); + } + + #[cfg(feature = "wamr")] + { + return Self::Wamr(crate::rt::wamr::entities::engine::default_engine()); + } + + #[cfg(feature = "wasmi")] + { + return Self::Wasmi(crate::rt::wasmi::entities::engine::default_engine()); + } + + #[cfg(feature = "v8")] + { + return Self::V8(crate::rt::v8::entities::engine::default_engine()); + } + + #[cfg(feature = "js")] + { + return Self::Js(crate::rt::js::entities::engine::default_engine()); + } + + #[cfg(feature = "jsc")] + { + return Self::Jsc(crate::rt::jsc::entities::engine::default_engine()); + } + + panic!("No runtime enabled!") + } +} diff --git a/lib/api/src/entities/engine/mod.rs b/lib/api/src/entities/engine/mod.rs new file mode 100644 index 00000000000..f402e99698e --- /dev/null +++ b/lib/api/src/entities/engine/mod.rs @@ -0,0 +1,130 @@ +//! Defines the [`Engine`] type, the [`EngineLike`] trait for implementors and useful +//! traits and data types to interact with them. + +use bytes::Bytes; +use std::{path::Path, sync::Arc}; +use wasmer_types::DeserializeError; + +#[cfg(feature = "sys")] +use wasmer_compiler::Artifact; + +use crate::{IntoBytes, Store}; + +/// Create temporary handles to engines. +mod engine_ref; + +/// The actual (private) definition of the engines. +mod inner; +pub(crate) use inner::RuntimeEngine; + +pub use engine_ref::*; + +/// An engine identifier. +#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +pub struct EngineId(u64); + +/// The [`Engine`] is the entrypoint type for the runtime. It defines the kind of steps the runtime must take to execute +/// the WebAssembly module (compile, interpret..) and the place of execution (in-browser, host, +/// ..). +#[derive(Debug, Clone)] +pub struct Engine { + pub(crate) rt: RuntimeEngine, + pub(crate) id: u64, +} + +impl Default for Engine { + fn default() -> Self { + Self { + rt: Default::default(), + id: Self::atomic_next_engine_id(), + } + } +} + +impl Engine { + pub(crate) fn atomic_next_engine_id() -> u64 { + static ENGINE_ID_COUNTER: std::sync::atomic::AtomicU64 = + std::sync::atomic::AtomicU64::new(0); + ENGINE_ID_COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + } + + /// Returns the deterministic id of this engine. + pub fn deterministic_id(&self) -> &str { + self.rt.deterministic_id() + } + + /// Returns the unique id of this engine. + pub fn id(&self) -> EngineId { + EngineId(self.id) + } + + #[cfg(all(feature = "sys", not(target_arch = "wasm32")))] + /// Deserializes a WebAssembly module which was previously serialized with + /// `Module::serialize`, + /// + /// # Note + /// You should almost always prefer [`EngineLike::deserialize`]. + /// + /// # Errors + /// Not every implementer supports serializing and deserializing modules. + /// Currently, only the `sys` engines support it, and only when the target + /// architecture is not `wasm32`. + /// + /// # Safety + /// See [`Artifact::deserialize_unchecked`]. + unsafe fn deserialize_unchecked( + &self, + bytes: impl IntoBytes, + ) -> Result, DeserializeError> { + self.rt.deserialize_unchecked(bytes) + } + + #[cfg(all(feature = "sys", not(target_arch = "wasm32")))] + /// Deserializes a WebAssembly module which was previously serialized with + /// `Module::serialize`, + /// + /// # Errors + /// Not every implementer supports serializing and deserializing modules. + /// Currently, only the `sys` engines support it, and only when the target + /// architecture is not `wasm32`. + unsafe fn deserialize(&self, bytes: impl IntoBytes) -> Result, DeserializeError> { + self.rt.deserialize(bytes) + } + + #[cfg(all(feature = "sys", not(target_arch = "wasm32")))] + /// Load a serialized WebAssembly module from a file and deserialize it. + /// + /// # Note + /// You should almost always prefer [`Self::deserialize_from_file`]. + /// + /// # Errors + /// Not every implementer supports serializing and deserializing modules. + /// Currently, only the `sys` engines support it, and only when the target + /// architecture is not `wasm32`. + /// + /// # Safety + /// See [`Artifact::deserialize_unchecked`]. + unsafe fn deserialize_from_file_unchecked( + &self, + file_ref: &Path, + ) -> Result, DeserializeError> { + self.rt.deserialize_from_file_unchecked(file_ref) + } + + #[cfg(all(feature = "sys", not(target_arch = "wasm32")))] + /// Load a serialized WebAssembly module from a file and deserialize it. + /// + /// # Errors + /// Not every implementer supports serializing and deserializing modules. + /// Currently, only the `sys` engines support it, and only when the target + /// architecture is not `wasm32`. + /// + /// # Safety + /// See [`Artifact::deserialize`]. + unsafe fn deserialize_from_file( + &self, + file_ref: &Path, + ) -> Result, DeserializeError> { + self.rt.deserialize_from_file_unchecked(file_ref) + } +} diff --git a/lib/api/src/exports.rs b/lib/api/src/entities/exports.rs similarity index 100% rename from lib/api/src/exports.rs rename to lib/api/src/entities/exports.rs diff --git a/lib/api/src/externals/mod.rs b/lib/api/src/entities/external.rs similarity index 79% rename from lib/api/src/externals/mod.rs rename to lib/api/src/entities/external.rs index f50ab972f3d..c2a1e950245 100644 --- a/lib/api/src/externals/mod.rs +++ b/lib/api/src/entities/external.rs @@ -1,32 +1,12 @@ -pub(crate) mod function; -mod global; -pub(crate) mod memory; -mod memory_view; -mod table; +use wasmer_types::ExternType; -pub use self::function::{Function, HostFunction}; -pub use self::global::Global; -pub use self::memory::{Memory, MemoryLocation, SharedMemory}; -pub use self::memory_view::MemoryView; -pub use self::table::Table; - -use crate::exports::{ExportError, Exportable}; -use crate::ExternType; -use std::fmt; - -#[cfg(feature = "wasm-c-api")] -use crate::c_api::vm::VMExtern; -#[cfg(feature = "js")] -use crate::js::vm::VMExtern; -#[cfg(feature = "jsc")] -use crate::jsc::vm::VMExtern; -#[cfg(feature = "sys")] -use crate::sys::vm::VMExtern; - -use crate::store::{AsStoreMut, AsStoreRef}; +use crate::{ + vm::VMExtern, AsStoreMut, AsStoreRef, ExportError, Exportable, Function, Global, Memory, Table, +}; /// Trait convert a VMExtern to a Extern pub trait VMExternToExtern { + /// Convert to [`Extern`] fn to_extern(self, store: &mut impl AsStoreMut) -> Extern; } @@ -91,8 +71,8 @@ impl<'a> Exportable<'a> for Extern { } } -impl fmt::Debug for Extern { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl std::fmt::Debug for Extern { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, "{}", diff --git a/lib/api/src/entities/extref/inner.rs b/lib/api/src/entities/extref/inner.rs new file mode 100644 index 00000000000..b6da8deeb73 --- /dev/null +++ b/lib/api/src/entities/extref/inner.rs @@ -0,0 +1,135 @@ +use std::any::Any; + +use crate::entities::store::{AsStoreMut, AsStoreRef}; +use crate::macros::rt::{gen_rt_ty, match_rt}; +use crate::vm::VMExternRef; +use crate::StoreRef; + +gen_rt_ty!(ExternRef @derives derive_more::From, Debug, Clone ; @path external); + +impl RuntimeExternRef { + /// Make a new extern reference + pub fn new(store: &mut impl AsStoreMut, value: T) -> Self + where + T: Any + Send + Sync + 'static + Sized, + { + match &store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(s) => Self::Sys( + crate::rt::sys::entities::external::ExternRef::new(store, value), + ), + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(s) => Self::Wamr( + crate::rt::wamr::entities::external::ExternRef::new(store, value), + ), + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(s) => Self::Wasmi( + crate::rt::wasmi::entities::external::ExternRef::new(store, value), + ), + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(s) => Self::V8( + crate::rt::v8::entities::external::ExternRef::new(store, value), + ), + #[cfg(feature = "js")] + crate::RuntimeStore::Js(s) => Self::Js( + crate::rt::js::entities::external::ExternRef::new(store, value), + ), + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(s) => Self::Jsc( + crate::rt::jsc::entities::external::ExternRef::new(store, value), + ), + } + } + + /// Try to downcast to the given value. + pub fn downcast<'a, T>(&self, store: &'a impl AsStoreRef) -> Option<&'a T> + where + T: Any + Send + Sync + 'static + Sized, + { + match_rt!(on self => r { + r.downcast::(store) + }) + } + + /// Create a [`VMExternRef`] from [`Self`]. + pub(crate) fn vm_externref(&self) -> VMExternRef { + match self { + #[cfg(feature = "sys")] + Self::Sys(r) => VMExternRef::Sys(r.vm_externref()), + #[cfg(feature = "wamr")] + Self::Wamr(r) => VMExternRef::Wamr(r.vm_externref()), + #[cfg(feature = "wasmi")] + Self::Wasmi(r) => VMExternRef::Wasmi(r.vm_externref()), + #[cfg(feature = "v8")] + Self::V8(r) => VMExternRef::V8(r.vm_externref()), + #[cfg(feature = "js")] + Self::Js(r) => VMExternRef::Js(r.vm_externref()), + #[cfg(feature = "jsc")] + Self::Jsc(r) => VMExternRef::Jsc(r.vm_externref()), + } + } + + /// Create an instance of [`Self`] from a [`VMExternRef`]. + pub(crate) unsafe fn from_vm_externref( + store: &mut impl AsStoreMut, + vm_externref: VMExternRef, + ) -> Self { + match &store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(_) => Self::Sys( + crate::rt::sys::entities::external::ExternRef::from_vm_externref( + store, + vm_externref.into_sys(), + ), + ), + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(_) => Self::Wamr( + crate::rt::wamr::entities::external::ExternRef::from_vm_externref( + store, + vm_externref.into_wamr(), + ), + ), + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(_) => Self::Wasmi( + crate::rt::wasmi::entities::external::ExternRef::from_vm_externref( + store, + vm_externref.into_wasmi(), + ), + ), + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(_) => Self::V8( + crate::rt::v8::entities::external::ExternRef::from_vm_externref( + store, + vm_externref.into_v8(), + ), + ), + #[cfg(feature = "js")] + crate::RuntimeStore::Js(_) => Self::Js( + crate::rt::js::entities::external::ExternRef::from_vm_externref( + store, + vm_externref.into_js(), + ), + ), + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(_) => Self::Jsc( + crate::rt::jsc::entities::external::ExternRef::from_vm_externref( + store, + vm_externref.into_jsc(), + ), + ), + } + } + + /// Checks whether this `ExternRef` can be used with the given context. + /// + /// Primitive (`i32`, `i64`, etc) and null funcref/externref values are not + /// tied to a context and can be freely shared between contexts. + /// + /// Externref and funcref values are tied to a context and can only be used + /// with that context. + pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + match_rt!(on self => r { + r.is_from_store(store) + }) + } +} diff --git a/lib/api/src/extern_ref.rs b/lib/api/src/entities/extref/mod.rs similarity index 66% rename from lib/api/src/extern_ref.rs rename to lib/api/src/entities/extref/mod.rs index 4a26eb40f30..89c07050706 100644 --- a/lib/api/src/extern_ref.rs +++ b/lib/api/src/entities/extref/mod.rs @@ -1,21 +1,15 @@ use std::any::Any; -use crate::store::{AsStoreMut, AsStoreRef}; - -#[cfg(feature = "wasm-c-api")] -use crate::c_api::extern_ref as extern_ref_imp; -#[cfg(feature = "js")] -use crate::js::extern_ref as extern_ref_imp; -#[cfg(feature = "jsc")] -use crate::jsc::extern_ref as extern_ref_imp; -#[cfg(feature = "sys")] -use crate::sys::extern_ref as extern_ref_imp; +use crate::entities::store::{AsStoreMut, AsStoreRef}; use crate::vm::VMExternRef; +use crate::StoreRef; -#[derive(Debug, Clone)] -#[repr(transparent)] +pub(crate) mod inner; +pub(crate) use inner::*; + +#[derive(Debug, Clone, derive_more::From)] /// An opaque reference to some data. This reference can be passed through Wasm. -pub struct ExternRef(pub(crate) extern_ref_imp::ExternRef); +pub struct ExternRef(pub(crate) RuntimeExternRef); impl ExternRef { /// Make a new extern reference @@ -23,7 +17,7 @@ impl ExternRef { where T: Any + Send + Sync + 'static + Sized, { - Self(extern_ref_imp::ExternRef::new(store, value)) + Self(RuntimeExternRef::new(store, value)) } /// Try to downcast to the given value. @@ -34,18 +28,17 @@ impl ExternRef { self.0.downcast(store) } + /// Create a [`VMExternRef`] from [`Self`]. pub(crate) fn vm_externref(&self) -> VMExternRef { self.0.vm_externref() } + /// Create an instance of [`Self`] from a [`VMExternRef`]. pub(crate) unsafe fn from_vm_externref( store: &mut impl AsStoreMut, vm_externref: VMExternRef, ) -> Self { - Self(extern_ref_imp::ExternRef::from_vm_externref( - store, - vm_externref, - )) + Self(RuntimeExternRef::from_vm_externref(store, vm_externref)) } /// Checks whether this `ExternRef` can be used with the given context. diff --git a/lib/api/src/entities/function/env/inner.rs b/lib/api/src/entities/function/env/inner.rs new file mode 100644 index 00000000000..654fb2275b6 --- /dev/null +++ b/lib/api/src/entities/function/env/inner.rs @@ -0,0 +1,239 @@ +use crate::{ + macros::rt::match_rt, AsStoreMut, AsStoreRef, FunctionEnv, FunctionEnvMut, StoreMut, StoreRef, +}; +use std::{any::Any, marker::PhantomData}; + +#[derive(Debug, derive_more::From)] +/// An opaque reference to a function environment. +/// The function environment data is owned by the `Store`. +pub enum RuntimeFunctionEnv { + #[cfg(feature = "sys")] + /// The function environment for the `sys` runtime. + Sys(crate::rt::sys::function::env::FunctionEnv), + #[cfg(feature = "wamr")] + /// The function environment for the `wamr` runtime. + Wamr(crate::rt::wamr::function::env::FunctionEnv), + #[cfg(feature = "wasmi")] + /// The function environment for the `wasmi` runtime. + Wasmi(crate::rt::wasmi::function::env::FunctionEnv), + #[cfg(feature = "v8")] + /// The function environment for the `v8` runtime. + V8(crate::rt::v8::function::env::FunctionEnv), + #[cfg(feature = "js")] + /// The function environment for the `js` runtime. + Js(crate::rt::js::function::env::FunctionEnv), + #[cfg(feature = "jsc")] + /// The function environment for the `jsc` runtime. + Jsc(crate::rt::jsc::function::env::FunctionEnv), +} + +impl Clone for RuntimeFunctionEnv { + fn clone(&self) -> Self { + match self { + #[cfg(feature = "sys")] + Self::Sys(s) => Self::Sys(s.clone()), + #[cfg(feature = "wamr")] + Self::Wamr(s) => Self::Wamr(s.clone()), + + #[cfg(feature = "wasmi")] + Self::Wasmi(s) => Self::Wasmi(s.clone()), + #[cfg(feature = "v8")] + Self::V8(s) => Self::V8(s.clone()), + #[cfg(feature = "js")] + Self::Js(s) => Self::Js(s.clone()), + #[cfg(feature = "jsc")] + Self::Jsc(s) => Self::Jsc(s.clone()), + } + } +} + +impl RuntimeFunctionEnv { + /// Make a new FunctionEnv + pub fn new(store: &mut impl AsStoreMut, value: T) -> Self + where + T: Any + Send + 'static + Sized, + { + match store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(_) => Self::Sys( + crate::rt::sys::function::env::FunctionEnv::new(store, value), + ), + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(_) => Self::Wamr( + crate::rt::wamr::function::env::FunctionEnv::new(store, value), + ), + + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(_) => Self::Wasmi( + crate::rt::wasmi::function::env::FunctionEnv::new(store, value), + ), + + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(_) => { + Self::V8(crate::rt::v8::function::env::FunctionEnv::new(store, value)) + } + + #[cfg(feature = "js")] + crate::RuntimeStore::Js(_) => { + Self::Js(crate::rt::js::function::env::FunctionEnv::new(store, value)) + } + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(_) => Self::Jsc( + crate::rt::jsc::function::env::FunctionEnv::new(store, value), + ), + } + } + + //#[allow(dead_code)] // This function is only used in js + //pub(crate) fn from_handle(handle: StoreHandle) -> Self { + // todo!() + //} + + /// Get the data as reference + pub fn as_ref<'a>(&self, store: &'a impl AsStoreRef) -> &'a T + where + T: Any + Send + 'static + Sized, + { + match_rt!(on self => f { + f.as_ref(store) + }) + } + + /// Get the data as mutable + pub fn as_mut<'a>(&self, store: &'a mut impl AsStoreMut) -> &'a mut T + where + T: Any + Send + 'static + Sized, + { + match_rt!(on self => s { + s.as_mut(store) + }) + } + + /// Convert it into a `FunctionEnvMut` + pub fn into_mut(self, store: &mut impl AsStoreMut) -> FunctionEnvMut + where + T: Any + Send + 'static + Sized, + { + match_rt!(on self => f { + f.into_mut(store).into() + }) + } +} + +/// A temporary handle to a [`FunctionEnv`]. +#[derive(derive_more::From)] +pub enum RuntimeFunctionEnvMut<'a, T: 'a> { + #[cfg(feature = "sys")] + /// The function environment for the `sys` runtime. + Sys(crate::rt::sys::function::env::FunctionEnvMut<'a, T>), + #[cfg(feature = "wamr")] + /// The function environment for the `wamr` runtime. + Wamr(crate::rt::wamr::function::env::FunctionEnvMut<'a, T>), + + #[cfg(feature = "wasmi")] + /// The function environment for the `wasmi` runtime. + Wasmi(crate::rt::wasmi::function::env::FunctionEnvMut<'a, T>), + #[cfg(feature = "v8")] + /// The function environment for the `v8` runtime. + V8(crate::rt::v8::function::env::FunctionEnvMut<'a, T>), + + #[cfg(feature = "js")] + /// The function environment for the `js` runtime. + Js(crate::rt::js::function::env::FunctionEnvMut<'a, T>), + + #[cfg(feature = "jsc")] + /// The function environment for the `jsc` runtime. + Jsc(crate::rt::jsc::function::env::FunctionEnvMut<'a, T>), +} + +impl RuntimeFunctionEnvMut<'_, T> { + /// Returns a reference to the host state in this function environement. + pub fn data(&self) -> &T { + match_rt!(on self => f { + f.data() + }) + } + + /// Returns a mutable- reference to the host state in this function environement. + pub fn data_mut(&mut self) -> &mut T { + match_rt!(on self => f { + f.data_mut() + }) + } + + /// Borrows a new immmutable reference + pub fn as_ref(&self) -> FunctionEnv { + match self { + #[cfg(feature = "sys")] + Self::Sys(ref f) => RuntimeFunctionEnv::Sys(f.as_ref()).into(), + #[cfg(feature = "wamr")] + Self::Wamr(ref f) => RuntimeFunctionEnv::Wamr(f.as_ref()).into(), + #[cfg(feature = "wasmi")] + Self::Wasmi(ref f) => RuntimeFunctionEnv::Wasmi(f.as_ref()).into(), + #[cfg(feature = "v8")] + Self::V8(ref f) => RuntimeFunctionEnv::V8(f.as_ref()).into(), + #[cfg(feature = "js")] + Self::Js(ref f) => RuntimeFunctionEnv::Js(f.as_ref()).into(), + #[cfg(feature = "jsc")] + Self::Jsc(ref f) => RuntimeFunctionEnv::Jsc(f.as_ref()).into(), + } + } + + /// Borrows a new mutable reference + pub fn as_mut(&mut self) -> FunctionEnvMut<'_, T> { + match self { + #[cfg(feature = "sys")] + Self::Sys(ref mut f) => RuntimeFunctionEnvMut::Sys(f.as_mut()).into(), + #[cfg(feature = "wamr")] + Self::Wamr(ref mut f) => RuntimeFunctionEnvMut::Wamr(f.as_mut()).into(), + #[cfg(feature = "wasmi")] + Self::Wasmi(ref mut f) => RuntimeFunctionEnvMut::Wasmi(f.as_mut()).into(), + #[cfg(feature = "v8")] + Self::V8(ref mut f) => RuntimeFunctionEnvMut::V8(f.as_mut()).into(), + #[cfg(feature = "js")] + Self::Js(ref mut f) => RuntimeFunctionEnvMut::Js(f.as_mut()).into(), + #[cfg(feature = "jsc")] + Self::Jsc(ref mut f) => RuntimeFunctionEnvMut::Jsc(f.as_mut()).into(), + } + } + + /// Borrows a new mutable reference of both the attached Store and host state + pub fn data_and_store_mut(&mut self) -> (&mut T, StoreMut) { + match_rt!(on self => f { + f.data_and_store_mut() + }) + } +} + +impl AsStoreRef for RuntimeFunctionEnvMut<'_, T> { + fn as_store_ref(&self) -> StoreRef<'_> { + match_rt!(on &self => f { + f.as_store_ref() + }) + } +} + +impl AsStoreMut for RuntimeFunctionEnvMut<'_, T> { + fn as_store_mut(&mut self) -> StoreMut<'_> { + match_rt!(on self => s { + s.as_store_mut() + }) + } + + fn objects_mut(&mut self) -> &mut crate::StoreObjects { + match_rt!(on self => s { + s.objects_mut() + }) + } +} + +impl<'a, T> std::fmt::Debug for RuntimeFunctionEnvMut<'a, T> +where + T: Send + std::fmt::Debug + 'static, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match_rt!(on self => s { + write!(f, "{s:?}") + }) + } +} diff --git a/lib/api/src/entities/function/env/mod.rs b/lib/api/src/entities/function/env/mod.rs new file mode 100644 index 00000000000..9b9478902f3 --- /dev/null +++ b/lib/api/src/entities/function/env/mod.rs @@ -0,0 +1,111 @@ +pub(crate) mod inner; +pub(crate) use inner::*; + +use crate::{macros::rt::match_rt, AsStoreMut, AsStoreRef, StoreMut, StoreRef}; +use std::{any::Any, fmt::Debug, marker::PhantomData}; + +#[derive(Debug, derive_more::From)] +/// An opaque reference to a function environment. +/// The function environment data is owned by the `Store`. +pub struct FunctionEnv(pub(crate) RuntimeFunctionEnv); + +impl Clone for FunctionEnv { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl FunctionEnv { + /// Make a new FunctionEnv + pub fn new(store: &mut impl AsStoreMut, value: T) -> Self + where + T: Any + Send + 'static + Sized, + { + Self(RuntimeFunctionEnv::new(store, value)) + } + + //#[allow(dead_code)] // This function is only used in js + //pub(crate) fn from_handle(handle: StoreHandle) -> Self { + // todo!() + //} + + /// Get the data as reference + pub fn as_ref<'a>(&self, store: &'a impl AsStoreRef) -> &'a T + where + T: Any + Send + 'static + Sized, + { + self.0.as_ref(store) + } + + /// Get the data as mutable + pub fn as_mut<'a>(&self, store: &'a mut impl AsStoreMut) -> &'a mut T + where + T: Any + Send + 'static + Sized, + { + self.0.as_mut(store) + } + + /// Convert it into a `FunctionEnvMut` + pub fn into_mut(self, store: &mut impl AsStoreMut) -> FunctionEnvMut + where + T: Any + Send + 'static + Sized, + { + self.0.into_mut(store) + } +} + +/// A temporary handle to a [`FunctionEnv`]. +#[derive(derive_more::From)] +pub struct FunctionEnvMut<'a, T: 'a>(pub(crate) RuntimeFunctionEnvMut<'a, T>); + +impl FunctionEnvMut<'_, T> { + /// Returns a reference to the host state in this function environement. + pub fn data(&self) -> &T { + self.0.data() + } + + /// Returns a mutable- reference to the host state in this function environement. + pub fn data_mut(&mut self) -> &mut T { + self.0.data_mut() + } + + /// Borrows a new immmutable reference + pub fn as_ref(&self) -> FunctionEnv { + self.0.as_ref() + } + + /// Borrows a new mutable reference + pub fn as_mut(&mut self) -> FunctionEnvMut<'_, T> { + self.0.as_mut() + } + + /// Borrows a new mutable reference of both the attached Store and host state + pub fn data_and_store_mut(&mut self) -> (&mut T, StoreMut) { + self.0.data_and_store_mut() + } +} + +impl AsStoreRef for FunctionEnvMut<'_, T> { + fn as_store_ref(&self) -> StoreRef<'_> { + self.0.as_store_ref() + } +} + +impl AsStoreMut for FunctionEnvMut<'_, T> { + fn as_store_mut(&mut self) -> StoreMut<'_> { + self.0.as_store_mut() + } + + fn objects_mut(&mut self) -> &mut crate::StoreObjects { + self.0.objects_mut() + } +} + +impl<'a, T> std::fmt::Debug for FunctionEnvMut<'a, T> +where + T: Send + std::fmt::Debug + 'static, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} diff --git a/lib/api/src/entities/function/host/imp.rs b/lib/api/src/entities/function/host/imp.rs new file mode 100644 index 00000000000..8546af561e3 --- /dev/null +++ b/lib/api/src/entities/function/host/imp.rs @@ -0,0 +1,144 @@ +use crate::{ + utils::{FromToNativeWasmType, IntoResult, NativeWasmTypeInto, WasmTypeList}, + AsStoreMut, AsStoreRef, FunctionEnvMut, FunctionType, HostFunction, RuntimeError, + RuntimeFunctionEnv, RuntimeFunctionEnvMut, StoreInner, StoreMut, StoreRef, Value, WithEnv, + WithoutEnv, +}; + +use std::panic::{self, AssertUnwindSafe}; +use std::{cell::UnsafeCell, cmp::max, ffi::c_void}; +use wasmer_types::{NativeWasmType, RawValue}; + +macro_rules! impl_host_function { + ([$c_struct_representation:ident] $c_struct_name:ident, $( $x:ident ),* ) => { +#[allow(unused_parens)] +impl< $( $x, )* Rets, RetsAsResult, T, Func> crate::HostFunction for Func where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + RetsAsResult: IntoResult, + T: Send + 'static, + Func: Fn(FunctionEnvMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static, +{ + + #[allow(non_snake_case)] + fn function_callback(&self, rt: crate::rt::Runtime) -> crate::vm::VMFunctionCallback { + paste::paste!{ + match rt { + #[cfg(feature = "sys")] + crate::rt::Runtime::Sys => crate::vm::VMFunctionCallback::Sys(crate::rt::sys::function::[](self)), + #[cfg(feature = "js")] + crate::rt::Runtime::Js => crate::vm::VMFunctionCallback::Js(crate::rt::js::function::[](self)), + #[cfg(feature = "jsc")] + crate::rt::Runtime::Jsc => crate::vm::VMFunctionCallback::Jsc(crate::rt::jsc::function::[](self)), + #[cfg(feature = "wamr")] + crate::rt::Runtime::Wamr => crate::vm::VMFunctionCallback::Wamr(crate::rt::wamr::function::[](self)), + #[cfg(feature = "wasmi")] + crate::rt::Runtime::Wasmi => crate::vm::VMFunctionCallback::Wasmi(crate::rt::wasmi::function::[](self)), + #[cfg(feature = "v8")] + crate::rt::Runtime::V8 => crate::vm::VMFunctionCallback::V8(crate::rt::v8::function::[](self)) + } + } + } + + #[allow(non_snake_case)] + fn call_trampoline_address(rt: crate::rt::Runtime) -> crate::vm::VMTrampoline { + paste::paste!{ + match rt { + #[cfg(feature = "sys")] + crate::rt::Runtime::Sys => crate::vm::VMTrampoline::Sys(crate::rt::sys::function::[]::<$($x,)* Rets>()), + _ => unimplemented!() + } + } + } + +} + +// Implement `HostFunction` for a function that has the same arity than the tuple. +#[allow(unused_parens)] +impl< $( $x, )* Rets, RetsAsResult, Func > + crate::HostFunction<(), ( $( $x ),* ), Rets, WithoutEnv> +for + Func +where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + RetsAsResult: IntoResult, + Func: Fn($( $x , )*) -> RetsAsResult + 'static +{ + + + #[allow(non_snake_case)] + fn function_callback(&self, rt: crate::rt::Runtime) -> crate::vm::VMFunctionCallback { + paste::paste!{ + match rt { + #[cfg(feature = "sys")] + crate::rt::Runtime::Sys => crate::vm::VMFunctionCallback::Sys(crate::rt::sys::function::[](self)), + #[cfg(feature = "js")] + crate::rt::Runtime::Js => crate::vm::VMFunctionCallback::Js(crate::rt::js::function::[](self)), + #[cfg(feature = "jsc")] + crate::rt::Runtime::Jsc => crate::vm::VMFunctionCallback::Jsc(crate::rt::jsc::function::[](self)), + #[cfg(feature = "wamr")] + crate::rt::Runtime::Wamr => crate::vm::VMFunctionCallback::Wamr(crate::rt::wamr::function::[](self)), + #[cfg(feature = "wasmi")] + crate::rt::Runtime::Wasmi => crate::vm::VMFunctionCallback::Wasmi(crate::rt::wasmi::function::[](self)), + #[cfg(feature = "v8")] + crate::rt::Runtime::V8 => crate::vm::VMFunctionCallback::V8(crate::rt::v8::function::[](self)) + } + } + } + + #[allow(non_snake_case)] + fn call_trampoline_address(rt: crate::rt::Runtime) -> crate::vm::VMTrampoline { + paste::paste!{ + match rt { + #[cfg(feature = "sys")] + crate::rt::Runtime::Sys => crate::vm::VMTrampoline::Sys(crate::rt::sys::function::[]::<$($x,)* Rets>()), + _ => unimplemented!() + } + } + } + } + }; +} + +// Black-magic to count the number of identifiers at compile-time. +macro_rules! count_idents { + ( $($idents:ident),* ) => { + { + #[allow(dead_code, non_camel_case_types)] + enum Idents { $( $idents, )* __CountIdentsLast } + const COUNT: usize = Idents::__CountIdentsLast as usize; + COUNT + } + }; +} + +// Here we go! Let's generate all the C struct, `WasmTypeList` +// implementations and `HostFunction` implementations. +impl_host_function!([C] S0,); +impl_host_function!([transparent] S1, A1); +impl_host_function!([C] S2, A1, A2); +impl_host_function!([C] S3, A1, A2, A3); +impl_host_function!([C] S4, A1, A2, A3, A4); +impl_host_function!([C] S5, A1, A2, A3, A4, A5); +impl_host_function!([C] S6, A1, A2, A3, A4, A5, A6); +impl_host_function!([C] S7, A1, A2, A3, A4, A5, A6, A7); +impl_host_function!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8); +impl_host_function!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9); +impl_host_function!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); +impl_host_function!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); +impl_host_function!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); +impl_host_function!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); +impl_host_function!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); +impl_host_function!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); +impl_host_function!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16); +impl_host_function!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17); +impl_host_function!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18); +impl_host_function!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19); +impl_host_function!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20); +impl_host_function!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21); +impl_host_function!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22); +impl_host_function!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23); +impl_host_function!([C] S24, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24); +impl_host_function!([C] S25, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25); +impl_host_function!([C] S26, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26); diff --git a/lib/api/src/entities/function/host/mod.rs b/lib/api/src/entities/function/host/mod.rs new file mode 100644 index 00000000000..6fc70f4bddb --- /dev/null +++ b/lib/api/src/entities/function/host/mod.rs @@ -0,0 +1,54 @@ +mod imp; + +use crate::{ + vm::{VMFunctionCallback, VMTrampoline}, + Runtime, WasmTypeList, +}; + +/// The `HostFunction` trait represents the set of functions that +/// can be used as host function. To uphold this statement, it is +/// necessary for a function to be transformed into a +/// `VMFunctionCallback`. +pub trait HostFunction +where + Args: WasmTypeList, + Rets: WasmTypeList, + Kind: HostFunctionKind, +{ + /// Get the pointer to the function body for a given runtime. + fn function_callback(&self, rt: Runtime) -> crate::vm::VMFunctionCallback; + + /// Get the pointer to the function call trampoline for a given runtime. + fn call_trampoline_address(rt: Runtime) -> crate::vm::VMTrampoline; +} + +/// Empty trait to specify the kind of `HostFunction`: With or +/// without an environment. +/// +/// This trait is never aimed to be used by a user. It is used by +/// the trait system to automatically generate the appropriate +/// host functions. +#[doc(hidden)] +pub trait HostFunctionKind: private::HostFunctionKindSealed {} + +/// An empty struct to help Rust typing to determine +/// when a `HostFunction` does have an environment. +pub struct WithEnv; + +impl HostFunctionKind for WithEnv {} + +/// An empty struct to help Rust typing to determine +/// when a `HostFunction` does not have an environment. +pub struct WithoutEnv; + +impl HostFunctionKind for WithoutEnv {} + +mod private { + //! Sealing the HostFunctionKind because it shouldn't be implemented + //! by any type outside. + //! See: + //! + pub trait HostFunctionKindSealed {} + impl HostFunctionKindSealed for super::WithEnv {} + impl HostFunctionKindSealed for super::WithoutEnv {} +} diff --git a/lib/api/src/entities/function/inner.rs b/lib/api/src/entities/function/inner.rs new file mode 100644 index 00000000000..5e0f41fc235 --- /dev/null +++ b/lib/api/src/entities/function/inner.rs @@ -0,0 +1,579 @@ +use wasmer_types::{FunctionType, RawValue}; + +use crate::{ + error::RuntimeError, + macros::rt::{gen_rt_ty, match_rt}, + vm::{VMExtern, VMExternFunction, VMFuncRef}, + AsStoreMut, AsStoreRef, ExportError, Exportable, Extern, FunctionEnv, FunctionEnvMut, + HostFunction, StoreMut, StoreRef, TypedFunction, Value, WasmTypeList, WithEnv, WithoutEnv, +}; + +/// A WebAssembly `function` instance. +/// +/// A function instance is the runtime representation of a function. +/// It effectively is a closure of the original function (defined in either +/// the host or the WebAssembly module) over the runtime `Instance` of its +/// originating `Module`. +/// +/// The module instance is used to resolve references to other definitions +/// during execution of the function. +/// +/// Spec: +/// +/// # Panics +/// - Closures (functions with captured environments) are not currently supported +/// with native functions. Attempting to create a native `Function` with one will +/// result in a panic. +/// [Closures as host functions tracking issue](https://github.com/wasmerio/wasmer/issues/1840) +gen_rt_ty!(Function + @cfg feature = "artifact-size" => derive(loupe::MemoryUsage) + @derives Debug, Clone, PartialEq, Eq +); + +impl RuntimeFunction { + /// Creates a new host `Function` (dynamic) with the provided signature. + /// + /// If you know the signature of the host function at compile time, + /// consider using [`Function::new_typed`] for less runtime overhead. + pub fn new(store: &mut impl AsStoreMut, ty: FT, func: F) -> Self + where + FT: Into, + F: Fn(&[Value]) -> Result, RuntimeError> + 'static + Send + Sync, + { + let env = FunctionEnv::new(&mut store.as_store_mut(), ()); + let wrapped_func = move |_env: FunctionEnvMut<()>, + args: &[Value]| + -> Result, RuntimeError> { func(args) }; + Self::new_with_env(store, &env, ty, wrapped_func) + } + + /// Creates a new host `Function` (dynamic) with the provided signature. + /// + /// If you know the signature of the host function at compile time, + /// consider using [`Function::new_typed_with_env`] for less runtime overhead. + /// + /// Takes a [`FunctionEnv`] that is passed into func. If that is not required, + /// [`Function::new`] might be an option as well. + /// + /// # Examples + /// + /// ``` + /// # use wasmer::{Function, FunctionEnv, FunctionType, Type, Store, Value}; + /// # let mut store = Store::default(); + /// # let env = FunctionEnv::new(&mut store, ()); + /// # + /// let signature = FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]); + /// + /// let f = Function::new_with_env(&mut store, &env, &signature, |_env, args| { + /// let sum = args[0].unwrap_i32() + args[1].unwrap_i32(); + /// Ok(vec![Value::I32(sum)]) + /// }); + /// ``` + /// + /// With constant signature: + /// + /// ``` + /// # use wasmer::{Function, FunctionEnv, FunctionType, Type, Store, Value}; + /// # let mut store = Store::default(); + /// # let env = FunctionEnv::new(&mut store, ()); + /// # + /// const I32_I32_TO_I32: ([Type; 2], [Type; 1]) = ([Type::I32, Type::I32], [Type::I32]); + /// + /// let f = Function::new_with_env(&mut store, &env, I32_I32_TO_I32, |_env, args| { + /// let sum = args[0].unwrap_i32() + args[1].unwrap_i32(); + /// Ok(vec![Value::I32(sum)]) + /// }); + /// ``` + pub fn new_with_env( + store: &mut impl AsStoreMut, + env: &FunctionEnv, + ty: FT, + func: F, + ) -> Self + where + FT: Into, + F: Fn(FunctionEnvMut, &[Value]) -> Result, RuntimeError> + + 'static + + Send + + Sync, + { + match &store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(_) => Self::Sys( + crate::rt::sys::entities::function::Function::new_with_env(store, env, ty, func), + ), + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(_) => Self::Wamr( + crate::rt::wamr::entities::function::Function::new_with_env(store, env, ty, func), + ), + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(_) => Self::Wasmi( + crate::rt::wasmi::entities::function::Function::new_with_env(store, env, ty, func), + ), + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(_) => Self::V8( + crate::rt::v8::entities::function::Function::new_with_env(store, env, ty, func), + ), + #[cfg(feature = "js")] + crate::RuntimeStore::Js(_) => Self::Js( + crate::rt::js::entities::function::Function::new_with_env(store, env, ty, func), + ), + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(_) => Self::Jsc( + crate::rt::jsc::entities::function::Function::new_with_env(store, env, ty, func), + ), + } + } + + /// Creates a new host `Function` from a native function. + pub fn new_typed(store: &mut impl AsStoreMut, func: F) -> Self + where + F: HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync, + Args: WasmTypeList, + Rets: WasmTypeList, + { + match &store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(_) => Self::Sys( + crate::rt::sys::entities::function::Function::new_typed(store, func), + ), + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(_) => Self::Wamr( + crate::rt::wamr::entities::function::Function::new_typed(store, func), + ), + + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(_) => Self::Wasmi( + crate::rt::wasmi::entities::function::Function::new_typed(store, func), + ), + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(_) => Self::V8( + crate::rt::v8::entities::function::Function::new_typed(store, func), + ), + #[cfg(feature = "js")] + crate::RuntimeStore::Js(_) => Self::Js( + crate::rt::js::entities::function::Function::new_typed(store, func), + ), + + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(_) => Self::Jsc( + crate::rt::jsc::entities::function::Function::new_typed(store, func), + ), + } + } + + /// Creates a new host `Function` with an environment from a typed function. + /// + /// The function signature is automatically retrieved using the + /// Rust typing system. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Store, Function, FunctionEnv, FunctionEnvMut}; + /// # let mut store = Store::default(); + /// # let env = FunctionEnv::new(&mut store, ()); + /// # + /// fn sum(_env: FunctionEnvMut<()>, a: i32, b: i32) -> i32 { + /// a + b + /// } + /// + /// let f = Function::new_typed_with_env(&mut store, &env, sum); + /// ``` + pub fn new_typed_with_env( + store: &mut impl AsStoreMut, + env: &FunctionEnv, + func: F, + ) -> Self + where + F: HostFunction + 'static + Send + Sync, + Args: WasmTypeList, + Rets: WasmTypeList, + { + match &store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(s) => Self::Sys( + crate::rt::sys::entities::function::Function::new_typed_with_env(store, env, func), + ), + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(s) => Self::Wamr( + crate::rt::wamr::entities::function::Function::new_typed_with_env(store, env, func), + ), + + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(s) => Self::Wasmi( + crate::rt::wasmi::entities::function::Function::new_typed_with_env( + store, env, func, + ), + ), + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(s) => Self::V8( + crate::rt::v8::entities::function::Function::new_typed_with_env(store, env, func), + ), + #[cfg(feature = "js")] + crate::RuntimeStore::Js(s) => Self::Js( + crate::rt::js::entities::function::Function::new_typed_with_env(store, env, func), + ), + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(s) => Self::Jsc( + crate::rt::jsc::entities::function::Function::new_typed_with_env(store, env, func), + ), + } + } + + /// Returns the [`FunctionType`] of the `Function`. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Function, FunctionEnv, FunctionEnvMut, Store, Type}; + /// # let mut store = Store::default(); + /// # let env = FunctionEnv::new(&mut store, ()); + /// # + /// fn sum(_env: FunctionEnvMut<()>, a: i32, b: i32) -> i32 { + /// a + b + /// } + /// + /// let f = Function::new_typed_with_env(&mut store, &env, sum); + /// + /// assert_eq!(f.ty(&mut store).params(), vec![Type::I32, Type::I32]); + /// assert_eq!(f.ty(&mut store).results(), vec![Type::I32]); + /// ``` + pub fn ty(&self, store: &impl AsStoreRef) -> FunctionType { + match_rt!(on self => f { + f.ty(store) + }) + } + + /// Returns the number of parameters that this function takes. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Function, FunctionEnv, FunctionEnvMut, Store, Type}; + /// # let mut store = Store::default(); + /// # let env = FunctionEnv::new(&mut store, ()); + /// # + /// fn sum(_env: FunctionEnvMut<()>, a: i32, b: i32) -> i32 { + /// a + b + /// } + /// + /// let f = Function::new_typed_with_env(&mut store, &env, sum); + /// + /// assert_eq!(f.param_arity(&mut store), 2); + /// ``` + pub fn param_arity(&self, store: &impl AsStoreRef) -> usize { + self.ty(store).params().len() + } + + /// Returns the number of results this function produces. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Function, FunctionEnv, FunctionEnvMut, Store, Type}; + /// # let mut store = Store::default(); + /// # let env = FunctionEnv::new(&mut store, ()); + /// # + /// fn sum(_env: FunctionEnvMut<()>, a: i32, b: i32) -> i32 { + /// a + b + /// } + /// + /// let f = Function::new_typed_with_env(&mut store, &env, sum); + /// + /// assert_eq!(f.result_arity(&mut store), 1); + /// ``` + pub fn result_arity(&self, store: &impl AsStoreRef) -> usize { + self.ty(store).params().len() + } + + /// Call the `Function` function. + /// + /// Depending on where the Function is defined, it will call it. + /// 1. If the function is defined inside a WebAssembly, it will call the trampoline + /// for the function signature. + /// 2. If the function is defined in the host (in a native way), it will + /// call the trampoline. + /// + /// # Examples + /// + /// ``` + /// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value}; + /// # use wasmer::FunctionEnv; + /// # let mut store = Store::default(); + /// # let env = FunctionEnv::new(&mut store, ()); + /// # let wasm_bytes = wat2wasm(r#" + /// # (module + /// # (func (export "sum") (param $x i32) (param $y i32) (result i32) + /// # local.get $x + /// # local.get $y + /// # i32.add + /// # )) + /// # "#.as_bytes()).unwrap(); + /// # let module = Module::new(&store, wasm_bytes).unwrap(); + /// # let import_object = imports! {}; + /// # let instance = Instance::new(&mut store, &module, &import_object).unwrap(); + /// # + /// let sum = instance.exports.get_function("sum").unwrap(); + /// + /// assert_eq!(sum.call(&mut store, &[Value::I32(1), Value::I32(2)]).unwrap().to_vec(), vec![Value::I32(3)]); + /// ``` + pub fn call( + &self, + store: &mut impl AsStoreMut, + params: &[Value], + ) -> Result, RuntimeError> { + match_rt!(on self => f { + f.call(store, params) + }) + } + + #[doc(hidden)] + #[allow(missing_docs)] + pub fn call_raw( + &self, + store: &mut impl AsStoreMut, + params: Vec, + ) -> Result, RuntimeError> { + match_rt!(on self => f { + f.call_raw(store, params) + }) + } + + pub(crate) fn vm_funcref(&self, store: &impl AsStoreRef) -> VMFuncRef { + match self { + #[cfg(feature = "sys")] + Self::Sys(f) => VMFuncRef::Sys(f.vm_funcref(store)), + #[cfg(feature = "wamr")] + Self::Wamr(f) => VMFuncRef::Wamr(f.vm_funcref(store)), + #[cfg(feature = "wasmi")] + Self::Wasmi(f) => VMFuncRef::Wasmi(f.vm_funcref(store)), + #[cfg(feature = "v8")] + Self::V8(f) => VMFuncRef::V8(f.vm_funcref(store)), + #[cfg(feature = "js")] + Self::Js(f) => VMFuncRef::Js(f.vm_funcref(store)), + #[cfg(feature = "jsc")] + Self::Jsc(f) => VMFuncRef::Jsc(f.vm_funcref(store)), + } + } + + pub(crate) unsafe fn from_vm_funcref(store: &mut impl AsStoreMut, funcref: VMFuncRef) -> Self { + match &store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(s) => Self::Sys( + crate::rt::sys::entities::function::Function::from_vm_funcref( + store, + funcref.into_sys(), + ), + ), + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(s) => Self::Wamr( + crate::rt::wamr::entities::function::Function::from_vm_funcref( + store, + funcref.into_wamr(), + ), + ), + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(s) => Self::Wasmi( + crate::rt::wasmi::entities::function::Function::from_vm_funcref( + store, + funcref.into_wasmi(), + ), + ), + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(s) => Self::V8( + crate::rt::v8::entities::function::Function::from_vm_funcref( + store, + funcref.into_v8(), + ), + ), + #[cfg(feature = "js")] + crate::RuntimeStore::Js(s) => Self::Js( + crate::rt::js::entities::function::Function::from_vm_funcref( + store, + funcref.into_js(), + ), + ), + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(s) => Self::Jsc( + crate::rt::jsc::entities::function::Function::from_vm_funcref( + store, + funcref.into_jsc(), + ), + ), + } + } + + /// Transform this WebAssembly function into a typed function. + /// See [`TypedFunction`] to learn more. + /// + /// # Examples + /// + /// ``` + /// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, TypedFunction, Value}; + /// # use wasmer::FunctionEnv; + /// # let mut store = Store::default(); + /// # let env = FunctionEnv::new(&mut store, ()); + /// # let wasm_bytes = wat2wasm(r#" + /// # (module + /// # (func (export "sum") (param $x i32) (param $y i32) (result i32) + /// # local.get $x + /// # local.get $y + /// # i32.add + /// # )) + /// # "#.as_bytes()).unwrap(); + /// # let module = Module::new(&store, wasm_bytes).unwrap(); + /// # let import_object = imports! {}; + /// # let instance = Instance::new(&mut store, &module, &import_object).unwrap(); + /// # + /// let sum = instance.exports.get_function("sum").unwrap(); + /// let sum_typed: TypedFunction<(i32, i32), i32> = sum.typed(&mut store).unwrap(); + /// + /// assert_eq!(sum_typed.call(&mut store, 1, 2).unwrap(), 3); + /// ``` + /// + /// # Errors + /// + /// If the `Args` generic parameter does not match the exported function + /// an error will be raised: + /// + /// ```should_panic + /// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, TypedFunction, Value}; + /// # use wasmer::FunctionEnv; + /// # let mut store = Store::default(); + /// # let env = FunctionEnv::new(&mut store, ()); + /// # let wasm_bytes = wat2wasm(r#" + /// # (module + /// # (func (export "sum") (param $x i32) (param $y i32) (result i32) + /// # local.get $x + /// # local.get $y + /// # i32.add + /// # )) + /// # "#.as_bytes()).unwrap(); + /// # let module = Module::new(&store, wasm_bytes).unwrap(); + /// # let import_object = imports! {}; + /// # let instance = Instance::new(&mut store, &module, &import_object).unwrap(); + /// # + /// let sum = instance.exports.get_function("sum").unwrap(); + /// + /// // This results in an error: `RuntimeError` + /// let sum_typed : TypedFunction<(i64, i64), i32> = sum.typed(&mut store).unwrap(); + /// ``` + /// + /// If the `Rets` generic parameter does not match the exported function + /// an error will be raised: + /// + /// ```should_panic + /// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, TypedFunction, Value}; + /// # use wasmer::FunctionEnv; + /// # let mut store = Store::default(); + /// # let env = FunctionEnv::new(&mut store, ()); + /// # let wasm_bytes = wat2wasm(r#" + /// # (module + /// # (func (export "sum") (param $x i32) (param $y i32) (result i32) + /// # local.get $x + /// # local.get $y + /// # i32.add + /// # )) + /// # "#.as_bytes()).unwrap(); + /// # let module = Module::new(&store, wasm_bytes).unwrap(); + /// # let import_object = imports! {}; + /// # let instance = Instance::new(&mut store, &module, &import_object).unwrap(); + /// # + /// let sum = instance.exports.get_function("sum").unwrap(); + /// + /// // This results in an error: `RuntimeError` + /// let sum_typed: TypedFunction<(i32, i32), i64> = sum.typed(&mut store).unwrap(); + /// ``` + pub fn typed( + &self, + store: &impl AsStoreRef, + ) -> Result, RuntimeError> + where + Args: WasmTypeList, + Rets: WasmTypeList, + { + let ty = self.ty(store); + + // type check + { + let expected = ty.params(); + let given = Args::wasm_types(); + + if expected != given { + return Err(RuntimeError::new(format!( + "given types (`{:?}`) for the function arguments don't match the actual types (`{:?}`)", + given, + expected, + ))); + } + } + + { + let expected = ty.results(); + let given = Rets::wasm_types(); + + if expected != given { + // todo: error result types don't match + return Err(RuntimeError::new(format!( + "given types (`{:?}`) for the function results don't match the actual types (`{:?}`)", + given, + expected, + ))); + } + } + + Ok(TypedFunction::new(store, super::Function(self.clone()))) + } + + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternFunction) -> Self { + match &store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(_) => Self::Sys( + crate::rt::sys::entities::function::Function::from_vm_extern(store, vm_extern), + ), + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(_) => Self::Wamr( + crate::rt::wamr::entities::function::Function::from_vm_extern(store, vm_extern), + ), + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(_) => Self::Wasmi( + crate::rt::wasmi::entities::function::Function::from_vm_extern(store, vm_extern), + ), + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(_) => Self::V8( + crate::rt::v8::entities::function::Function::from_vm_extern(store, vm_extern), + ), + #[cfg(feature = "js")] + crate::RuntimeStore::Js(_) => Self::Js( + crate::rt::js::entities::function::Function::from_vm_extern(store, vm_extern), + ), + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(_) => Self::Jsc( + crate::rt::jsc::entities::function::Function::from_vm_extern(store, vm_extern), + ), + } + } + + /// Checks whether this `Function` can be used with the given store. + pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + match_rt!(on self => f { + f.is_from_store(store) + }) + } + + pub(crate) fn to_vm_extern(&self) -> VMExtern { + match_rt!(on self => f { + f.to_vm_extern() + }) + } +} + +impl<'a> Exportable<'a> for RuntimeFunction { + fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { + match _extern { + Extern::Function(func) => Ok(&func.0), + _ => Err(ExportError::IncompatibleType), + } + } +} diff --git a/lib/api/src/externals/function.rs b/lib/api/src/entities/function/mod.rs similarity index 75% rename from lib/api/src/externals/function.rs rename to lib/api/src/entities/function/mod.rs index 4cfb6c1224b..34e125d0845 100644 --- a/lib/api/src/externals/function.rs +++ b/lib/api/src/entities/function/mod.rs @@ -1,72 +1,23 @@ -#[cfg(feature = "wasm-c-api")] -use crate::c_api::externals::function as function_impl; -#[cfg(feature = "js")] -use crate::js::externals::function as function_impl; -#[cfg(feature = "jsc")] -use crate::jsc::externals::function as function_impl; -#[cfg(feature = "sys")] -use crate::sys::externals::function as function_impl; +//! Defines the [`Function`] and [`HostFunction`] types, the [`FunctionLike`] trait for implementors and useful +//! traits and data types to interact with them. -use crate::exports::{ExportError, Exportable}; -use crate::store::{AsStoreMut, AsStoreRef}; -use crate::vm::{VMExtern, VMExternFunction, VMFuncRef, VMFunctionCallback, VMTrampoline}; -use crate::{ - Extern, FunctionEnv, FunctionEnvMut, FunctionType, RuntimeError, TypedFunction, Value, -}; -use wasmer_types::RawValue; - -use crate::native_type::WasmTypeList; +pub(crate) mod inner; +pub use inner::*; -/// The `HostFunction` trait represents the set of functions that -/// can be used as host function. To uphold this statement, it is -/// necessary for a function to be transformed into a -/// `VMFunctionCallback`. -pub trait HostFunction -where - Args: WasmTypeList, - Rets: WasmTypeList, - Kind: HostFunctionKind, -{ - /// Get the pointer to the function body. - fn function_callback(&self) -> VMFunctionCallback; +pub(crate) mod host; +pub use host::*; - /// Get the pointer to the function call trampoline. - fn call_trampoline_address() -> VMTrampoline { - // This is not implemented in JS - unimplemented!(); - } -} +pub(crate) mod env; +pub use env::*; -/// Empty trait to specify the kind of `HostFunction`: With or -/// without an environment. -/// -/// This trait is never aimed to be used by a user. It is used by -/// the trait system to automatically generate the appropriate -/// host functions. -#[doc(hidden)] -pub trait HostFunctionKind: private::HostFunctionKindSealed {} +use wasmer_types::{FunctionType, RawValue}; -/// An empty struct to help Rust typing to determine -/// when a `HostFunction` does have an environment. -pub struct WithEnv; - -impl HostFunctionKind for WithEnv {} - -/// An empty struct to help Rust typing to determine -/// when a `HostFunction` does not have an environment. -pub struct WithoutEnv; - -impl HostFunctionKind for WithoutEnv {} - -mod private { - //! Sealing the HostFunctionKind because it shouldn't be implemented - //! by any type outside. - //! See: - //! - pub trait HostFunctionKindSealed {} - impl HostFunctionKindSealed for super::WithEnv {} - impl HostFunctionKindSealed for super::WithoutEnv {} -} +use crate::{ + error::RuntimeError, + vm::{VMExtern, VMExternFunction, VMFuncRef}, + AsStoreMut, AsStoreRef, ExportError, Exportable, Extern, StoreMut, StoreRef, TypedFunction, + Value, WasmTypeList, +}; /// A WebAssembly `function` instance. /// @@ -85,9 +36,9 @@ mod private { /// with native functions. Attempting to create a native `Function` with one will /// result in a panic. /// [Closures as host functions tracking issue](https://github.com/wasmerio/wasmer/issues/1840) -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))] -pub struct Function(pub(crate) function_impl::Function); +pub struct Function(pub(crate) RuntimeFunction); impl Function { /// Creates a new host `Function` (dynamic) with the provided signature. @@ -99,11 +50,7 @@ impl Function { FT: Into, F: Fn(&[Value]) -> Result, RuntimeError> + 'static + Send + Sync, { - let env = FunctionEnv::new(&mut store.as_store_mut(), ()); - let wrapped_func = move |_env: FunctionEnvMut<()>, - args: &[Value]| - -> Result, RuntimeError> { func(args) }; - Self::new_with_env(store, &env, ty, wrapped_func) + Self(RuntimeFunction::new(store, ty, func)) } /// Creates a new host `Function` (dynamic) with the provided signature. @@ -156,7 +103,7 @@ impl Function { + Send + Sync, { - Self(function_impl::Function::new_with_env(store, env, ty, func)) + Self(RuntimeFunction::new_with_env(store, env, ty, func)) } /// Creates a new host `Function` from a native function. @@ -166,7 +113,7 @@ impl Function { Args: WasmTypeList, Rets: WasmTypeList, { - Self(function_impl::Function::new_typed(store, func)) + Self(RuntimeFunction::new_typed(store, func)) } /// Creates a new host `Function` with an environment from a typed function. @@ -197,9 +144,7 @@ impl Function { Args: WasmTypeList, Rets: WasmTypeList, { - Self(function_impl::Function::new_typed_with_env( - store, env, func, - )) + Self(RuntimeFunction::new_typed_with_env(store, env, func)) } /// Returns the [`FunctionType`] of the `Function`. @@ -320,7 +265,7 @@ impl Function { } pub(crate) unsafe fn from_vm_funcref(store: &mut impl AsStoreMut, funcref: VMFuncRef) -> Self { - Self(function_impl::Function::from_vm_funcref(store, funcref)) + Self(RuntimeFunction::from_vm_funcref(store, funcref)) } /// Transform this WebAssembly function into a typed function. @@ -412,41 +357,11 @@ impl Function { Args: WasmTypeList, Rets: WasmTypeList, { - let ty = self.ty(store); - - // type check - { - let expected = ty.params(); - let given = Args::wasm_types(); - - if expected != given { - return Err(RuntimeError::new(format!( - "given types (`{:?}`) for the function arguments don't match the actual types (`{:?}`)", - given, - expected, - ))); - } - } - - { - let expected = ty.results(); - let given = Rets::wasm_types(); - - if expected != given { - // todo: error result types don't match - return Err(RuntimeError::new(format!( - "given types (`{:?}`) for the function results don't match the actual types (`{:?}`)", - given, - expected, - ))); - } - } - - Ok(TypedFunction::new(store, self.clone())) + self.0.typed(store) } pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternFunction) -> Self { - Self(function_impl::Function::from_vm_extern(store, vm_extern)) + Self(RuntimeFunction::from_vm_extern(store, vm_extern)) } /// Checks whether this `Function` can be used with the given store. @@ -459,8 +374,6 @@ impl Function { } } -impl std::cmp::Eq for Function {} - impl<'a> Exportable<'a> for Function { fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { match _extern { diff --git a/lib/api/src/entities/global/inner.rs b/lib/api/src/entities/global/inner.rs new file mode 100644 index 00000000000..caca5857725 --- /dev/null +++ b/lib/api/src/entities/global/inner.rs @@ -0,0 +1,219 @@ +use crate::{ + error::RuntimeError, + macros::rt::{gen_rt_ty, match_rt}, + store::{AsStoreMut, AsStoreRef, StoreMut, StoreRef}, + value::Value, + vm::{VMExtern, VMExternGlobal}, + ExportError, Exportable, Extern, +}; +use wasmer_types::{GlobalType, Mutability}; + +/// A WebAssembly `global` instance. +/// +/// A global instance is the runtime representation of a global variable. +/// It consists of an individual value and a flag indicating whether it is mutable. +/// +/// Spec: +gen_rt_ty!(Global + @cfg feature = "artifact-size" => derive(loupe::MemoryUsage) + @derives Debug, Clone, PartialEq, Eq, derive_more::From +); + +impl RuntimeGlobal { + /// Create a new global with the initial [`Value`]. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Global, Mutability, Store, Value}; + /// # let mut store = Store::default(); + /// # + /// let g = Global::new(&mut store, Value::I32(1)); + /// + /// assert_eq!(g.get(&mut store), Value::I32(1)); + /// assert_eq!(g.ty(&mut store).mutability, Mutability::Const); + /// ``` + pub fn new(store: &mut impl AsStoreMut, val: Value) -> Self { + Self::from_value(store, val, Mutability::Const).unwrap() + } + + /// Create a mutable global with the initial [`Value`]. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Global, Mutability, Store, Value}; + /// # let mut store = Store::default(); + /// # + /// let g = Global::new_mut(&mut store, Value::I32(1)); + /// + /// assert_eq!(g.get(&mut store), Value::I32(1)); + /// assert_eq!(g.ty(&mut store).mutability, Mutability::Var); + /// ``` + pub fn new_mut(store: &mut impl AsStoreMut, val: Value) -> Self { + Self::from_value(store, val, Mutability::Var).unwrap() + } + + /// Create a global with the initial [`Value`] and the provided [`Mutability`]. + pub(crate) fn from_value( + store: &mut impl AsStoreMut, + val: Value, + mutability: Mutability, + ) -> Result { + match &store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(_) => Ok(Self::Sys( + crate::rt::sys::global::Global::from_value(store, val, mutability)?, + )), + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(_) => Ok(Self::Wamr( + crate::rt::wamr::global::Global::from_value(store, val, mutability)?, + )), + + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(_) => Ok(Self::Wasmi( + crate::rt::wasmi::global::Global::from_value(store, val, mutability)?, + )), + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(_) => Ok(Self::V8(crate::rt::v8::global::Global::from_value( + store, val, mutability, + )?)), + #[cfg(feature = "js")] + crate::RuntimeStore::Js(_) => Ok(Self::Js(crate::rt::js::global::Global::from_value( + store, val, mutability, + )?)), + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(_) => Ok(Self::Jsc( + crate::rt::jsc::global::Global::from_value(store, val, mutability)?, + )), + } + } + + /// Returns the [`GlobalType`] of the global. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Global, Mutability, Store, Type, Value, GlobalType}; + /// # let mut store = Store::default(); + /// # + /// let c = Global::new(&mut store, Value::I32(1)); + /// let v = Global::new_mut(&mut store, Value::I64(1)); + /// + /// assert_eq!(c.ty(&mut store), GlobalType::new(Type::I32, Mutability::Const)); + /// assert_eq!(v.ty(&mut store), GlobalType::new(Type::I64, Mutability::Var)); + /// ``` + pub fn ty(&self, store: &impl AsStoreRef) -> GlobalType { + match_rt!(on self => g { + g.ty(store) + }) + } + + /// Retrieves the current value [`Value`] that the global has. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Global, Store, Value}; + /// # let mut store = Store::default(); + /// # + /// let g = Global::new(&mut store, Value::I32(1)); + /// + /// assert_eq!(g.get(&mut store), Value::I32(1)); + /// ``` + pub fn get(&self, store: &mut impl AsStoreMut) -> Value { + match_rt!(on self => g { + g.get(store) + }) + } + + /// Sets a custom [`Value`] to the runtime global. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Global, Store, Value}; + /// # let mut store = Store::default(); + /// # + /// let g = Global::new_mut(&mut store, Value::I32(1)); + /// + /// assert_eq!(g.get(&mut store), Value::I32(1)); + /// + /// g.set(&mut store, Value::I32(2)); + /// + /// assert_eq!(g.get(&mut store), Value::I32(2)); + /// ``` + /// + /// # Errors + /// + /// Trying to mutate a immutable global will raise an error: + /// + /// ```should_panic + /// # use wasmer::{Global, Store, Value}; + /// # let mut store = Store::default(); + /// # + /// let g = Global::new(&mut store, Value::I32(1)); + /// + /// g.set(&mut store, Value::I32(2)).unwrap(); + /// ``` + /// + /// Trying to set a value of a incompatible type will raise an error: + /// + /// ```should_panic + /// # use wasmer::{Global, Store, Value}; + /// # let mut store = Store::default(); + /// # + /// let g = Global::new(&mut store, Value::I32(1)); + /// + /// // This results in an error: `RuntimeError`. + /// g.set(&mut store, Value::I64(2)).unwrap(); + /// ``` + pub fn set(&self, store: &mut impl AsStoreMut, val: Value) -> Result<(), RuntimeError> { + match_rt!(on self => s { + s.set(store, val) + }) + } + + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternGlobal) -> Self { + match &store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(_) => Self::Sys( + crate::rt::sys::global::Global::from_vm_extern(store, vm_extern), + ), + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(_) => Self::Wamr( + crate::rt::wamr::global::Global::from_vm_extern(store, vm_extern), + ), + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(_) => Self::Wasmi( + crate::rt::wasmi::global::Global::from_vm_extern(store, vm_extern), + ), + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(_) => Self::V8(crate::rt::v8::global::Global::from_vm_extern( + store, vm_extern, + )), + #[cfg(feature = "js")] + crate::RuntimeStore::Js(_) => Self::Js(crate::rt::js::global::Global::from_vm_extern( + store, vm_extern, + )), + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(_) => Self::Jsc( + crate::rt::jsc::global::Global::from_vm_extern(store, vm_extern), + ), + } + } + + /// Checks whether this global can be used with the given context. + pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + match_rt!(on self => s { + s.is_from_store(store) + }) + } + + /// Create a [`VMExtern`] from self. + pub(crate) fn to_vm_extern(&self) -> VMExtern { + match_rt!(on self => s { + s.to_vm_extern() + }) + } +} diff --git a/lib/api/src/externals/global.rs b/lib/api/src/entities/global/mod.rs similarity index 74% rename from lib/api/src/externals/global.rs rename to lib/api/src/entities/global/mod.rs index 4cc279ba040..a6ebb37de7d 100644 --- a/lib/api/src/externals/global.rs +++ b/lib/api/src/entities/global/mod.rs @@ -1,21 +1,14 @@ -use crate::exports::{ExportError, Exportable}; -use crate::store::{AsStoreMut, AsStoreRef}; -use crate::value::Value; -use crate::vm::VMExtern; -use crate::vm::VMExternGlobal; -use crate::Extern; -use crate::GlobalType; -use crate::Mutability; -use crate::RuntimeError; +use crate::{ + error::RuntimeError, + store::{AsStoreMut, AsStoreRef, StoreMut, StoreRef}, + value::Value, + vm::{VMExtern, VMExternGlobal}, + ExportError, Exportable, Extern, +}; +use wasmer_types::{GlobalType, Mutability}; -#[cfg(feature = "wasm-c-api")] -use crate::c_api::externals::global as global_impl; -#[cfg(feature = "js")] -use crate::js::externals::global as global_impl; -#[cfg(feature = "jsc")] -use crate::jsc::externals::global as global_impl; -#[cfg(feature = "sys")] -use crate::sys::externals::global as global_impl; +pub(crate) mod inner; +pub(crate) use inner::*; /// A WebAssembly `global` instance. /// @@ -23,12 +16,12 @@ use crate::sys::externals::global as global_impl; /// It consists of an individual value and a flag indicating whether it is mutable. /// /// Spec: -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, derive_more::From)] #[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))] -pub struct Global(pub(crate) global_impl::Global); +pub struct Global(pub(crate) RuntimeGlobal); impl Global { - /// Create a new `Global` with the initial value [`Value`]. + /// Create a new global with the initial [`Value`]. /// /// # Example /// @@ -42,10 +35,10 @@ impl Global { /// assert_eq!(g.ty(&mut store).mutability, Mutability::Const); /// ``` pub fn new(store: &mut impl AsStoreMut, val: Value) -> Self { - Self::from_value(store, val, Mutability::Const).unwrap() + Self(RuntimeGlobal::new(store, val)) } - /// Create a mutable `Global` with the initial value [`Value`]. + /// Create a mutable global with the initial [`Value`]. /// /// # Example /// @@ -59,21 +52,19 @@ impl Global { /// assert_eq!(g.ty(&mut store).mutability, Mutability::Var); /// ``` pub fn new_mut(store: &mut impl AsStoreMut, val: Value) -> Self { - Self::from_value(store, val, Mutability::Var).unwrap() + Self(RuntimeGlobal::new_mut(store, val)) } - /// Create a `Global` with the initial value [`Value`] and the provided [`Mutability`]. + /// Create a global with the initial [`Value`] and the provided [`Mutability`]. fn from_value( store: &mut impl AsStoreMut, val: Value, mutability: Mutability, ) -> Result { - Ok(Self(global_impl::Global::from_value( - store, val, mutability, - )?)) + RuntimeGlobal::from_value(store, val, mutability).map(Self) } - /// Returns the [`GlobalType`] of the `Global`. + /// Returns the [`GlobalType`] of the global. /// /// # Example /// @@ -91,7 +82,7 @@ impl Global { self.0.ty(store) } - /// Retrieves the current value [`Value`] that the Global has. + /// Retrieves the current value [`Value`] that the global has. /// /// # Example /// @@ -107,7 +98,7 @@ impl Global { self.0.get(store) } - /// Sets a custom value [`Value`] to the runtime Global. + /// Sets a custom [`Value`] to the runtime global. /// /// # Example /// @@ -153,21 +144,20 @@ impl Global { } pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternGlobal) -> Self { - Self(global_impl::Global::from_vm_extern(store, vm_extern)) + Self(RuntimeGlobal::from_vm_extern(store, vm_extern)) } - /// Checks whether this `Global` can be used with the given context. + /// Checks whether this global can be used with the given context. pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { self.0.is_from_store(store) } + /// Create a [`VMExtern`] from self. pub(crate) fn to_vm_extern(&self) -> VMExtern { self.0.to_vm_extern() } } -impl std::cmp::Eq for Global {} - impl<'a> Exportable<'a> for Global { fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { match _extern { diff --git a/lib/api/src/imports.rs b/lib/api/src/entities/imports.rs similarity index 98% rename from lib/api/src/imports.rs rename to lib/api/src/entities/imports.rs index 335ec09278b..60d281d8123 100644 --- a/lib/api/src/imports.rs +++ b/lib/api/src/entities/imports.rs @@ -1,7 +1,7 @@ //! The import module contains the implementation data structures and helper functions used to //! manipulate and access a wasm module's imports including memories, tables, globals, and //! functions. -use crate::{Exports, Extern, LinkError, Module}; +use crate::{error::LinkError, Exports, Extern, Module}; use std::collections::HashMap; use std::fmt; use wasmer_types::ImportError; @@ -166,12 +166,13 @@ impl Imports { } } +/// An iterator over module imports. pub struct ImportsIterator<'a> { iter: std::collections::hash_map::Iter<'a, (String, String), Extern>, } impl<'a> ImportsIterator<'a> { - fn new(imports: &'a Imports) -> Self { + pub(crate) fn new(imports: &'a Imports) -> Self { let iter = imports.map.iter(); Self { iter } } diff --git a/lib/api/src/entities/instance.rs b/lib/api/src/entities/instance.rs new file mode 100644 index 00000000000..98a96a9a48c --- /dev/null +++ b/lib/api/src/entities/instance.rs @@ -0,0 +1,180 @@ +use crate::{ + error::InstantiationError, exports::Exports, imports::Imports, macros::rt::gen_rt_ty, + module::Module, store::AsStoreMut, Extern, +}; + +/// A WebAssembly Instance is a stateful, executable +/// instance of a WebAssembly [`Module`]. +/// +/// Instance objects contain all the exported WebAssembly +/// functions, memories, tables and globals that allow +/// interacting with WebAssembly. +/// +/// Spec: +#[derive(Clone, PartialEq, Eq)] +pub struct Instance { + pub(crate) _inner: RuntimeInstance, + pub(crate) module: Module, + /// The exports for an instance. + pub exports: Exports, +} + +impl Instance { + /// Creates a new `Instance` from a WebAssembly [`Module`] and a + /// set of imports using [`Imports`] or the [`imports!`] macro helper. + /// + /// [`imports!`]: crate::imports! + /// [`Imports!`]: crate::Imports! + /// + /// ``` + /// # use wasmer::{imports, Store, Module, Global, Value, Instance}; + /// # use wasmer::FunctionEnv; + /// # fn main() -> anyhow::Result<()> { + /// let mut store = Store::default(); + /// let env = FunctionEnv::new(&mut store, ()); + /// let module = Module::new(&store, "(module)")?; + /// let imports = imports!{ + /// "host" => { + /// "var" => Global::new(&mut store, Value::I32(2)) + /// } + /// }; + /// let instance = Instance::new(&mut store, &module, &imports)?; + /// # Ok(()) + /// # } + /// ``` + /// + /// ## Errors + /// + /// The function can return [`InstantiationError`]s. + /// + /// Those are, as defined by the spec: + /// * Link errors that happen when plugging the imports into the instance + /// * Runtime errors that happen when running the module `start` function. + #[allow(clippy::result_large_err)] + pub fn new( + store: &mut impl AsStoreMut, + module: &Module, + imports: &Imports, + ) -> Result { + let (_inner, exports) = match &store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(_) => { + let (i, e) = crate::rt::sys::instance::Instance::new(store, module, imports)?; + (crate::RuntimeInstance::Sys(i), e) + } + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(_) => { + let (i, e) = crate::rt::wamr::instance::Instance::new(store, module, imports)?; + + (crate::RuntimeInstance::Wamr(i), e) + } + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(_) => { + let (i, e) = crate::rt::wasmi::instance::Instance::new(store, module, imports)?; + + (crate::RuntimeInstance::Wasmi(i), e) + } + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(_) => { + let (i, e) = crate::rt::v8::instance::Instance::new(store, module, imports)?; + (crate::RuntimeInstance::V8(i), e) + } + #[cfg(feature = "js")] + crate::RuntimeStore::Js(_) => { + let (i, e) = crate::rt::js::instance::Instance::new(store, module, imports)?; + (crate::RuntimeInstance::Js(i), e) + } + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(_) => { + let (i, e) = crate::rt::jsc::instance::Instance::new(store, module, imports)?; + (crate::RuntimeInstance::Jsc(i), e) + } + }; + + Ok(Self { + _inner, + module: module.clone(), + exports, + }) + } + + /// Creates a new `Instance` from a WebAssembly [`Module`] and a + /// vector of imports. + /// + /// ## Errors + /// + /// The function can return [`InstantiationError`]s. + /// + /// Those are, as defined by the spec: + /// * Link errors that happen when plugging the imports into the instance + /// * Runtime errors that happen when running the module `start` function. + #[allow(clippy::result_large_err)] + pub fn new_by_index( + store: &mut impl AsStoreMut, + module: &Module, + externs: &[Extern], + ) -> Result { + let (_inner, exports) = match &store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(_) => { + let (i, e) = + crate::rt::sys::instance::Instance::new_by_index(store, module, externs)?; + (crate::RuntimeInstance::Sys(i), e) + } + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(_) => { + let (i, e) = + crate::rt::wamr::instance::Instance::new_by_index(store, module, externs)?; + + (crate::RuntimeInstance::Wamr(i), e) + } + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(_) => { + let (i, e) = + crate::rt::wasmi::instance::Instance::new_by_index(store, module, externs)?; + + (crate::RuntimeInstance::Wasmi(i), e) + } + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(_) => { + let (i, e) = + crate::rt::v8::instance::Instance::new_by_index(store, module, externs)?; + (crate::RuntimeInstance::V8(i), e) + } + #[cfg(feature = "js")] + crate::RuntimeStore::Js(_) => { + let (i, e) = + crate::rt::js::instance::Instance::new_by_index(store, module, externs)?; + (crate::RuntimeInstance::Js(i), e) + } + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(_) => { + let (i, e) = + crate::rt::jsc::instance::Instance::new_by_index(store, module, externs)?; + (crate::RuntimeInstance::Jsc(i), e) + } + }; + + Ok(Self { + _inner, + module: module.clone(), + exports, + }) + } + + /// Gets the [`Module`] associated with this instance. + pub fn module(&self) -> &Module { + &self.module + } +} + +impl std::fmt::Debug for Instance { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("Instance") + .field("exports", &self.exports) + .finish() + } +} + +/// An enumeration of all the possible instances kind supported by the runtimes. +gen_rt_ty!(Instance @derives Clone, PartialEq, Eq); diff --git a/lib/api/src/entities/memory/buffer/inner.rs b/lib/api/src/entities/memory/buffer/inner.rs new file mode 100644 index 00000000000..b3335954fe8 --- /dev/null +++ b/lib/api/src/entities/memory/buffer/inner.rs @@ -0,0 +1,78 @@ +use std::{marker::PhantomData, mem::MaybeUninit}; + +use crate::{ + macros::rt::{gen_rt_ty, match_rt}, + MemoryAccessError, +}; + +/// Underlying buffer for a memory. +gen_rt_ty!(MemoryBuffer<'a> + @derives Debug, Copy, Clone, derive_more::From; + @path memory +); + +impl<'a> RuntimeMemoryBuffer<'a> { + #[allow(unused)] + pub(crate) fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> { + match_rt!(on self => s { + s.read(offset, buf) + }) + } + + #[allow(unused)] + pub(crate) fn read_uninit<'b>( + &self, + offset: u64, + buf: &'b mut [MaybeUninit], + ) -> Result<&'b mut [u8], MemoryAccessError> { + match_rt!(on self => s { + s.read_uninit(offset, buf) + }) + } + + #[allow(unused)] + pub(crate) fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { + match_rt!(on self => s { + s.write(offset, data) + }) + } + + pub(crate) fn len(&self) -> usize { + match self { + #[cfg(feature = "sys")] + Self::Sys(s) => s.len, + + #[cfg(feature = "wamr")] + Self::Wamr(s) => s.len, + + #[cfg(feature = "wasmi")] + Self::Wasmi(s) => s.len, + + #[cfg(feature = "v8")] + Self::V8(s) => s.len, + + #[cfg(feature = "js")] + Self::Js(s) => panic!("js memory buffers do not support the `len` function!"), + + #[cfg(feature = "jsc")] + Self::Jsc(s) => s.len, + } + } + + pub(crate) fn base(&self) -> *mut u8 { + match self { + #[cfg(feature = "sys")] + Self::Sys(s) => s.base, + #[cfg(feature = "wamr")] + Self::Wamr(s) => s.base, + #[cfg(feature = "wasmi")] + Self::Wasmi(s) => s.base, + #[cfg(feature = "v8")] + Self::V8(s) => s.base, + #[cfg(feature = "js")] + Self::Js(s) => panic!("js memory buffers do not support the `base` function!"), + #[cfg(feature = "jsc")] + Self::Jsc(s) => s.base, + } + } +} diff --git a/lib/api/src/entities/memory/buffer/mod.rs b/lib/api/src/entities/memory/buffer/mod.rs new file mode 100644 index 00000000000..b7f452c376e --- /dev/null +++ b/lib/api/src/entities/memory/buffer/mod.rs @@ -0,0 +1,39 @@ +use std::{marker::PhantomData, mem::MaybeUninit}; + +use crate::MemoryAccessError; + +pub(crate) mod inner; +pub(crate) use inner::*; + +/// Underlying buffer for a memory. +#[derive(Debug, Copy, Clone, derive_more::From)] +pub(crate) struct MemoryBuffer<'a>(pub(crate) RuntimeMemoryBuffer<'a>); + +impl<'a> MemoryBuffer<'a> { + #[allow(unused)] + pub(crate) fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> { + self.0.read(offset, buf) + } + + #[allow(unused)] + pub(crate) fn read_uninit<'b>( + &self, + offset: u64, + buf: &'b mut [MaybeUninit], + ) -> Result<&'b mut [u8], MemoryAccessError> { + self.0.read_uninit(offset, buf) + } + + #[allow(unused)] + pub(crate) fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { + self.0.write(offset, data) + } + + pub(crate) fn len(&self) -> usize { + self.0.len() + } + + pub(crate) fn base(&self) -> *mut u8 { + self.0.base() + } +} diff --git a/lib/api/src/entities/memory/inner.rs b/lib/api/src/entities/memory/inner.rs new file mode 100644 index 00000000000..4077603f2b9 --- /dev/null +++ b/lib/api/src/entities/memory/inner.rs @@ -0,0 +1,361 @@ +use super::{shared::SharedMemory, view::*}; +use wasmer_types::{MemoryError, MemoryType, Pages}; + +use crate::{ + macros::rt::{gen_rt_ty, match_rt}, + vm::{VMExtern, VMExternMemory, VMMemory}, + AsStoreMut, AsStoreRef, ExportError, Exportable, Extern, StoreMut, StoreRef, +}; + +gen_rt_ty!(Memory + @cfg feature = "artifact-size" => derive(loupe::MemoryUsage) + @derives Debug, Clone, PartialEq, Eq, derive_more::From +); + +impl RuntimeMemory { + /// Creates a new host [`Memory`] from the provided [`MemoryType`]. + /// + /// This function will construct the `Memory` using the store + /// `BaseTunables`. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; + /// # let mut store = Store::default(); + /// # + /// let m = Memory::new(&mut store, MemoryType::new(1, None, false)).unwrap(); + /// ``` + pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result { + match &store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(s) => Ok(Self::Sys( + crate::rt::sys::entities::memory::Memory::new(store, ty)?, + )), + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(s) => Ok(Self::Wamr( + crate::rt::wamr::entities::memory::Memory::new(store, ty)?, + )), + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(s) => Ok(Self::Wasmi( + crate::rt::wasmi::entities::memory::Memory::new(store, ty)?, + )), + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(s) => Ok(Self::V8( + crate::rt::v8::entities::memory::Memory::new(store, ty)?, + )), + #[cfg(feature = "js")] + crate::RuntimeStore::Js(s) => Ok(Self::Js( + crate::rt::js::entities::memory::Memory::new(store, ty)?, + )), + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(s) => Ok(Self::Jsc( + crate::rt::jsc::entities::memory::Memory::new(store, ty)?, + )), + } + } + + /// Create a memory object from an existing memory and attaches it to the store + pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { + match new_store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(_) => { + Self::Sys(crate::rt::sys::entities::memory::Memory::new_from_existing( + new_store, + memory.into_sys(), + )) + } + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(_) => Self::Wamr( + crate::rt::wamr::entities::memory::Memory::new_from_existing( + new_store, + memory.into_wamr(), + ), + ), + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(_) => Self::Wasmi( + crate::rt::wasmi::entities::memory::Memory::new_from_existing( + new_store, + memory.into_wasmi(), + ), + ), + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(_) => { + Self::V8(crate::rt::v8::entities::memory::Memory::new_from_existing( + new_store, + memory.into_v8(), + )) + } + #[cfg(feature = "js")] + crate::RuntimeStore::Js(_) => { + Self::Js(crate::rt::js::entities::memory::Memory::new_from_existing( + new_store, + memory.into_js(), + )) + } + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(_) => { + Self::Jsc(crate::rt::jsc::entities::memory::Memory::new_from_existing( + new_store, + memory.into_jsc(), + )) + } + } + } + + /// Returns the [`MemoryType`] of the `Memory`. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; + /// # let mut store = Store::default(); + /// # + /// let mt = MemoryType::new(1, None, false); + /// let m = Memory::new(&mut store, mt).unwrap(); + /// + /// assert_eq!(m.ty(&mut store), mt); + /// ``` + pub fn ty(&self, store: &impl AsStoreRef) -> MemoryType { + match_rt!(on self => s { + s.ty(store) + }) + } + + /// Grow memory by the specified amount of WebAssembly [`Pages`] and return + /// the previous memory size. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES}; + /// # let mut store = Store::default(); + /// # + /// let m = Memory::new(&mut store, MemoryType::new(1, Some(3), false)).unwrap(); + /// let p = m.grow(&mut store, 2).unwrap(); + /// + /// assert_eq!(p, Pages(1)); + /// assert_eq!(m.view(&mut store).size(), Pages(3)); + /// ``` + /// + /// # Errors + /// + /// Returns an error if memory can't be grown by the specified amount + /// of pages. + /// + /// ```should_panic + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES}; + /// # use wasmer::FunctionEnv; + /// # let mut store = Store::default(); + /// # let env = FunctionEnv::new(&mut store, ()); + /// # + /// let m = Memory::new(&mut store, MemoryType::new(1, Some(1), false)).unwrap(); + /// + /// // This results in an error: `MemoryError::CouldNotGrow`. + /// let s = m.grow(&mut store, 1).unwrap(); + /// ``` + pub fn grow( + &self, + store: &mut impl AsStoreMut, + delta: IntoPages, + ) -> Result + where + IntoPages: Into, + { + match_rt!(on self => s { + s.grow(store, delta) + }) + } + + /// Grows the memory to at least a minimum size. + /// + /// # Note + /// + /// If the memory is already big enough for the min size this function does nothing. + pub fn grow_at_least( + &self, + store: &mut impl AsStoreMut, + min_size: u64, + ) -> Result<(), MemoryError> { + match_rt!(on self => s { + s.grow_at_least(store, min_size) + }) + } + + /// Resets the memory back to zero length + pub fn reset(&self, store: &mut impl AsStoreMut) -> Result<(), MemoryError> { + match_rt!(on self => s { + s.reset(store) + }) + } + + /// Attempts to duplicate this memory (if its clonable) in a new store + /// (copied memory) + pub fn copy_to_store( + &self, + store: &impl AsStoreRef, + new_store: &mut impl AsStoreMut, + ) -> Result { + if !self.ty(store).shared { + // We should only be able to duplicate in a new store if the memory is shared + return Err(MemoryError::InvalidMemory { + reason: "memory is not a shared memory type".to_string(), + }); + } + + match self { + #[cfg(feature = "sys")] + Self::Sys(s) => s.try_copy(store).map(|new_memory| { + Self::new_from_existing( + new_store, + VMMemory::Sys(crate::rt::sys::vm::VMMemory(new_memory)), + ) + }), + #[cfg(feature = "wamr")] + Self::Wamr(s) => s + .try_copy(store) + .map(|new_memory| Self::new_from_existing(new_store, VMMemory::Wamr(new_memory))), + #[cfg(feature = "wasmi")] + Self::Wasmi(s) => s + .try_copy(store) + .map(|new_memory| Self::new_from_existing(new_store, VMMemory::Wasmi(new_memory))), + + #[cfg(feature = "v8")] + Self::V8(s) => s + .try_copy(store) + .map(|new_memory| Self::new_from_existing(new_store, VMMemory::V8(new_memory))), + #[cfg(feature = "js")] + Self::Js(s) => s + .try_copy(store) + .map(|new_memory| Self::new_from_existing(new_store, VMMemory::Js(new_memory))), + #[cfg(feature = "jsc")] + Self::Jsc(s) => s + .try_copy(store) + .map(|new_memory| Self::new_from_existing(new_store, VMMemory::Jsc(new_memory))), + } + } + + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternMemory) -> Self { + match &store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(s) => Self::Sys( + crate::rt::sys::entities::memory::Memory::from_vm_extern(store, vm_extern), + ), + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(s) => Self::Wamr( + crate::rt::wamr::entities::memory::Memory::from_vm_extern(store, vm_extern), + ), + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(s) => Self::Wasmi( + crate::rt::wasmi::entities::memory::Memory::from_vm_extern(store, vm_extern), + ), + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(s) => Self::V8( + crate::rt::v8::entities::memory::Memory::from_vm_extern(store, vm_extern), + ), + #[cfg(feature = "js")] + crate::RuntimeStore::Js(s) => Self::Js( + crate::rt::js::entities::memory::Memory::from_vm_extern(store, vm_extern), + ), + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(s) => Self::Jsc( + crate::rt::jsc::entities::memory::Memory::from_vm_extern(store, vm_extern), + ), + } + } + + /// Checks whether this `Memory` can be used with the given context. + pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + match_rt!(on self => s { + s.is_from_store(store) + }) + } + + /// Attempt to create a new reference to the underlying memory; this new reference can then be + /// used within a different store (from the same implementer). + /// + /// # Errors + /// + /// Fails if the underlying memory is not clonable. + pub fn try_clone(&self, store: &impl AsStoreRef) -> Result { + match self { + #[cfg(feature = "sys")] + Self::Sys(s) => s.try_clone(store).map(VMMemory::Sys), + #[cfg(feature = "wamr")] + Self::Wamr(s) => s.try_clone(store).map(VMMemory::Wamr), + #[cfg(feature = "wasmi")] + Self::Wasmi(s) => s.try_clone(store).map(VMMemory::Wasmi), + #[cfg(feature = "v8")] + Self::V8(s) => s.try_clone(store).map(VMMemory::V8), + #[cfg(feature = "js")] + Self::Js(s) => s.try_clone(store).map(VMMemory::Js), + #[cfg(feature = "jsc")] + Self::Jsc(s) => s.try_clone(store).map(VMMemory::Jsc), + } + } + + /// Attempts to clone this memory (if its clonable) in a new store + /// (cloned memory will be shared between those that clone it) + pub fn share_in_store( + &self, + store: &impl AsStoreRef, + new_store: &mut impl AsStoreMut, + ) -> Result { + if !self.ty(store).shared { + // We should only be able to duplicate in a new store if the memory is shared + return Err(MemoryError::InvalidMemory { + reason: "memory is not a shared memory type".to_string(), + }); + } + + match self { + #[cfg(feature = "sys")] + Self::Sys(s) => s + .try_clone(store) + .map(|new_memory| Self::new_from_existing(new_store, VMMemory::Sys(new_memory))), + #[cfg(feature = "wamr")] + Self::Wamr(s) => s + .try_clone(store) + .map(|new_memory| Self::new_from_existing(new_store, VMMemory::Wamr(new_memory))), + #[cfg(feature = "wasmi")] + Self::Wasmi(s) => s + .try_clone(store) + .map(|new_memory| Self::new_from_existing(new_store, VMMemory::Wasmi(new_memory))), + #[cfg(feature = "v8")] + Self::V8(s) => s + .try_clone(store) + .map(|new_memory| Self::new_from_existing(new_store, VMMemory::V8(new_memory))), + #[cfg(feature = "js")] + Self::Js(s) => s + .try_clone(store) + .map(|new_memory| Self::new_from_existing(new_store, VMMemory::Js(new_memory))), + #[cfg(feature = "jsc")] + Self::Jsc(s) => s + .try_clone(store) + .map(|new_memory| Self::new_from_existing(new_store, VMMemory::Jsc(new_memory))), + } + } + + /// Get a [`SharedMemory`]. + /// + /// Only returns `Some(_)` if the memory is shared, and if the target + /// backend supports shared memory operations. + /// + /// See [`SharedMemory`] and its methods for more information. + pub fn as_shared(&self, store: &impl AsStoreRef) -> Option { + if !self.ty(store).shared { + return None; + } + + match_rt!(on self => s { + s.as_shared(store) + }) + } + + /// Create a [`VMExtern`] from self. + pub(crate) fn to_vm_extern(&self) -> VMExtern { + match_rt!(on self => s { + s.to_vm_extern() + }) + } +} diff --git a/lib/api/src/entities/memory/location.rs b/lib/api/src/entities/memory/location.rs new file mode 100644 index 00000000000..779115666ad --- /dev/null +++ b/lib/api/src/entities/memory/location.rs @@ -0,0 +1,52 @@ +use wasmer_types::MemoryError; + +use crate::error::AtomicsError; + +/// Location in a WebAssembly memory. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct MemoryLocation { + // NOTE: must be expanded to an enum that also supports 64bit memory in + // the future + // That's why this is private. + pub(crate) address: u32, +} + +impl MemoryLocation { + /// Create a new memory location for a 32bit memory. + pub fn new_32(address: u32) -> Self { + Self { address } + } +} + +impl From for MemoryLocation { + fn from(value: u32) -> Self { + Self::new_32(value) + } +} + +/// See [`SharedMemory`]. +pub(crate) trait SharedMemoryOps { + /// See [`SharedMemory::disable_atomics`]. + fn disable_atomics(&self) -> Result<(), MemoryError> { + Err(MemoryError::AtomicsNotSupported) + } + + /// See [`SharedMemory::wake_all_atomic_waiters`]. + fn wake_all_atomic_waiters(&self) -> Result<(), MemoryError> { + Err(MemoryError::AtomicsNotSupported) + } + + /// See [`SharedMemory::notify`]. + fn notify(&self, _dst: MemoryLocation, _count: u32) -> Result { + Err(AtomicsError::Unimplemented) + } + + /// See [`SharedMemory::wait`]. + fn wait( + &self, + _dst: MemoryLocation, + _timeout: Option, + ) -> Result { + Err(AtomicsError::Unimplemented) + } +} diff --git a/lib/api/src/entities/memory/mod.rs b/lib/api/src/entities/memory/mod.rs new file mode 100644 index 00000000000..201393e797e --- /dev/null +++ b/lib/api/src/entities/memory/mod.rs @@ -0,0 +1,205 @@ +pub use shared::SharedMemory; +use wasmer_types::{MemoryError, MemoryType, Pages}; + +use crate::{ + vm::{VMExtern, VMExternMemory, VMMemory}, + AsStoreMut, AsStoreRef, ExportError, Exportable, Extern, StoreMut, StoreRef, +}; + +pub(crate) mod buffer; +pub(crate) mod inner; +pub(crate) mod location; +pub(crate) mod shared; +pub(crate) mod view; + +pub(crate) use inner::*; +pub use view::*; + +/// A WebAssembly `memory` instance. +/// +/// A memory instance is the runtime representation of a linear memory. +/// It consists of a vector of bytes and an optional maximum size. +/// +/// The length of the vector always is a multiple of the WebAssembly +/// page size, which is defined to be the constant 65536 – abbreviated 64Ki. +/// Like in a memory type, the maximum size in a memory instance is +/// given in units of this page size. +/// +/// A memory created by the host or in WebAssembly code will be accessible and +/// mutable from both host and WebAssembly. +/// +/// Spec: +#[derive(Debug, Clone, PartialEq, Eq, derive_more::From)] +#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))] +pub struct Memory(pub(crate) RuntimeMemory); + +impl Memory { + /// Creates a new host [`Memory`] from the provided [`MemoryType`]. + /// + /// This function will construct the `Memory` using the store + /// `BaseTunables`. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; + /// # let mut store = Store::default(); + /// # + /// let m = Memory::new(&mut store, MemoryType::new(1, None, false)).unwrap(); + /// ``` + pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result { + RuntimeMemory::new(store, ty).map(Self) + } + + /// Create a memory object from an existing memory and attaches it to the store + pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { + Self(RuntimeMemory::new_from_existing(new_store, memory)) + } + + /// Returns the [`MemoryType`] of the `Memory`. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; + /// # let mut store = Store::default(); + /// # + /// let mt = MemoryType::new(1, None, false); + /// let m = Memory::new(&mut store, mt).unwrap(); + /// + /// assert_eq!(m.ty(&mut store), mt); + /// ``` + pub fn ty(&self, store: &impl AsStoreRef) -> MemoryType { + self.0.ty(store) + } + + /// Creates a view into the memory that then allows for + /// read and write + pub fn view<'a>(&self, store: &'a (impl AsStoreRef + ?Sized)) -> MemoryView<'a> { + MemoryView::new(self, store) + } + + /// Grow memory by the specified amount of WebAssembly [`Pages`] and return + /// the previous memory size. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES}; + /// # let mut store = Store::default(); + /// # + /// let m = Memory::new(&mut store, MemoryType::new(1, Some(3), false)).unwrap(); + /// let p = m.grow(&mut store, 2).unwrap(); + /// + /// assert_eq!(p, Pages(1)); + /// assert_eq!(m.view(&mut store).size(), Pages(3)); + /// ``` + /// + /// # Errors + /// + /// Returns an error if memory can't be grown by the specified amount + /// of pages. + /// + /// ```should_panic + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES}; + /// # use wasmer::FunctionEnv; + /// # let mut store = Store::default(); + /// # let env = FunctionEnv::new(&mut store, ()); + /// # + /// let m = Memory::new(&mut store, MemoryType::new(1, Some(1), false)).unwrap(); + /// + /// // This results in an error: `MemoryError::CouldNotGrow`. + /// let s = m.grow(&mut store, 1).unwrap(); + /// ``` + pub fn grow( + &self, + store: &mut impl AsStoreMut, + delta: IntoPages, + ) -> Result + where + IntoPages: Into, + { + self.0.grow(store, delta) + } + + /// Grows the memory to at least a minimum size. + /// + /// # Note + /// + /// If the memory is already big enough for the min size this function does nothing. + pub fn grow_at_least( + &self, + store: &mut impl AsStoreMut, + min_size: u64, + ) -> Result<(), MemoryError> { + self.0.grow_at_least(store, min_size) + } + + /// Resets the memory back to zero length + pub fn reset(&self, store: &mut impl AsStoreMut) -> Result<(), MemoryError> { + self.0.reset(store) + } + + /// Attempts to duplicate this memory (if its clonable) in a new store + /// (copied memory) + pub fn copy_to_store( + &self, + store: &impl AsStoreRef, + new_store: &mut impl AsStoreMut, + ) -> Result { + self.0.copy_to_store(store, new_store).map(Self) + } + + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternMemory) -> Self { + Self(RuntimeMemory::from_vm_extern(store, vm_extern)) + } + + /// Checks whether this `Memory` can be used with the given context. + pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + self.0.is_from_store(store) + } + + /// Attempt to create a new reference to the underlying memory; this new reference can then be + /// used within a different store (from the same implementer). + /// + /// # Errors + /// + /// Fails if the underlying memory is not clonable. + pub fn try_clone(&self, store: &impl AsStoreRef) -> Result { + self.0.try_clone(store) + } + + /// Attempts to clone this memory (if its clonable) in a new store + /// (cloned memory will be shared between those that clone it) + pub fn share_in_store( + &self, + store: &impl AsStoreRef, + new_store: &mut impl AsStoreMut, + ) -> Result { + self.0.share_in_store(store, new_store).map(Self) + } + + /// Get a [`SharedMemory`]. + /// + /// Only returns `Some(_)` if the memory is shared, and if the target + /// backend supports shared memory operations. + /// + /// See [`SharedMemory`] and its methods for more information. + pub fn as_shared(&self, store: &impl AsStoreRef) -> Option { + self.0.as_shared(store) + } + + /// Create a [`VMExtern`] from self. + pub(crate) fn to_vm_extern(&self) -> VMExtern { + self.0.to_vm_extern() + } +} + +impl<'a> Exportable<'a> for Memory { + fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { + match _extern { + Extern::Memory(memory) => Ok(memory), + _ => Err(ExportError::IncompatibleType), + } + } +} diff --git a/lib/api/src/entities/memory/shared.rs b/lib/api/src/entities/memory/shared.rs new file mode 100644 index 00000000000..45a31eb8f4a --- /dev/null +++ b/lib/api/src/entities/memory/shared.rs @@ -0,0 +1,78 @@ +use wasmer_types::MemoryError; + +use crate::{ + error::AtomicsError, + location::{MemoryLocation, SharedMemoryOps}, + Memory, +}; + +/// A handle that exposes operations only relevant for shared memories. +/// +/// Enables interaction independent from the [`crate::Store`], and thus allows calling +/// some methods an instane is running. +/// +/// **NOTE**: Not all methods are supported by all backends. +#[derive(Clone)] +pub struct SharedMemory { + memory: Memory, + ops: std::sync::Arc, +} + +impl std::fmt::Debug for SharedMemory { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SharedMemory").finish() + } +} + +impl SharedMemory { + /// Get the underlying memory. + pub fn memory(&self) -> &Memory { + &self.memory + } + + /// Create a new handle from ops. + #[allow(unused)] + pub(crate) fn new(memory: Memory, ops: impl SharedMemoryOps + Send + Sync + 'static) -> Self { + Self { + memory, + ops: std::sync::Arc::new(ops), + } + } + + /// Notify up to `count` waiters waiting for the memory location. + pub fn notify(&self, location: MemoryLocation, count: u32) -> Result { + self.ops.notify(location, count) + } + + /// Wait for the memory location to be notified. + pub fn wait( + &self, + location: MemoryLocation, + timeout: Option, + ) -> Result { + self.ops.wait(location, timeout) + } + + /// Disable atomics for this memory. + /// + /// All subsequent atomic wait calls will produce a trap. + /// + /// This can be used or forced shutdown of instances that continuously try + /// to wait on atomics. + /// + /// NOTE: this operation might not be supported by all memory implementations. + /// In that case, this function will return an error. + pub fn disable_atomics(&self) -> Result<(), MemoryError> { + self.ops.disable_atomics() + } + + /// Wake up all atomic waiters. + /// + /// This can be used to force-resume waiting execution. + /// + /// NOTE: this operation might not be supported by all memory implementations. + /// In that case, this function will return an error. + pub fn wake_all_atomic_waiters(&self) -> Result<(), MemoryError> { + self.ops.wake_all_atomic_waiters() + } +} diff --git a/lib/api/src/entities/memory/view/inner.rs b/lib/api/src/entities/memory/view/inner.rs new file mode 100644 index 00000000000..37f0de93a66 --- /dev/null +++ b/lib/api/src/entities/memory/view/inner.rs @@ -0,0 +1,253 @@ +use std::{mem::MaybeUninit, ops::Range}; +use wasmer_types::Pages; + +use crate::{ + buffer::{MemoryBuffer, RuntimeMemoryBuffer}, + macros::rt::{gen_rt_ty, match_rt}, + AsStoreRef, Memory, MemoryAccessError, +}; + +/// A WebAssembly `memory` view. +/// +/// A memory view is used to read and write to the linear memory. +/// +/// After a memory is grown a view must not be used anymore. Views are +/// created using the Memory.view() method. +gen_rt_ty!(MemoryView<'a> @derives Debug, derive_more::From ; @path memory::view); + +impl<'a> RuntimeMemoryView<'a> { + pub(crate) fn new(memory: &Memory, store: &'a (impl AsStoreRef + ?Sized)) -> Self { + match &store.as_store_ref().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(s) => { + return Self::Sys(crate::rt::sys::entities::memory::view::MemoryView::new( + memory.as_sys(), + store, + )) + } + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(s) => { + return Self::Wamr(crate::rt::wamr::entities::memory::view::MemoryView::new( + memory.as_wamr(), + store, + )) + } + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(s) => { + return Self::Wasmi(crate::rt::wasmi::entities::memory::view::MemoryView::new( + memory.as_wasmi(), + store, + )) + } + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(s) => { + return Self::V8(crate::rt::v8::entities::memory::view::MemoryView::new( + memory.as_v8(), + store, + )) + } + #[cfg(feature = "js")] + crate::RuntimeStore::Js(s) => { + return Self::Js(crate::rt::js::entities::memory::view::MemoryView::new( + memory.as_js(), + store, + )) + } + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(s) => { + return Self::Jsc(crate::rt::jsc::entities::memory::view::MemoryView::new( + memory.as_jsc(), + store, + )) + } + } + } + + /// Returns the pointer to the raw bytes of the `Memory`. + // + // This used by wasmer-c-api, but should be treated + // as deprecated and not used in future code. + #[doc(hidden)] + pub fn data_ptr(&self) -> *mut u8 { + match_rt!(on self => s { + s.data_ptr() + }) + } + + /// Returns the size (in bytes) of the `Memory`. + pub fn data_size(&self) -> u64 { + match_rt!(on self => s { + s.data_size() + }) + } + + /// Retrieve a slice of the memory contents. + /// + /// # Safety + /// + /// Until the returned slice is dropped, it is undefined behaviour to + /// modify the memory contents in any way including by calling a wasm + /// function that writes to the memory or by resizing the memory. + #[doc(hidden)] + pub unsafe fn data_unchecked(&self) -> &[u8] { + match_rt!(on self => s { + s.data_unchecked() + }) + } + + /// Retrieve a mutable slice of the memory contents. + /// + /// # Safety + /// + /// This method provides interior mutability without an UnsafeCell. Until + /// the returned value is dropped, it is undefined behaviour to read or + /// write to the pointed-to memory in any way except through this slice, + /// including by calling a wasm function that reads the memory contents or + /// by resizing this Memory. + #[allow(clippy::mut_from_ref)] + #[doc(hidden)] + pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] { + match_rt!(on self => s { + s.data_unchecked_mut() + }) + } + + /// Returns the size (in [`Pages`]) of the `Memory`. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; + /// # let mut store = Store::default(); + /// # + /// let m = Memory::new(&mut store, MemoryType::new(1, None, false)).unwrap(); + /// + /// assert_eq!(m.view(&mut store).size(), Pages(1)); + /// ``` + pub fn size(&self) -> Pages { + match_rt!(on self => s { + s.size() + }) + } + + #[inline] + pub(crate) fn buffer(&'a self) -> MemoryBuffer<'a> { + match self { + #[cfg(feature = "sys")] + Self::Sys(s) => MemoryBuffer(RuntimeMemoryBuffer::Sys(s.buffer())), + #[cfg(feature = "wamr")] + Self::Wamr(s) => MemoryBuffer(RuntimeMemoryBuffer::Wamr(s.buffer())), + #[cfg(feature = "wasmi")] + Self::Wasmi(s) => MemoryBuffer(RuntimeMemoryBuffer::Wasmi(s.buffer())), + #[cfg(feature = "v8")] + Self::V8(s) => MemoryBuffer(RuntimeMemoryBuffer::V8(s.buffer())), + #[cfg(feature = "js")] + Self::Js(s) => MemoryBuffer(RuntimeMemoryBuffer::Js(s.buffer())), + #[cfg(feature = "jsc")] + Self::Jsc(s) => MemoryBuffer(RuntimeMemoryBuffer::Jsc(s.buffer())), + } + } + + /// Safely reads bytes from the memory at the given offset. + /// + /// The full buffer will be filled, otherwise a `MemoryAccessError` is returned + /// to indicate an out-of-bounds access. + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent writes. + pub fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> { + match_rt!(on self => s { + s.read(offset, buf) + }) + } + + /// Safely reads a single byte from memory at the given offset + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent writes. + pub fn read_u8(&self, offset: u64) -> Result { + match_rt!(on self => s { + s.read_u8(offset) + }) + } + + /// Safely reads bytes from the memory at the given offset. + /// + /// This method is similar to `read` but allows reading into an + /// uninitialized buffer. An initialized view of the buffer is returned. + /// + /// The full buffer will be filled, otherwise a `MemoryAccessError` is returned + /// to indicate an out-of-bounds access. + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent writes. + pub fn read_uninit<'b>( + &self, + offset: u64, + buf: &'b mut [MaybeUninit], + ) -> Result<&'b mut [u8], MemoryAccessError> { + match_rt!(on self => s { + s.read_uninit(offset, buf) + }) + } + + /// Safely writes bytes to the memory at the given offset. + /// + /// If the write exceeds the bounds of the memory then a `MemoryAccessError` is + /// returned. + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent reads/writes. + pub fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { + match_rt!(on self => s { + s.write(offset, data) + }) + } + + /// Safely writes a single byte from memory at the given offset + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent writes. + pub fn write_u8(&self, offset: u64, val: u8) -> Result<(), MemoryAccessError> { + match_rt!(on self => s { + s.write_u8(offset, val) + }) + } + + /// Copies the memory and returns it as a vector of bytes + pub fn copy_to_vec(&self) -> Result, MemoryAccessError> { + self.copy_range_to_vec(0..self.data_size()) + } + + /// Copies a range of the memory and returns it as a vector of bytes + pub fn copy_range_to_vec(&self, range: Range) -> Result, MemoryAccessError> { + let mut new_memory = Vec::new(); + let mut offset = range.start; + let end = range.end.min(self.data_size()); + let mut chunk = [0u8; 40960]; + while offset < end { + let remaining = end - offset; + let sublen = remaining.min(chunk.len() as u64) as usize; + self.read(offset, &mut chunk[..sublen])?; + new_memory.extend_from_slice(&chunk[..sublen]); + offset += sublen as u64; + } + Ok(new_memory) + } + + /// Copies the memory to another new memory object + pub fn copy_to_memory(&self, amount: u64, new_memory: &Self) -> Result<(), MemoryAccessError> { + let mut offset = 0; + let mut chunk = [0u8; 40960]; + while offset < amount { + let remaining = amount - offset; + let sublen = remaining.min(chunk.len() as u64) as usize; + self.read(offset, &mut chunk[..sublen])?; + + new_memory.write(offset, &chunk[..sublen])?; + + offset += sublen as u64; + } + Ok(()) + } +} diff --git a/lib/api/src/entities/memory/view/mod.rs b/lib/api/src/entities/memory/view/mod.rs new file mode 100644 index 00000000000..39380467b24 --- /dev/null +++ b/lib/api/src/entities/memory/view/mod.rs @@ -0,0 +1,155 @@ +use std::{mem::MaybeUninit, ops::Range}; +use wasmer_types::Pages; + +use crate::{buffer::MemoryBuffer, AsStoreRef, Memory, MemoryAccessError}; + +pub(crate) mod inner; +pub(crate) use inner::*; + +/// A WebAssembly `memory` view. +/// +/// A memory view is used to read and write to the linear memory. +/// +/// After a memory is grown a view must not be used anymore. Views are +/// created using the Memory.view() method. +#[derive(Debug, derive_more::From)] +pub struct MemoryView<'a>(pub(crate) RuntimeMemoryView<'a>); + +impl<'a> MemoryView<'a> { + pub(crate) fn new(memory: &Memory, store: &'a (impl AsStoreRef + ?Sized)) -> Self { + Self(RuntimeMemoryView::new(memory, store)) + } + + /// Returns the pointer to the raw bytes of the `Memory`. + // + // This used by wasmer-c-api, but should be treated + // as deprecated and not used in future code. + #[doc(hidden)] + pub fn data_ptr(&self) -> *mut u8 { + self.0.data_ptr() + } + + /// Returns the size (in bytes) of the `Memory`. + pub fn data_size(&self) -> u64 { + self.0.data_size() + } + + /// Retrieve a slice of the memory contents. + /// + /// # Safety + /// + /// Until the returned slice is dropped, it is undefined behaviour to + /// modify the memory contents in any way including by calling a wasm + /// function that writes to the memory or by resizing the memory. + #[doc(hidden)] + pub unsafe fn data_unchecked(&self) -> &[u8] { + self.0.data_unchecked() + } + + /// Retrieve a mutable slice of the memory contents. + /// + /// # Safety + /// + /// This method provides interior mutability without an UnsafeCell. Until + /// the returned value is dropped, it is undefined behaviour to read or + /// write to the pointed-to memory in any way except through this slice, + /// including by calling a wasm function that reads the memory contents or + /// by resizing this Memory. + #[allow(clippy::mut_from_ref)] + #[doc(hidden)] + pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] { + self.0.data_unchecked_mut() + } + + /// Returns the size (in [`Pages`]) of the `Memory`. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; + /// # let mut store = Store::default(); + /// # + /// let m = Memory::new(&mut store, MemoryType::new(1, None, false)).unwrap(); + /// + /// assert_eq!(m.view(&mut store).size(), Pages(1)); + /// ``` + pub fn size(&self) -> Pages { + self.0.size() + } + + #[inline] + pub(crate) fn buffer(&'a self) -> MemoryBuffer<'a> { + self.0.buffer() + } + + /// Safely reads bytes from the memory at the given offset. + /// + /// The full buffer will be filled, otherwise a `MemoryAccessError` is returned + /// to indicate an out-of-bounds access. + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent writes. + pub fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> { + self.0.read(offset, buf) + } + + /// Safely reads a single byte from memory at the given offset + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent writes. + pub fn read_u8(&self, offset: u64) -> Result { + self.0.read_u8(offset) + } + + /// Safely reads bytes from the memory at the given offset. + /// + /// This method is similar to `read` but allows reading into an + /// uninitialized buffer. An initialized view of the buffer is returned. + /// + /// The full buffer will be filled, otherwise a `MemoryAccessError` is returned + /// to indicate an out-of-bounds access. + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent writes. + pub fn read_uninit<'b>( + &self, + offset: u64, + buf: &'b mut [MaybeUninit], + ) -> Result<&'b mut [u8], MemoryAccessError> { + self.0.read_uninit(offset, buf) + } + + /// Safely writes bytes to the memory at the given offset. + /// + /// If the write exceeds the bounds of the memory then a `MemoryAccessError` is + /// returned. + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent reads/writes. + pub fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { + self.0.write(offset, data) + } + + /// Safely writes a single byte from memory at the given offset + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent writes. + pub fn write_u8(&self, offset: u64, val: u8) -> Result<(), MemoryAccessError> { + self.0.write_u8(offset, val) + } + + /// Copies the memory and returns it as a vector of bytes + pub fn copy_to_vec(&self) -> Result, MemoryAccessError> { + self.0.copy_to_vec() + } + + /// Copies a range of the memory and returns it as a vector of bytes + pub fn copy_range_to_vec(&self, range: Range) -> Result, MemoryAccessError> { + self.0.copy_range_to_vec(range) + } + + /// Copies the memory to another new memory object + pub fn copy_to_memory(&self, amount: u64, new_memory: &Self) -> Result<(), MemoryAccessError> { + self.0.copy_to_memory(amount, &new_memory.0) + } +} diff --git a/lib/api/src/entities/mod.rs b/lib/api/src/entities/mod.rs new file mode 100644 index 00000000000..99c462ebc03 --- /dev/null +++ b/lib/api/src/entities/mod.rs @@ -0,0 +1,51 @@ +//! This module defines data types, functions and traits used to describe and interact with +//! various WebAssembly entities such as [`Value`], [`Module`] and [`Store`]. It also describes +//! entities related to the runtime, such as [`Engine`]. +//! +//! For more informations, refer to the [WebAssembly specification] and the [Wasmer Runtime +//! documentation]. +//! +//! [WebAssembly specification]: https://webassembly.github.io/spec/core/ +//! [Wasmer Runtime documentation]: https://webassembly.github.io/spec/core/ + +pub(crate) mod engine; +pub use engine::*; + +pub(crate) mod store; +pub use store::*; + +pub(crate) mod module; +pub use module::*; + +pub(crate) mod instance; +pub use instance::*; + +pub(crate) mod trap; +pub use trap::*; + +pub(crate) mod value; +pub use value::*; + +pub(crate) mod extref; +pub use extref::*; + +pub(crate) mod external; +pub use external::*; + +pub(crate) mod table; +pub use table::*; + +pub(crate) mod global; +pub use global::*; + +pub(crate) mod function; +pub use function::*; + +pub(crate) mod memory; +pub use memory::*; + +pub(crate) mod exports; +pub use exports::*; + +pub(crate) mod imports; +pub use imports::*; diff --git a/lib/api/src/entities/module/inner.rs b/lib/api/src/entities/module/inner.rs new file mode 100644 index 00000000000..c42fa4ffee1 --- /dev/null +++ b/lib/api/src/entities/module/inner.rs @@ -0,0 +1,614 @@ +//! Defines the [`Module`] data type, the [`ModuleLike`] trait for implementers and various useful traits +//! and data types to interact with them. + +use std::{fs, path::Path}; + +use bytes::Bytes; +use thiserror::Error; +#[cfg(feature = "wat")] +use wasmer_types::WasmError; +use wasmer_types::{ + CompileError, DeserializeError, ExportType, ExportsIterator, ImportType, ImportsIterator, + ModuleInfo, SerializeError, +}; + +use crate::{ + macros::rt::{gen_rt_ty, match_rt}, + utils::IntoBytes, + AsEngineRef, +}; + +/// IO errors that can happen while compiling a [`Module`]. +#[derive(Error, Debug)] +pub enum IoCompileError { + /// An IO error + #[error(transparent)] + Io(#[from] std::io::Error), + /// A compilation error + #[error(transparent)] + Compile(#[from] CompileError), +} + +/// A WebAssembly Module contains stateless WebAssembly +/// code that has already been compiled and can be instantiated +/// multiple times. +/// +/// ## Cloning a module +/// +/// Cloning a module is cheap: it does a shallow copy of the compiled +/// contents rather than a deep copy. +gen_rt_ty!(Module + @cfg feature = "artifact-size" => derive(loupe::MemoryUsage) + @derives Clone, PartialEq, Eq, derive_more::From +); + +impl RuntimeModule { + pub fn new(engine: &impl AsEngineRef, bytes: impl AsRef<[u8]>) -> Result { + #[cfg(feature = "wat")] + let bytes = wat::parse_bytes(bytes.as_ref()).map_err(|e| { + CompileError::Wasm(WasmError::Generic(format!( + "Error when converting wat: {}", + e + ))) + })?; + Self::from_binary(engine, bytes.as_ref()) + } + + /// Creates a new WebAssembly module from a file path. + pub fn from_file( + engine: &impl AsEngineRef, + file: impl AsRef, + ) -> Result { + let file_ref = file.as_ref(); + let canonical = file_ref.canonicalize()?; + let wasm_bytes = std::fs::read(file_ref)?; + let mut module = Self::new(engine, wasm_bytes)?; + // Set the module name to the absolute path of the filename. + // This is useful for debugging the stack traces. + let filename = canonical.as_path().to_str().unwrap(); + module.set_name(filename); + Ok(module) + } + + /// Creates a new WebAssembly module from a Wasm binary. + /// + /// Opposed to [`Module::new`], this function is not compatible with + /// the WebAssembly text format (if the "wat" feature is enabled for + /// this crate). + pub fn from_binary(engine: &impl AsEngineRef, binary: &[u8]) -> Result { + match engine.as_engine_ref().inner.rt { + #[cfg(feature = "sys")] + crate::RuntimeEngine::Sys(_) => Ok(Self::Sys( + crate::rt::sys::entities::module::Module::from_binary(engine, binary)?, + )), + + #[cfg(feature = "wamr")] + crate::RuntimeEngine::Wamr(_) => Ok(Self::Wamr( + crate::rt::wamr::entities::module::Module::from_binary(engine, binary)?, + )), + + #[cfg(feature = "wasmi")] + crate::RuntimeEngine::Wasmi(_) => Ok(Self::Wasmi( + crate::rt::wasmi::entities::module::Module::from_binary(engine, binary)?, + )), + + #[cfg(feature = "v8")] + crate::RuntimeEngine::V8(_) => Ok(Self::V8( + crate::rt::v8::entities::module::Module::from_binary(engine, binary)?, + )), + + #[cfg(feature = "js")] + crate::RuntimeEngine::Js(_) => Ok(Self::Js( + crate::rt::js::entities::module::Module::from_binary(engine, binary)?, + )), + + #[cfg(feature = "jsc")] + crate::RuntimeEngine::Jsc(_) => Ok(Self::Jsc( + crate::rt::jsc::entities::module::Module::from_binary(engine, binary)?, + )), + } + } + + /// Creates a new WebAssembly module from a Wasm binary, + /// skipping any kind of validation on the WebAssembly file. + /// + /// # Safety + /// + /// This can speed up compilation time a bit, but it should be only used + /// in environments where the WebAssembly modules are trusted and validated + /// beforehand. + pub unsafe fn from_binary_unchecked( + engine: &impl AsEngineRef, + binary: &[u8], + ) -> Result { + match engine.as_engine_ref().inner.rt { + #[cfg(feature = "sys")] + crate::RuntimeEngine::Sys(_) => Ok(Self::Sys( + crate::rt::sys::entities::module::Module::from_binary_unchecked(engine, binary)?, + )), + + #[cfg(feature = "wamr")] + crate::RuntimeEngine::Wamr(_) => Ok(Self::Wamr( + crate::rt::wamr::entities::module::Module::from_binary_unchecked(engine, binary)?, + )), + + #[cfg(feature = "wasmi")] + crate::RuntimeEngine::Wasmi(_) => Ok(Self::Wasmi( + crate::rt::wasmi::entities::module::Module::from_binary_unchecked(engine, binary)?, + )), + + #[cfg(feature = "v8")] + crate::RuntimeEngine::V8(_) => Ok(Self::V8( + crate::rt::v8::entities::module::Module::from_binary_unchecked(engine, binary)?, + )), + #[cfg(feature = "js")] + crate::RuntimeEngine::Js(_) => Ok(Self::Js( + crate::rt::js::entities::module::Module::from_binary_unchecked(engine, binary)?, + )), + #[cfg(feature = "jsc")] + crate::RuntimeEngine::Jsc(_) => Ok(Self::Jsc( + crate::rt::jsc::entities::module::Module::from_binary_unchecked(engine, binary)?, + )), + } + } + + /// Validates a new WebAssembly Module given the configuration + /// in the Store. + /// + /// This validation is normally pretty fast and checks the enabled + /// WebAssembly features in the Store Engine to assure deterministic + /// validation of the Module. + pub fn validate(engine: &impl AsEngineRef, binary: &[u8]) -> Result<(), CompileError> { + match engine.as_engine_ref().inner.rt { + #[cfg(feature = "sys")] + crate::RuntimeEngine::Sys(_) => { + crate::rt::sys::entities::module::Module::validate(engine, binary)? + } + #[cfg(feature = "wamr")] + crate::RuntimeEngine::Wamr(_) => { + crate::rt::wamr::entities::module::Module::validate(engine, binary)? + } + + #[cfg(feature = "wasmi")] + crate::RuntimeEngine::Wasmi(_) => { + crate::rt::wasmi::entities::module::Module::validate(engine, binary)? + } + #[cfg(feature = "v8")] + crate::RuntimeEngine::V8(_) => { + crate::rt::v8::entities::module::Module::validate(engine, binary)? + } + #[cfg(feature = "js")] + crate::RuntimeEngine::Js(_) => { + crate::rt::js::entities::module::Module::validate(engine, binary)? + } + #[cfg(feature = "jsc")] + crate::RuntimeEngine::Jsc(_) => { + crate::rt::jsc::entities::module::Module::validate(engine, binary)? + } + } + Ok(()) + } + + /// Serializes a module into a binary representation that the `Engine` + /// can later process via [`Module::deserialize`]. + /// + /// # Important + /// + /// This function will return a custom binary format that will be different than + /// the `wasm` binary format, but faster to load in Native hosts. + /// + /// # Usage + /// + /// ```ignore + /// # use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// # let module = Module::from_file(&store, "path/to/foo.wasm")?; + /// let serialized = module.serialize()?; + /// # Ok(()) + /// # } + /// ``` + pub fn serialize(&self) -> Result { + match_rt!(on self => s { + s.serialize() + }) + } + + /// Serializes a module into a file that the `Engine` + /// can later process via [`Module::deserialize_from_file`]. + /// + /// # Usage + /// + /// ```ignore + /// # use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// # let module = Module::from_file(&store, "path/to/foo.wasm")?; + /// module.serialize_to_file("path/to/foo.so")?; + /// # Ok(()) + /// # } + /// ``` + pub fn serialize_to_file(&self, path: impl AsRef) -> Result<(), SerializeError> { + let serialized = self.serialize()?; + fs::write(path, serialized)?; + Ok(()) + } + + /// Deserializes a serialized module binary into a `Module`. + /// + /// Note: You should usually prefer the safer [`Module::deserialize`]. + /// + /// # Important + /// + /// This function only accepts a custom binary format, which will be different + /// than the `wasm` binary format and may change among Wasmer versions. + /// (it should be the result of the serialization of a Module via the + /// `Module::serialize` method.). + /// + /// # Safety + /// + /// This function is inherently **unsafe** as the provided bytes: + /// 1. Are going to be deserialized directly into Rust objects. + /// 2. Contains the function assembly bodies and, if intercepted, + /// a malicious actor could inject code into executable + /// memory. + /// + /// And as such, the `deserialize_unchecked` method is unsafe. + /// + /// # Usage + /// + /// ```ignore + /// # use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// let module = Module::deserialize_unchecked(&store, serialized_data)?; + /// # Ok(()) + /// # } + /// ``` + pub unsafe fn deserialize_unchecked( + engine: &impl AsEngineRef, + bytes: impl IntoBytes, + ) -> Result { + match engine.as_engine_ref().inner.rt { + #[cfg(feature = "sys")] + crate::RuntimeEngine::Sys(_) => Ok(Self::Sys( + crate::rt::sys::entities::module::Module::deserialize_unchecked(engine, bytes)?, + )), + #[cfg(feature = "wamr")] + crate::RuntimeEngine::Wamr(_) => Ok(Self::Wamr( + crate::rt::wamr::entities::module::Module::deserialize_unchecked(engine, bytes)?, + )), + + #[cfg(feature = "wasmi")] + crate::RuntimeEngine::Wasmi(_) => Ok(Self::Wasmi( + crate::rt::wasmi::entities::module::Module::deserialize_unchecked(engine, bytes)?, + )), + #[cfg(feature = "v8")] + crate::RuntimeEngine::V8(_) => Ok(Self::V8( + crate::rt::v8::entities::module::Module::deserialize_unchecked(engine, bytes)?, + )), + #[cfg(feature = "js")] + crate::RuntimeEngine::Js(_) => Ok(Self::Js( + crate::rt::js::entities::module::Module::deserialize_unchecked(engine, bytes)?, + )), + #[cfg(feature = "jsc")] + crate::RuntimeEngine::Jsc(_) => Ok(Self::Jsc( + crate::rt::jsc::entities::module::Module::deserialize_unchecked(engine, bytes)?, + )), + } + } + + /// Deserializes a serialized Module binary into a `Module`. + /// + /// # Important + /// + /// This function only accepts a custom binary format, which will be different + /// than the `wasm` binary format and may change among Wasmer versions. + /// (it should be the result of the serialization of a Module via the + /// `Module::serialize` method.). + /// + /// # Usage + /// + /// ```ignore + /// # use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// let module = Module::deserialize(&store, serialized_data)?; + /// # Ok(()) + /// # } + /// ``` + /// + /// # Safety + /// This function is inherently **unsafe**, because it loads executable code + /// into memory. + /// The loaded bytes must be trusted to contain a valid artifact previously + /// built with [`Self::serialize`]. + pub unsafe fn deserialize( + engine: &impl AsEngineRef, + bytes: impl IntoBytes, + ) -> Result { + match engine.as_engine_ref().inner.rt { + #[cfg(feature = "sys")] + crate::RuntimeEngine::Sys(_) => Ok(Self::Sys( + crate::rt::sys::entities::module::Module::deserialize(engine, bytes)?, + )), + #[cfg(feature = "wamr")] + crate::RuntimeEngine::Wamr(_) => Ok(Self::Wamr( + crate::rt::wamr::entities::module::Module::deserialize(engine, bytes)?, + )), + + #[cfg(feature = "wasmi")] + crate::RuntimeEngine::Wasmi(_) => Ok(Self::Wasmi( + crate::rt::wasmi::entities::module::Module::deserialize(engine, bytes)?, + )), + #[cfg(feature = "v8")] + crate::RuntimeEngine::V8(_) => Ok(Self::V8( + crate::rt::v8::entities::module::Module::deserialize(engine, bytes)?, + )), + #[cfg(feature = "js")] + crate::RuntimeEngine::Js(_) => Ok(Self::Js( + crate::rt::js::entities::module::Module::deserialize(engine, bytes)?, + )), + #[cfg(feature = "jsc")] + crate::RuntimeEngine::Jsc(_) => Ok(Self::Jsc( + crate::rt::jsc::entities::module::Module::deserialize(engine, bytes)?, + )), + } + } + + /// Deserializes a serialized Module located in a `Path` into a `Module`. + /// > Note: the module has to be serialized before with the `serialize` method. + /// + /// # Usage + /// + /// ```ignore + /// # use wasmer::*; + /// # let mut store = Store::default(); + /// # fn main() -> anyhow::Result<()> { + /// let module = Module::deserialize_from_file(&store, path)?; + /// # Ok(()) + /// # } + /// ``` + /// + /// # Safety + /// + /// See [`Self::deserialize`]. + pub unsafe fn deserialize_from_file( + engine: &impl AsEngineRef, + path: impl AsRef, + ) -> Result { + match engine.as_engine_ref().inner.rt { + #[cfg(feature = "sys")] + crate::RuntimeEngine::Sys(_) => Ok(Self::Sys( + crate::rt::sys::entities::module::Module::deserialize_from_file(engine, path)?, + )), + #[cfg(feature = "wamr")] + crate::RuntimeEngine::Wamr(_) => Ok(Self::Wamr( + crate::rt::wamr::entities::module::Module::deserialize_from_file(engine, path)?, + )), + + #[cfg(feature = "wasmi")] + crate::RuntimeEngine::Wasmi(_) => Ok(Self::Wasmi( + crate::rt::wasmi::entities::module::Module::deserialize_from_file(engine, path)?, + )), + #[cfg(feature = "v8")] + crate::RuntimeEngine::V8(_) => Ok(Self::V8( + crate::rt::v8::entities::module::Module::deserialize_from_file(engine, path)?, + )), + #[cfg(feature = "js")] + crate::RuntimeEngine::Js(_) => Ok(Self::Js( + crate::rt::js::entities::module::Module::deserialize_from_file(engine, path)?, + )), + #[cfg(feature = "jsc")] + crate::RuntimeEngine::Jsc(_) => Ok(Self::Jsc( + crate::rt::jsc::entities::module::Module::deserialize_from_file(engine, path)?, + )), + } + } + + /// Deserializes a serialized Module located in a `Path` into a `Module`. + /// > Note: the module has to be serialized before with the `serialize` method. + /// + /// You should usually prefer the safer [`Module::deserialize_from_file`]. + /// + /// # Safety + /// + /// Please check [`Module::deserialize_unchecked`]. + /// + /// # Usage + /// + /// ```ignore + /// # use wasmer::*; + /// # let mut store = Store::default(); + /// # fn main() -> anyhow::Result<()> { + /// let module = Module::deserialize_from_file_unchecked(&store, path)?; + /// # Ok(()) + /// # } + /// ``` + pub unsafe fn deserialize_from_file_unchecked( + engine: &impl AsEngineRef, + path: impl AsRef, + ) -> Result { + match engine.as_engine_ref().inner.rt { + #[cfg(feature = "sys")] + crate::RuntimeEngine::Sys(_) => Ok(Self::Sys( + crate::rt::sys::entities::module::Module::deserialize_from_file_unchecked( + engine, path, + )?, + )), + #[cfg(feature = "wamr")] + crate::RuntimeEngine::Wamr(_) => Ok(Self::Wamr( + crate::rt::wamr::entities::module::Module::deserialize_from_file_unchecked( + engine, path, + )?, + )), + + #[cfg(feature = "wasmi")] + crate::RuntimeEngine::Wasmi(_) => Ok(Self::Wasmi( + crate::rt::wasmi::entities::module::Module::deserialize_from_file_unchecked( + engine, path, + )?, + )), + #[cfg(feature = "v8")] + crate::RuntimeEngine::V8(_) => Ok(Self::V8( + crate::rt::v8::entities::module::Module::deserialize_from_file_unchecked( + engine, path, + )?, + )), + #[cfg(feature = "js")] + crate::RuntimeEngine::Js(_) => Ok(Self::Js( + crate::rt::js::entities::module::Module::deserialize_from_file_unchecked( + engine, path, + )?, + )), + #[cfg(feature = "jsc")] + crate::RuntimeEngine::Jsc(_) => Ok(Self::Jsc( + crate::rt::jsc::entities::module::Module::deserialize_from_file_unchecked( + engine, path, + )?, + )), + } + } + + /// Returns the name of the current module. + /// + /// This name is normally set in the WebAssembly bytecode by some + /// compilers, but can be also overwritten using the [`Module::set_name`] method. + /// + /// # Example + /// + /// ``` + /// # use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// let wat = "(module $moduleName)"; + /// let module = Module::new(&store, wat)?; + /// assert_eq!(module.name(), Some("moduleName")); + /// # Ok(()) + /// # } + /// ``` + pub fn name(&self) -> Option<&str> { + match_rt!(on self => s { + s.name() + }) + } + + /// Sets the name of the current module. + /// This is normally useful for stacktraces and debugging. + /// + /// It will return `true` if the module name was changed successfully, + /// and return `false` otherwise (in case the module is cloned or + /// already instantiated). + /// + /// # Example + /// + /// ``` + /// # use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// let wat = "(module)"; + /// let mut module = Module::new(&store, wat)?; + /// assert_eq!(module.name(), None); + /// module.set_name("foo"); + /// assert_eq!(module.name(), Some("foo")); + /// # Ok(()) + /// # } + /// ``` + pub fn set_name(&mut self, name: &str) -> bool { + match_rt!(on self => s { + s.set_name(name) + }) + } + + /// Returns an iterator over the imported types in the Module. + /// + /// The order of the imports is guaranteed to be the same as in the + /// WebAssembly bytecode. + /// + /// # Example + /// + /// ``` + /// # use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// let wat = r#"(module + /// (import "host" "func1" (func)) + /// (import "host" "func2" (func)) + /// )"#; + /// let module = Module::new(&store, wat)?; + /// for import in module.imports() { + /// assert_eq!(import.module(), "host"); + /// assert!(import.name().contains("func")); + /// import.ty(); + /// } + /// # Ok(()) + /// # } + /// ``` + pub fn imports(&self) -> ImportsIterator + '_>> { + match_rt!(on self => s { + s.imports() + }) + } + + /// Returns an iterator over the exported types in the Module. + /// + /// The order of the exports is guaranteed to be the same as in the + /// WebAssembly bytecode. + /// + /// # Example + /// + /// ``` + /// # use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// let wat = r#"(module + /// (func (export "namedfunc")) + /// (memory (export "namedmemory") 1) + /// )"#; + /// let module = Module::new(&store, wat)?; + /// for export_ in module.exports() { + /// assert!(export_.name().contains("named")); + /// export_.ty(); + /// } + /// # Ok(()) + /// # } + /// ``` + pub fn exports(&self) -> ExportsIterator + '_>> { + match_rt!(on self => s { + s.exports() + }) + } + + /// Get the custom sections of the module given a `name`. + /// + /// # Important + /// + /// Following the WebAssembly spec, one name can have multiple + /// custom sections. That's why an iterator (rather than one element) + /// is returned. + pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator> + 'a { + match_rt!(on self => s { + s.custom_sections(name) + }) + } + + /// The ABI of the [`ModuleInfo`] is very unstable, we refactor it very often. + /// This function is public because in some cases it can be useful to get some + /// extra information from the module. + /// + /// However, the usage is highly discouraged. + #[doc(hidden)] + pub fn info(&self) -> &ModuleInfo { + match_rt!(on self => s { + s.info() + }) + } +} + +impl std::fmt::Debug for RuntimeModule { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RuntimeModule") + .field("name", &self.name()) + .finish() + } +} diff --git a/lib/api/src/module.rs b/lib/api/src/entities/module/mod.rs similarity index 89% rename from lib/api/src/module.rs rename to lib/api/src/entities/module/mod.rs index 7f024e9b711..f91de8ac535 100644 --- a/lib/api/src/module.rs +++ b/lib/api/src/entities/module/mod.rs @@ -1,35 +1,28 @@ -use bytes::Bytes; -use std::fmt; -use std::fs; -use std::io; -use std::path::Path; +//! Defines the [`Module`] data type, the [`ModuleLike`] trait for implementers and various useful traits +//! and data types to interact with them. + +pub(crate) mod inner; +pub(crate) use inner::*; -use crate::engine::AsEngineRef; +use std::{fs, path::Path}; + +use bytes::Bytes; use thiserror::Error; #[cfg(feature = "wat")] use wasmer_types::WasmError; use wasmer_types::{ - CompileError, DeserializeError, ExportsIterator, ImportsIterator, ModuleInfo, SerializeError, + CompileError, DeserializeError, ExportType, ExportsIterator, ImportType, ImportsIterator, + ModuleInfo, SerializeError, }; -use wasmer_types::{ExportType, ImportType}; -use crate::into_bytes::IntoBytes; +use crate::{macros::rt::match_rt, utils::IntoBytes, AsEngineRef}; -#[cfg(feature = "wasm-c-api")] -use crate::c_api::module as module_imp; -#[cfg(feature = "js")] -use crate::js::module as module_imp; -#[cfg(feature = "jsc")] -use crate::jsc::module as module_imp; -#[cfg(feature = "sys")] -use crate::sys::module as module_imp; - -/// IO Error on a Module Compilation +/// IO errors that can happen while compiling a [`Module`]. #[derive(Error, Debug)] pub enum IoCompileError { /// An IO error #[error(transparent)] - Io(#[from] io::Error), + Io(#[from] std::io::Error), /// A compilation error #[error(transparent)] Compile(#[from] CompileError), @@ -43,9 +36,9 @@ pub enum IoCompileError { /// /// Cloning a module is cheap: it does a shallow copy of the compiled /// contents rather than a deep copy. -#[derive(Clone, PartialEq, Eq)] #[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))] -pub struct Module(pub(crate) module_imp::Module); +#[derive(Clone, PartialEq, Eq, derive_more::From)] +pub struct Module(pub(crate) RuntimeModule); impl Module { /// Creates a new WebAssembly Module given the configuration @@ -112,7 +105,7 @@ impl Module { /// ``` /// # use wasmer::*; /// # - /// # let engine: Engine = Cranelift::default().into(); + /// # let engine: Engine = Engine::default().into(); /// /// let module = Module::from_file(&engine, "path/to/foo.wasm"); /// ``` @@ -149,7 +142,7 @@ impl Module { /// the WebAssembly text format (if the "wat" feature is enabled for /// this crate). pub fn from_binary(engine: &impl AsEngineRef, binary: &[u8]) -> Result { - Ok(Self(module_imp::Module::from_binary(engine, binary)?)) + RuntimeModule::from_binary(engine, binary).map(Self) } /// Creates a new WebAssembly module from a Wasm binary, @@ -164,9 +157,7 @@ impl Module { engine: &impl AsEngineRef, binary: &[u8], ) -> Result { - Ok(Self(module_imp::Module::from_binary_unchecked( - engine, binary, - )?)) + RuntimeModule::from_binary_unchecked(engine, binary).map(Self) } /// Validates a new WebAssembly Module given the configuration @@ -176,7 +167,8 @@ impl Module { /// WebAssembly features in the Store Engine to assure deterministic /// validation of the Module. pub fn validate(engine: &impl AsEngineRef, binary: &[u8]) -> Result<(), CompileError> { - module_imp::Module::validate(engine, binary) + RuntimeModule::validate(engine, binary)?; + Ok(()) } /// Serializes a module into a binary representation that the `Engine` @@ -217,7 +209,7 @@ impl Module { /// # } /// ``` pub fn serialize_to_file(&self, path: impl AsRef) -> Result<(), SerializeError> { - let serialized = self.0.serialize()?; + let serialized = self.serialize()?; fs::write(path, serialized)?; Ok(()) } @@ -257,9 +249,7 @@ impl Module { engine: &impl AsEngineRef, bytes: impl IntoBytes, ) -> Result { - Ok(Self(module_imp::Module::deserialize_unchecked( - engine, bytes, - )?)) + RuntimeModule::deserialize_unchecked(engine, bytes).map(Self) } /// Deserializes a serialized Module binary into a `Module`. @@ -291,7 +281,7 @@ impl Module { engine: &impl AsEngineRef, bytes: impl IntoBytes, ) -> Result { - Ok(Self(module_imp::Module::deserialize(engine, bytes)?)) + RuntimeModule::deserialize_unchecked(engine, bytes).map(Self) } /// Deserializes a serialized Module located in a `Path` into a `Module`. @@ -315,9 +305,7 @@ impl Module { engine: &impl AsEngineRef, path: impl AsRef, ) -> Result { - Ok(Self(module_imp::Module::deserialize_from_file( - engine, path, - )?)) + RuntimeModule::deserialize_from_file(engine, path).map(Self) } /// Deserializes a serialized Module located in a `Path` into a `Module`. @@ -343,9 +331,7 @@ impl Module { engine: &impl AsEngineRef, path: impl AsRef, ) -> Result { - Ok(Self(module_imp::Module::deserialize_from_file_unchecked( - engine, path, - )?)) + RuntimeModule::deserialize_from_file_unchecked(engine, path).map(Self) } /// Returns the name of the current module. @@ -418,7 +404,7 @@ impl Module { /// # Ok(()) /// # } /// ``` - pub fn imports(&self) -> ImportsIterator + '_> { + pub fn imports(&self) -> ImportsIterator + '_>> { self.0.imports() } @@ -445,7 +431,7 @@ impl Module { /// # Ok(()) /// # } /// ``` - pub fn exports(&self) -> ExportsIterator + '_> { + pub fn exports(&self) -> ExportsIterator + '_>> { self.0.exports() } @@ -471,8 +457,8 @@ impl Module { } } -impl fmt::Debug for Module { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl std::fmt::Debug for Module { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Module") .field("name", &self.name()) .finish() @@ -482,6 +468,6 @@ impl fmt::Debug for Module { #[cfg(feature = "js")] impl From for wasm_bindgen::JsValue { fn from(value: Module) -> Self { - wasm_bindgen::JsValue::from(value.0) + todo!() } } diff --git a/lib/api/src/entities/store/inner.rs b/lib/api/src/entities/store/inner.rs new file mode 100644 index 00000000000..77948c5c580 --- /dev/null +++ b/lib/api/src/entities/store/inner.rs @@ -0,0 +1,63 @@ +use crate::{ + entities::{ + engine::{AsEngineRef, Engine}, + store::{StoreMut, StoreObjects}, + }, + macros::rt::{gen_rt_ty, match_rt}, + AsStoreMut, +}; + +#[cfg(feature = "sys")] +use wasmer_vm::TrapHandlerFn; + +/// We require the context to have a fixed memory address for its lifetime since +/// various bits of the VM have raw pointers that point back to it. Hence we +/// wrap the actual context in a box. +pub(crate) struct StoreInner { + pub(crate) objects: StoreObjects, + pub(crate) store: RuntimeStore, + pub(crate) on_called: Option, +} + +impl std::fmt::Debug for StoreInner { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("StoreInner") + .field("objects", &self.objects) + .field("store", &self.store) + .field("on_called", &"<...>") + .finish() + } +} + +/// Call handler for a store. +// TODO: better documentation! +pub type OnCalledHandler = Box< + dyn FnOnce( + StoreMut<'_>, + ) + -> Result>, +>; + +gen_rt_ty!(Store @derives derive_more::From, Debug; @path store); + +impl RuntimeStore { + pub(crate) fn engine(&self) -> &Engine { + match_rt!(on self => s { + s.engine() + }) + } + + pub(crate) fn engine_mut(&mut self) -> &mut Engine { + match_rt!(on self => s { + s.engine_mut() + }) + } +} + +impl AsEngineRef for RuntimeStore { + fn as_engine_ref(&self) -> crate::EngineRef<'_> { + match_rt!(on self => s { + s.as_engine_ref() + }) + } +} diff --git a/lib/api/src/entities/store/mod.rs b/lib/api/src/entities/store/mod.rs new file mode 100644 index 00000000000..3f67edcea23 --- /dev/null +++ b/lib/api/src/entities/store/mod.rs @@ -0,0 +1,161 @@ +//! Defines the [`Store`] data type, the [`StoreLike`] trait for implementers and various useful traits +//! and data types to interact with them. + +/// Defines the [`StoreInner`] data type and the [`StoreLike`] trait. +mod inner; + +/// Create temporary handles to engines. +mod store_ref; +pub use store_ref::*; + +mod obj; +pub use obj::*; + +use crate::{AsEngineRef, Engine, EngineRef, RuntimeEngine}; +pub(crate) use inner::*; +use wasmer_types::StoreId; + +#[cfg(feature = "sys")] +use wasmer_vm::TrapHandlerFn; + +/// The store represents all global state that can be manipulated by +/// WebAssembly programs. It consists of the runtime representation +/// of all instances of functions, tables, memories, and globals that +/// have been allocated during the lifetime of the abstract machine. +/// +/// The [`Store`] is tied to the underlying [`Engine`] that is — among many things — used to +/// compile the Wasm bytes into a valid module artifact. +/// +/// For more informations, check out the [related WebAssembly specification] +/// [related WebAssembly specification]: +pub struct Store { + pub(crate) inner: Box, +} + +impl Store { + /// Creates a new `Store` with a specific [`Engine`]. + pub fn new(engine: impl Into) -> Self { + let engine: Engine = engine.into(); + + let store = match engine.rt { + #[cfg(feature = "sys")] + RuntimeEngine::Sys(_) => { + RuntimeStore::Sys(crate::rt::sys::entities::store::Store::new(engine)) + } + #[cfg(feature = "wamr")] + RuntimeEngine::Wamr(_) => { + RuntimeStore::Wamr(crate::rt::wamr::entities::store::Store::new(engine)) + } + #[cfg(feature = "wasmi")] + RuntimeEngine::Wasmi(_) => { + RuntimeStore::Wasmi(crate::rt::wasmi::entities::store::Store::new(engine)) + } + #[cfg(feature = "v8")] + RuntimeEngine::V8(_) => { + RuntimeStore::V8(crate::rt::v8::entities::store::Store::new(engine)) + } + #[cfg(feature = "js")] + RuntimeEngine::Js(_) => { + RuntimeStore::Js(crate::rt::js::entities::store::Store::new(engine)) + } + #[cfg(feature = "jsc")] + RuntimeEngine::Jsc(_) => { + RuntimeStore::Jsc(crate::rt::jsc::entities::store::Store::new(engine)) + } + }; + + Self { + inner: Box::new(StoreInner { + objects: StoreObjects::from_store_ref(&store), + on_called: None, + store, + }), + } + } + + #[cfg(feature = "sys")] + /// Set the [`TrapHandlerFn`] for this store. + /// + /// # Note + /// + /// Not every implementor allows changing the trap handler. In those store that + /// don't allow it, this function has no effect. + pub fn set_trap_handler(&mut self, handler: Option>>) { + use crate::rt::sys::entities::store::NativeStoreExt; + #[allow(irrefutable_let_patterns)] + if let RuntimeStore::Sys(ref mut s) = self.inner.store { + s.set_trap_handler(handler) + } + } + + /// Returns the [`Engine`]. + pub fn engine(&self) -> &Engine { + self.inner.store.engine() + } + + /// Returns mutable reference to [`Engine`]. + pub fn engine_mut(&mut self) -> &mut Engine { + self.inner.store.engine_mut() + } + + /// Checks whether two stores are identical. A store is considered + /// equal to another store if both have the same engine. + pub fn same(a: &Self, b: &Self) -> bool { + a.id() == b.id() + } + + /// Returns the ID of this store + pub fn id(&self) -> StoreId { + self.inner.objects.id() + } +} + +impl PartialEq for Store { + fn eq(&self, other: &Self) -> bool { + Self::same(self, other) + } +} + +// This is required to be able to set the trap_handler in the +// Store. +unsafe impl Send for Store {} +unsafe impl Sync for Store {} + +impl Default for Store { + fn default() -> Self { + Self::new(Engine::default()) + } +} + +impl std::fmt::Debug for Store { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("Store").finish() + } +} + +impl AsEngineRef for Store { + fn as_engine_ref(&self) -> EngineRef<'_> { + self.inner.store.as_engine_ref() + } + + fn maybe_as_store(&self) -> Option> { + Some(self.as_store_ref()) + } +} + +impl AsStoreRef for Store { + fn as_store_ref(&self) -> StoreRef<'_> { + StoreRef { inner: &self.inner } + } +} +impl AsStoreMut for Store { + fn as_store_mut(&mut self) -> StoreMut<'_> { + StoreMut { + inner: &mut self.inner, + } + } + + fn objects_mut(&mut self) -> &mut StoreObjects { + &mut self.inner.objects + } +} diff --git a/lib/api/src/entities/store/obj.rs b/lib/api/src/entities/store/obj.rs new file mode 100644 index 00000000000..036b0abd72a --- /dev/null +++ b/lib/api/src/entities/store/obj.rs @@ -0,0 +1,97 @@ +use wasmer_types::StoreId; + +use crate::{macros::rt::match_rt, RuntimeStore}; + +/// Set of objects managed by a context. +#[derive(Debug)] +pub enum StoreObjects { + #[cfg(feature = "sys")] + /// Store objects for the `sys` runtime. + Sys(crate::rt::sys::store::StoreObjects), + + #[cfg(feature = "wamr")] + /// Store objects for the `wamr` runtime. + Wamr(crate::rt::wamr::store::StoreObjects), + + #[cfg(feature = "wasmi")] + /// Store objects for the `wasmi` runtime. + Wasmi(crate::rt::wasmi::store::StoreObjects), + + #[cfg(feature = "v8")] + /// Store objects for the `v8` runtime. + V8(crate::rt::v8::store::StoreObjects), + + #[cfg(feature = "js")] + /// Store objects for the `js` runtime. + Js(crate::rt::js::store::StoreObjects), + + #[cfg(feature = "jsc")] + /// Store objects for the `jsc` runtime. + Jsc(crate::rt::jsc::store::StoreObjects), +} + +impl StoreObjects { + /// Checks whether two stores are identical. A store is considered + /// equal to another store if both have the same engine. + pub fn same(a: &Self, b: &Self) -> bool { + match (a, b) { + #[cfg(feature = "sys")] + (Self::Sys(ref a), Self::Sys(ref b)) => a.id() == b.id(), + #[cfg(feature = "wamr")] + (Self::Wamr(ref a), Self::Wamr(ref b)) => a.id() == b.id(), + #[cfg(feature = "v8")] + (Self::V8(ref a), Self::V8(ref b)) => a.id() == b.id(), + #[cfg(feature = "js")] + (Self::Js(ref a), Self::Js(ref b)) => a.id() == b.id(), + + #[cfg(feature = "jsc")] + (Self::Jsc(ref a), Self::Jsc(ref b)) => a.id() == b.id(), + + _ => panic!( + "Incompatible `StoreObjects` instance: {}, {}!", + a.id(), + b.id() + ), + } + } + + /// Returns the ID of this store + pub fn id(&self) -> StoreId { + match_rt!(on self => s { + s.id() + }) + } + + pub(crate) fn from_store_ref(store: &RuntimeStore) -> Self { + match store { + #[cfg(feature = "sys")] + RuntimeStore::Sys(_) => Self::Sys(Default::default()), + #[cfg(feature = "wamr")] + RuntimeStore::Wamr(_) => Self::Wamr(Default::default()), + #[cfg(feature = "wasmi")] + RuntimeStore::Wasmi(_) => Self::Wasmi(Default::default()), + #[cfg(feature = "v8")] + RuntimeStore::V8(_) => Self::V8(Default::default()), + #[cfg(feature = "js")] + RuntimeStore::Js(_) => Self::Js(Default::default()), + #[cfg(feature = "jsc")] + RuntimeStore::Jsc(_) => Self::Jsc(Default::default()), + } + } + + /// Return a vector of all globals and converted to u128 + pub fn as_u128_globals(&self) -> Vec { + match_rt!(on self => s { + s.as_u128_globals() + }) + } + + /// Set a global, at index idx. Will panic if idx is out of range + /// Safety: the caller should check taht the raw value is compatible + /// with destination VMGlobal type + pub fn set_global_unchecked(&self, idx: usize, val: u128) { + match_rt!(on self => s { + s.set_global_unchecked(idx, val) + }) + } +} diff --git a/lib/api/src/entities/store/store_ref.rs b/lib/api/src/entities/store/store_ref.rs new file mode 100644 index 00000000000..825a58b6455 --- /dev/null +++ b/lib/api/src/entities/store/store_ref.rs @@ -0,0 +1,157 @@ +use std::ops::{Deref, DerefMut}; + +use super::{inner::StoreInner, StoreObjects}; +use crate::entities::engine::{AsEngineRef, Engine, EngineRef}; +use wasmer_types::{ExternType, OnCalledAction}; +//use wasmer_vm::{StoreObjects, TrapHandlerFn}; + +#[cfg(feature = "sys")] +use wasmer_vm::TrapHandlerFn; + +/// A temporary handle to a [`crate::Store`]. +#[derive(Debug)] +pub struct StoreRef<'a> { + pub(crate) inner: &'a StoreInner, +} + +impl<'a> StoreRef<'a> { + pub(crate) fn objects(&self) -> &'a StoreObjects { + &self.inner.objects + } + + /// Returns the [`Engine`]. + pub fn engine(&self) -> &Engine { + self.inner.store.engine() + } + + /// Checks whether two stores are identical. A store is considered + /// equal to another store if both have the same engine. + pub fn same(a: &Self, b: &Self) -> bool { + StoreObjects::same(&a.inner.objects, &b.inner.objects) + } + + /// The signal handler + #[cfg(feature = "sys")] + #[inline] + pub fn signal_handler(&self) -> Option<*const TrapHandlerFn<'static>> { + use crate::rt::sys::entities::store::NativeStoreExt; + self.inner.store.as_sys().signal_handler() + } +} + +/// A temporary handle to a [`crate::Store`]. +pub struct StoreMut<'a> { + pub(crate) inner: &'a mut StoreInner, +} + +impl<'a> StoreMut<'a> { + /// Returns the [`Engine`]. + pub fn engine(&self) -> &Engine { + self.inner.store.engine() + } + + /// Checks whether two stores are identical. A store is considered + /// equal to another store if both have the same engine. + pub fn same(a: &Self, b: &Self) -> bool { + StoreObjects::same(&a.inner.objects, &b.inner.objects) + } + + #[allow(unused)] + pub(crate) fn as_raw(&self) -> *mut StoreInner { + self.inner as *const StoreInner as *mut StoreInner + } + + #[allow(unused)] + pub(crate) unsafe fn from_raw(raw: *mut StoreInner) -> Self { + Self { inner: &mut *raw } + } + + #[allow(unused)] + pub(crate) fn engine_and_objects_mut(&mut self) -> (&Engine, &mut StoreObjects) { + (self.inner.store.engine(), &mut self.inner.objects) + } + + // TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 + /// Sets the unwind callback which will be invoked when the call finishes + pub fn on_called(&mut self, callback: F) + where + F: FnOnce(StoreMut<'_>) -> Result> + + Send + + Sync + + 'static, + { + self.inner.on_called.replace(Box::new(callback)); + } +} + +/// Helper trait for a value that is convertible to a [`StoreRef`]. +pub trait AsStoreRef { + /// Returns a `StoreRef` pointing to the underlying context. + fn as_store_ref(&self) -> StoreRef<'_>; +} + +/// Helper trait for a value that is convertible to a [`StoreMut`]. +pub trait AsStoreMut: AsStoreRef { + /// Returns a `StoreMut` pointing to the underlying context. + fn as_store_mut(&mut self) -> StoreMut<'_>; + + /// Returns the ObjectMutable + fn objects_mut(&mut self) -> &mut StoreObjects; +} + +impl AsStoreRef for StoreRef<'_> { + fn as_store_ref(&self) -> StoreRef<'_> { + StoreRef { inner: self.inner } + } +} + +impl AsEngineRef for StoreRef<'_> { + fn as_engine_ref(&self) -> EngineRef<'_> { + self.inner.store.as_engine_ref() + } +} + +impl AsStoreRef for StoreMut<'_> { + fn as_store_ref(&self) -> StoreRef<'_> { + StoreRef { inner: self.inner } + } +} +impl AsStoreMut for StoreMut<'_> { + fn as_store_mut(&mut self) -> StoreMut<'_> { + StoreMut { inner: self.inner } + } + + fn objects_mut(&mut self) -> &mut StoreObjects { + &mut self.inner.objects + } +} + +impl

AsStoreRef for P +where + P: Deref, + P::Target: AsStoreRef, +{ + fn as_store_ref(&self) -> StoreRef<'_> { + (**self).as_store_ref() + } +} + +impl

AsStoreMut for P +where + P: DerefMut, + P::Target: AsStoreMut, +{ + fn as_store_mut(&mut self) -> StoreMut<'_> { + (**self).as_store_mut() + } + + fn objects_mut(&mut self) -> &mut StoreObjects { + (**self).objects_mut() + } +} + +impl AsEngineRef for StoreMut<'_> { + fn as_engine_ref(&self) -> EngineRef<'_> { + self.inner.store.as_engine_ref() + } +} diff --git a/lib/api/src/entities/table/inner.rs b/lib/api/src/entities/table/inner.rs new file mode 100644 index 00000000000..5f6ef0d916d --- /dev/null +++ b/lib/api/src/entities/table/inner.rs @@ -0,0 +1,262 @@ +use wasmer_types::TableType; + +use crate::{ + error::RuntimeError, + macros::rt::{gen_rt_ty, match_rt}, + store::RuntimeStore, + vm::{VMExtern, VMExternTable}, + AsStoreMut, AsStoreRef, ExportError, Exportable, Extern, StoreMut, StoreRef, Value, +}; +/// A WebAssembly `table` instance. +/// +/// The `Table` struct is an array-like structure representing a WebAssembly Table, +/// which stores function references. +/// +/// A table created by the host or in WebAssembly code will be accessible and +/// mutable from both host and WebAssembly. +/// +/// Spec: +gen_rt_ty!(Table + @cfg feature = "artifact-size" => derive(loupe::MemoryUsage) + @derives Debug, Clone, PartialEq, Eq, derive_more::From +); + +impl RuntimeTable { + /// Creates a new table with the provided [`TableType`] definition. + /// + /// All the elements in the table will be set to the `init` value. + /// + /// This function will construct the table using the store `BaseTunables`. + pub fn new( + store: &mut impl AsStoreMut, + ty: TableType, + init: Value, + ) -> Result { + match &store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + RuntimeStore::Sys(_) => Ok(Self::Sys(crate::rt::sys::entities::table::Table::new( + store, ty, init, + )?)), + #[cfg(feature = "wamr")] + RuntimeStore::Wamr(_) => Ok(Self::Wamr(crate::rt::wamr::entities::table::Table::new( + store, ty, init, + )?)), + #[cfg(feature = "wasmi")] + RuntimeStore::Wasmi(_) => Ok(Self::Wasmi( + crate::rt::wasmi::entities::table::Table::new(store, ty, init)?, + )), + #[cfg(feature = "v8")] + RuntimeStore::V8(_) => Ok(Self::V8(crate::rt::v8::entities::table::Table::new( + store, ty, init, + )?)), + #[cfg(feature = "js")] + RuntimeStore::Js(_) => Ok(Self::Js(crate::rt::js::entities::table::Table::new( + store, ty, init, + )?)), + #[cfg(feature = "jsc")] + RuntimeStore::Jsc(_) => Ok(Self::Jsc(crate::rt::jsc::entities::table::Table::new( + store, ty, init, + )?)), + } + } + + /// Returns the [`TableType`] of the table. + pub fn ty(&self, store: &impl AsStoreRef) -> TableType { + match_rt!(on self => s { + s.ty(store) + }) + } + + /// Retrieves an element of the table at the provided `index`. + pub fn get(&self, store: &mut impl AsStoreMut, index: u32) -> Option { + match_rt!(on self => s { + s.get(store, index) + }) + } + + /// Sets an element `val` in the Table at the provided `index`. + pub fn set( + &self, + store: &mut impl AsStoreMut, + index: u32, + val: Value, + ) -> Result<(), RuntimeError> { + match_rt!(on self => s { + s.set(store, index, val) + }) + } + + /// Retrieves the size of the `Table` (in elements) + pub fn size(&self, store: &impl AsStoreRef) -> u32 { + match_rt!(on self => s { + s.size(store) + }) + } + + /// Grows the size of the `Table` by `delta`, initializating + /// the elements with the provided `init` value. + /// + /// It returns the previous size of the `Table` in case is able + /// to grow the Table successfully. + /// + /// # Errors + /// + /// Returns an error if the `delta` is out of bounds for the table. + pub fn grow( + &self, + store: &mut impl AsStoreMut, + delta: u32, + init: Value, + ) -> Result { + match_rt!(on self => s { + s.grow(store, delta, init) + }) + } + + /// Copies the `len` elements of `src_table` starting at `src_index` + /// to the destination table `dst_table` at index `dst_index`. + /// + /// # Errors + /// + /// Returns an error if the range is out of bounds of either the source or + /// destination tables. + pub fn copy( + store: &mut impl AsStoreMut, + dst_table: &Self, + dst_index: u32, + src_table: &Self, + src_index: u32, + len: u32, + ) -> Result<(), RuntimeError> { + match &store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + RuntimeStore::Sys(_) => crate::rt::sys::entities::table::Table::copy( + store, + dst_table.as_sys(), + dst_index, + src_table.as_sys(), + src_index, + len, + ), + #[cfg(feature = "wamr")] + RuntimeStore::Wamr(_) => crate::rt::wamr::entities::table::Table::copy( + store, + dst_table.as_wamr(), + dst_index, + src_table.as_wamr(), + src_index, + len, + ), + #[cfg(feature = "wasmi")] + RuntimeStore::Wasmi(_) => crate::rt::wasmi::entities::table::Table::copy( + store, + dst_table.as_wasmi(), + dst_index, + src_table.as_wasmi(), + src_index, + len, + ), + + #[cfg(feature = "v8")] + RuntimeStore::V8(_) => crate::rt::v8::entities::table::Table::copy( + store, + dst_table.as_v8(), + dst_index, + src_table.as_v8(), + src_index, + len, + ), + #[cfg(feature = "js")] + RuntimeStore::Js(_) => crate::rt::js::entities::table::Table::copy( + store, + dst_table.as_js(), + dst_index, + src_table.as_js(), + src_index, + len, + ), + #[cfg(feature = "jsc")] + RuntimeStore::Jsc(_) => crate::rt::jsc::entities::table::Table::copy( + store, + dst_table.as_jsc(), + dst_index, + src_table.as_jsc(), + src_index, + len, + ), + } + } + + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, ext: VMExternTable) -> Self { + match &store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + RuntimeStore::Sys(_) => Self::Sys( + crate::rt::sys::entities::table::Table::from_vm_extern(store, ext), + ), + #[cfg(feature = "wamr")] + RuntimeStore::Wamr(_) => Self::Wamr( + crate::rt::wamr::entities::table::Table::from_vm_extern(store, ext), + ), + #[cfg(feature = "wasmi")] + RuntimeStore::Wasmi(_) => Self::Wasmi( + crate::rt::wasmi::entities::table::Table::from_vm_extern(store, ext), + ), + #[cfg(feature = "v8")] + RuntimeStore::V8(_) => Self::V8(crate::rt::v8::entities::table::Table::from_vm_extern( + store, ext, + )), + #[cfg(feature = "js")] + RuntimeStore::Js(_) => Self::Js(crate::rt::js::entities::table::Table::from_vm_extern( + store, ext, + )), + #[cfg(feature = "jsc")] + RuntimeStore::Jsc(_) => Self::Jsc( + crate::rt::jsc::entities::table::Table::from_vm_extern(store, ext), + ), + } + } + + /// Checks whether this `Table` can be used with the given context. + pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + match_rt!(on self => s { + s.is_from_store(store) + }) + } + + pub(crate) fn to_vm_extern(&self) -> VMExtern { + match_rt!(on self => s { + s.to_vm_extern() + }) + } +} + +#[cfg(test)] +mod test { + /// Check the example from . + #[test] + #[cfg_attr( + feature = "wamr", + ignore = "wamr does not support direct calls to grow table" + )] + #[cfg_attr(feature = "wasmi", ignore = "wasmi does not support funcrefs")] + #[cfg_attr( + feature = "v8", + ignore = "growing tables in v8 is not currently supported" + )] + fn table_grow_issue_3197() { + use crate::{imports, Instance, Module, Store, Table, TableType, Type, Value}; + + const WAT: &str = r#"(module (table (import "env" "table") 100 funcref))"#; + + // Tests that the table type of `table` is compatible with the export in the WAT + // This tests that `wasmer_types::types::is_table_compatible` works as expected. + let mut store = Store::default(); + let module = Module::new(&store, WAT).unwrap(); + let ty = TableType::new(Type::FuncRef, 0, None); + let table = Table::new(&mut store, ty, Value::FuncRef(None)).unwrap(); + table.grow(&mut store, 100, Value::FuncRef(None)).unwrap(); + assert_eq!(table.ty(&store).minimum, 0); + let imports = imports! {"env" => {"table" => table}}; + let _instance = Instance::new(&mut store, &module, &imports).unwrap(); + } +} diff --git a/lib/api/src/externals/table.rs b/lib/api/src/entities/table/mod.rs similarity index 55% rename from lib/api/src/externals/table.rs rename to lib/api/src/entities/table/mod.rs index 703ff1a7eb6..7d82d116603 100644 --- a/lib/api/src/externals/table.rs +++ b/lib/api/src/entities/table/mod.rs @@ -1,19 +1,14 @@ -#[cfg(feature = "wasm-c-api")] -use crate::c_api::externals::table as table_impl; -#[cfg(feature = "js")] -use crate::js::externals::table as table_impl; -#[cfg(feature = "jsc")] -use crate::jsc::externals::table as table_impl; -#[cfg(feature = "sys")] -use crate::sys::externals::table as table_impl; - -use crate::exports::{ExportError, Exportable}; -use crate::store::{AsStoreMut, AsStoreRef}; -use crate::vm::{VMExtern, VMExternTable}; -use crate::Extern; -use crate::RuntimeError; -use crate::TableType; -use crate::Value; +use wasmer_types::TableType; + +pub(crate) mod inner; +pub(crate) use inner::*; + +use crate::{ + error::RuntimeError, + store::RuntimeStore, + vm::{VMExtern, VMExternTable}, + AsStoreMut, AsStoreRef, ExportError, Exportable, Extern, StoreMut, StoreRef, Value, +}; /// A WebAssembly `table` instance. /// @@ -24,25 +19,25 @@ use crate::Value; /// mutable from both host and WebAssembly. /// /// Spec: -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, derive_more::From)] #[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))] -pub struct Table(pub(crate) table_impl::Table); +pub struct Table(pub(crate) RuntimeTable); impl Table { - /// Creates a new `Table` with the provided [`TableType`] definition. + /// Creates a new table with the provided [`TableType`] definition. /// /// All the elements in the table will be set to the `init` value. /// - /// This function will construct the `Table` using the store `BaseTunables`. + /// This function will construct the table using the store `BaseTunables`. pub fn new( store: &mut impl AsStoreMut, ty: TableType, init: Value, ) -> Result { - Ok(Self(table_impl::Table::new(store, ty, init)?)) + RuntimeTable::new(store, ty, init).map(Self) } - /// Returns the [`TableType`] of the `Table`. + /// Returns the [`TableType`] of the table. pub fn ty(&self, store: &impl AsStoreRef) -> TableType { self.0.ty(store) } @@ -100,11 +95,11 @@ impl Table { src_index: u32, len: u32, ) -> Result<(), RuntimeError> { - table_impl::Table::copy(store, &dst_table.0, dst_index, &src_table.0, src_index, len) + RuntimeTable::copy(store, &dst_table.0, dst_index, &src_table.0, src_index, len) } - pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, extern_: VMExternTable) -> Self { - Self(table_impl::Table::from_vm_extern(store, extern_)) + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, ext: VMExternTable) -> Self { + Self(RuntimeTable::from_vm_extern(store, ext)) } /// Checks whether this `Table` can be used with the given context. @@ -117,41 +112,42 @@ impl Table { } } -impl std::cmp::Eq for Table {} - impl<'a> Exportable<'a> for Table { - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { - match _extern { + fn get_self_from_extern(ext: &'a Extern) -> Result<&'a Self, ExportError> { + match ext { Extern::Table(table) => Ok(table), _ => Err(ExportError::IncompatibleType), } } } -/// Check the example from . -#[test] -#[cfg_attr( - feature = "wamr", - ignore = "wamr does not support direct calls to grow table" -)] -#[cfg_attr(feature = "wasmi", ignore = "wasmi does not support funcrefs")] -#[cfg_attr( - feature = "v8", - ignore = "growing tables in v8 is not currently supported" -)] -fn test_table_grow_issue_3197() { - use crate::{imports, Instance, Module, Store, Table, TableType, Type, Value}; - - const WAT: &str = r#"(module (table (import "env" "table") 100 funcref))"#; - - // Tests that the table type of `table` is compatible with the export in the WAT - // This tests that `wasmer_types::types::is_table_compatible` works as expected. - let mut store = Store::default(); - let module = Module::new(&store, WAT).unwrap(); - let ty = TableType::new(Type::FuncRef, 0, None); - let table = Table::new(&mut store, ty, Value::FuncRef(None)).unwrap(); - table.grow(&mut store, 100, Value::FuncRef(None)).unwrap(); - assert_eq!(table.ty(&store).minimum, 0); - let imports = imports! {"env" => {"table" => table}}; - let _instance = Instance::new(&mut store, &module, &imports).unwrap(); +#[cfg(test)] +mod test { + /// Check the example from . + #[test] + #[cfg_attr( + feature = "wamr", + ignore = "wamr does not support direct calls to grow table" + )] + #[cfg_attr(feature = "wasmi", ignore = "wasmi does not support funcrefs")] + #[cfg_attr( + feature = "v8", + ignore = "growing tables in v8 is not currently supported" + )] + fn table_grow_issue_3197() { + use crate::{imports, Instance, Module, Store, Table, TableType, Type, Value}; + + const WAT: &str = r#"(module (table (import "env" "table") 100 funcref))"#; + + // Tests that the table type of `table` is compatible with the export in the WAT + // This tests that `wasmer_types::types::is_table_compatible` works as expected. + let mut store = Store::default(); + let module = Module::new(&store, WAT).unwrap(); + let ty = TableType::new(Type::FuncRef, 0, None); + let table = Table::new(&mut store, ty, Value::FuncRef(None)).unwrap(); + table.grow(&mut store, 100, Value::FuncRef(None)).unwrap(); + assert_eq!(table.ty(&store).minimum, 0); + let imports = imports! {"env" => {"table" => table}}; + let _instance = Instance::new(&mut store, &module, &imports).unwrap(); + } } diff --git a/lib/api/src/entities/trap.rs b/lib/api/src/entities/trap.rs new file mode 100644 index 00000000000..c67f1b4c981 --- /dev/null +++ b/lib/api/src/entities/trap.rs @@ -0,0 +1,103 @@ +use std::{any::Any, error::Error, fmt::Debug}; + +use crate::{macros::rt::match_rt, RuntimeError}; + +/// An enumeration of all the trap kinds supported by the runtimes. +#[derive(Debug, derive_more::From)] +pub enum RuntimeTrap { + #[cfg(feature = "sys")] + /// The trap from the `sys` runtime. + Sys(crate::rt::sys::vm::Trap), + + #[cfg(feature = "wamr")] + /// The trap from the `wamr` runtime. + Wamr(crate::rt::wamr::vm::Trap), + + #[cfg(feature = "wasmi")] + /// The trap from the `wasmi` runtime. + Wasmi(crate::rt::wasmi::vm::Trap), + + #[cfg(feature = "v8")] + /// The trap from the `v8` runtime. + V8(crate::rt::v8::vm::Trap), + + #[cfg(feature = "js")] + /// The trap from the `js` runtime. + Js(crate::rt::js::vm::Trap), + + #[cfg(feature = "jsc")] + /// The trap from the `jsc` runtime. + Jsc(crate::rt::jsc::vm::Trap), +} + +impl RuntimeTrap { + /// Construct a new Error with the given a user error. + /// + /// Internally saves a backtrace when constructed. + pub fn user(err: Box) -> RuntimeError { + #[cfg(feature = "sys")] + { + return crate::rt::sys::vm::Trap::user(err).into(); + } + #[cfg(feature = "wamr")] + { + return crate::rt::wamr::vm::Trap::user(err).into(); + } + + #[cfg(feature = "wasmi")] + { + return crate::rt::wasmi::vm::Trap::user(err).into(); + } + + #[cfg(feature = "v8")] + { + return crate::rt::v8::vm::Trap::user(err).into(); + } + #[cfg(feature = "js")] + { + return crate::rt::js::vm::Trap::user(err).into(); + } + #[cfg(feature = "jsc")] + { + return crate::rt::jsc::vm::Trap::user(err).into(); + } + + panic!("No runtime enabled!") + } + /// Attempts to downcast the `Trap` to a concrete type. + pub fn downcast(self) -> Result { + match_rt!(on self => s { + s.downcast::().map_err(Into::into) + }) + } + + /// Attempts to downcast the `Trap` to a concrete type. + pub fn downcast_ref(&self) -> Option<&T> { + match_rt!(on self => s { + s.downcast_ref::() + }) + } + + /// Returns true if the `Trap` is the same as T + pub fn is(&self) -> bool { + match_rt!(on self => s { + s.is::() + }) + } +} + +impl std::fmt::Display for RuntimeTrap { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match_rt!(on self => s { + (s as &dyn std::fmt::Display).fmt(f) + }) + } +} + +impl std::error::Error for RuntimeTrap { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match_rt!(on self => s { + s.source() + }) + } +} diff --git a/lib/api/src/value.rs b/lib/api/src/entities/value.rs similarity index 75% rename from lib/api/src/value.rs rename to lib/api/src/entities/value.rs index 8e4c18b790e..e439a99668b 100644 --- a/lib/api/src/value.rs +++ b/lib/api/src/entities/value.rs @@ -1,12 +1,10 @@ -use crate::store::AsStoreRef; -use crate::vm::{VMExternRef, VMFuncRef}; -use crate::ExternRef; -use crate::Function; -use std::convert::TryFrom; -use std::fmt; -use wasmer_types::Type; +use wasmer_types::{RawValue, Type}; -pub use wasmer_types::RawValue; +use crate::{ + entities::{ExternRef, Function}, + vm::{VMExternRef, VMFuncRef}, + AsStoreRef, +}; /// WebAssembly computations manipulate values of basic value types: /// * Integers (32 or 64 bit width) @@ -72,7 +70,7 @@ impl Value { Self::ExternRef(None) } - /// Returns the corresponding [`Type`] for this `Value`. + /// Returns the corresponding [`Type`] for this [`Value`]. pub fn ty(&self) -> Type { match self { Self::I32(_) => Type::I32, @@ -94,7 +92,6 @@ impl Value { Self::F64(f64) => RawValue { f64 }, Self::V128(u128) => RawValue { u128 }, Self::FuncRef(Some(ref f)) => f.vm_funcref(store).into_raw(), - Self::FuncRef(None) => RawValue { funcref: 0 }, Self::ExternRef(Some(ref e)) => e.vm_externref().into_raw(), Self::ExternRef(None) => RawValue { externref: 0 }, @@ -105,19 +102,95 @@ impl Value { /// /// # Safety /// - pub unsafe fn from_raw(store: &mut impl crate::AsStoreMut, ty: Type, raw: RawValue) -> Self { + pub unsafe fn from_raw( + store: &mut impl crate::entities::store::AsStoreMut, + ty: Type, + raw: RawValue, + ) -> Self { match ty { Type::I32 => Self::I32(raw.i32), Type::I64 => Self::I64(raw.i64), Type::F32 => Self::F32(raw.f32), Type::F64 => Self::F64(raw.f64), Type::V128 => Self::V128(raw.u128), - Type::FuncRef => { - Self::FuncRef(VMFuncRef::from_raw(raw).map(|f| Function::from_vm_funcref(store, f))) - } - Type::ExternRef => Self::ExternRef( - VMExternRef::from_raw(raw).map(|e| ExternRef::from_vm_externref(store, e)), - ), + Type::FuncRef => match store.as_store_ref().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(_) => Self::FuncRef( + crate::rt::sys::vm::VMFuncRef::from_raw(raw) + .map(VMFuncRef::Sys) + .map(|f| Function::from_vm_funcref(store, f)), + ), + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(_) => Self::FuncRef( + crate::rt::wamr::vm::VMFuncRef::from_raw(raw) + .map(VMFuncRef::Wamr) + .map(|f| Function::from_vm_funcref(store, f)), + ), + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(_) => Self::FuncRef( + crate::rt::wasmi::vm::VMFuncRef::from_raw(raw) + .map(VMFuncRef::Wasmi) + .map(|f| Function::from_vm_funcref(store, f)), + ), + + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(_) => Self::FuncRef( + crate::rt::v8::vm::VMFuncRef::from_raw(raw) + .map(VMFuncRef::V8) + .map(|f| Function::from_vm_funcref(store, f)), + ), + #[cfg(feature = "js")] + crate::RuntimeStore::Js(_) => Self::FuncRef( + crate::rt::js::vm::VMFuncRef::from_raw(raw) + .map(VMFuncRef::Js) + .map(|f| Function::from_vm_funcref(store, f)), + ), + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(_) => Self::FuncRef( + crate::rt::jsc::vm::VMFuncRef::from_raw(raw) + .map(VMFuncRef::Jsc) + .map(|f| Function::from_vm_funcref(store, f)), + ), + }, + Type::ExternRef => match store.as_store_ref().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(_) => Self::ExternRef( + crate::rt::sys::vm::VMExternRef::from_raw(raw) + .map(VMExternRef::Sys) + .map(|f| ExternRef::from_vm_externref(store, f)), + ), + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(_) => Self::ExternRef( + crate::rt::wamr::vm::VMExternRef::from_raw(raw) + .map(VMExternRef::Wamr) + .map(|f| ExternRef::from_vm_externref(store, f)), + ), + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(_) => Self::ExternRef( + crate::rt::wasmi::vm::VMExternRef::from_raw(raw) + .map(VMExternRef::Wasmi) + .map(|f| ExternRef::from_vm_externref(store, f)), + ), + + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(_) => Self::ExternRef( + crate::rt::v8::vm::VMExternRef::from_raw(raw) + .map(VMExternRef::V8) + .map(|f| ExternRef::from_vm_externref(store, f)), + ), + #[cfg(feature = "js")] + crate::RuntimeStore::Js(_) => Self::ExternRef( + crate::rt::js::vm::VMExternRef::from_raw(raw) + .map(VMExternRef::Js) + .map(|f| ExternRef::from_vm_externref(store, f)), + ), + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(_) => Self::ExternRef( + crate::rt::jsc::vm::VMExternRef::from_raw(raw) + .map(VMExternRef::Jsc) + .map(|f| ExternRef::from_vm_externref(store, f)), + ), + }, } } @@ -154,8 +227,8 @@ impl Value { } } -impl fmt::Debug for Value { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl std::fmt::Debug for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::I32(v) => write!(f, "I32({:?})", v), Self::I64(v) => write!(f, "I64({:?})", v), @@ -170,8 +243,8 @@ impl fmt::Debug for Value { } } -impl fmt::Display for Value { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl std::fmt::Display for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", diff --git a/lib/api/src/errors.rs b/lib/api/src/error.rs similarity index 93% rename from lib/api/src/errors.rs rename to lib/api/src/error.rs index 084160f9ae8..59509474b3d 100644 --- a/lib/api/src/errors.rs +++ b/lib/api/src/error.rs @@ -1,17 +1,8 @@ -#[cfg(feature = "wasm-c-api")] -use crate::c_api::trap::Trap; -#[cfg(feature = "js")] -use crate::js::trap::Trap; -#[cfg(feature = "jsc")] -use crate::jsc::trap::Trap; -use std::fmt; use std::sync::Arc; use thiserror::Error; -use wasmer_types::{FrameInfo, TrapCode}; -#[cfg(feature = "sys")] -use wasmer_vm::Trap; +use wasmer_types::{FrameInfo, ImportError, TrapCode}; -use wasmer_types::ImportError; +use crate::RuntimeTrap as Trap; /// The WebAssembly.LinkError object indicates an error during /// module instantiation (besides traps from the start function). @@ -88,8 +79,8 @@ impl RuntimeStringError { } } -impl fmt::Display for RuntimeStringError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl std::fmt::Display for RuntimeStringError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.details) } } @@ -209,8 +200,8 @@ impl RuntimeError { } } -impl fmt::Debug for RuntimeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl std::fmt::Debug for RuntimeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("RuntimeError") .field("source", &self.inner.source) .field("wasm_trace", &self.inner.wasm_trace) @@ -218,8 +209,8 @@ impl fmt::Debug for RuntimeError { } } -impl fmt::Display for RuntimeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl std::fmt::Display for RuntimeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "RuntimeError: {}", self.message())?; let trace = self.trace(); if trace.is_empty() { @@ -260,7 +251,7 @@ impl From> for RuntimeError { match error.downcast::() { // The error is already a RuntimeError, we return it directly Ok(runtime_error) => *runtime_error, - Err(error) => Trap::user(error).into(), + Err(error) => Trap::user(error), } } } diff --git a/lib/api/src/externals/memory.rs b/lib/api/src/externals/memory.rs deleted file mode 100644 index 24d2399a22c..00000000000 --- a/lib/api/src/externals/memory.rs +++ /dev/null @@ -1,366 +0,0 @@ -#[cfg(feature = "wasm-c-api")] -use crate::c_api::externals::memory as memory_impl; -#[cfg(feature = "js")] -use crate::js::externals::memory as memory_impl; -#[cfg(feature = "jsc")] -use crate::jsc::externals::memory as memory_impl; -#[cfg(feature = "sys")] -use crate::sys::externals::memory as memory_impl; - -use super::memory_view::MemoryView; -use crate::exports::{ExportError, Exportable}; -use crate::store::{AsStoreMut, AsStoreRef}; -use crate::vm::{VMExtern, VMExternMemory, VMMemory}; -use crate::MemoryAccessError; -use crate::MemoryType; -use crate::{AtomicsError, Extern}; -use std::mem::MaybeUninit; -use wasmer_types::{MemoryError, Pages}; - -/// A WebAssembly `memory` instance. -/// -/// A memory instance is the runtime representation of a linear memory. -/// It consists of a vector of bytes and an optional maximum size. -/// -/// The length of the vector always is a multiple of the WebAssembly -/// page size, which is defined to be the constant 65536 – abbreviated 64Ki. -/// Like in a memory type, the maximum size in a memory instance is -/// given in units of this page size. -/// -/// A memory created by the host or in WebAssembly code will be accessible and -/// mutable from both host and WebAssembly. -/// -/// Spec: -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))] -pub struct Memory(pub(crate) memory_impl::Memory); - -impl Memory { - /// Creates a new host `Memory` from the provided [`MemoryType`]. - /// - /// This function will construct the `Memory` using the store - /// `BaseTunables`. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; - /// # let mut store = Store::default(); - /// # - /// let m = Memory::new(&mut store, MemoryType::new(1, None, false)).unwrap(); - /// ``` - pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result { - Ok(Self(memory_impl::Memory::new(store, ty)?)) - } - - /// Create a memory object from an existing memory and attaches it to the store - pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { - Self(memory_impl::Memory::new_from_existing(new_store, memory)) - } - - /// Returns the [`MemoryType`] of the `Memory`. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; - /// # let mut store = Store::default(); - /// # - /// let mt = MemoryType::new(1, None, false); - /// let m = Memory::new(&mut store, mt).unwrap(); - /// - /// assert_eq!(m.ty(&mut store), mt); - /// ``` - pub fn ty(&self, store: &impl AsStoreRef) -> MemoryType { - self.0.ty(store) - } - - /// Creates a view into the memory that then allows for - /// read and write - pub fn view<'a>(&self, store: &'a (impl AsStoreRef + ?Sized)) -> MemoryView<'a> { - MemoryView::new(self, store) - } - - /// Grow memory by the specified amount of WebAssembly [`Pages`] and return - /// the previous memory size. - /// - /// # Example - /// - /// ``` - /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES}; - /// # let mut store = Store::default(); - /// # - /// let m = Memory::new(&mut store, MemoryType::new(1, Some(3), false)).unwrap(); - /// let p = m.grow(&mut store, 2).unwrap(); - /// - /// assert_eq!(p, Pages(1)); - /// assert_eq!(m.view(&mut store).size(), Pages(3)); - /// ``` - /// - /// # Errors - /// - /// Returns an error if memory can't be grown by the specified amount - /// of pages. - /// - /// ```should_panic - /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES}; - /// # use wasmer::FunctionEnv; - /// # let mut store = Store::default(); - /// # let env = FunctionEnv::new(&mut store, ()); - /// # - /// let m = Memory::new(&mut store, MemoryType::new(1, Some(1), false)).unwrap(); - /// - /// // This results in an error: `MemoryError::CouldNotGrow`. - /// let s = m.grow(&mut store, 1).unwrap(); - /// ``` - pub fn grow( - &self, - store: &mut impl AsStoreMut, - delta: IntoPages, - ) -> Result - where - IntoPages: Into, - { - self.0.grow(store, delta) - } - - /// Grows the memory to at least a minimum size. If the memory is already big enough - /// for the min size then this function does nothing - pub fn grow_at_least( - &self, - store: &mut impl AsStoreMut, - min_size: u64, - ) -> Result<(), MemoryError> { - self.0.grow_at_least(store, min_size) - } - - /// Resets the memory back to zero length - pub fn reset(&self, store: &mut impl AsStoreMut) -> Result<(), MemoryError> { - self.0.reset(store)?; - Ok(()) - } - - /// Attempts to duplicate this memory (if its clonable) in a new store - /// (copied memory) - pub fn copy_to_store( - &self, - store: &impl AsStoreRef, - new_store: &mut impl AsStoreMut, - ) -> Result { - if !self.ty(store).shared { - // We should only be able to duplicate in a new store if the memory is shared - return Err(MemoryError::InvalidMemory { - reason: "memory is not a shared memory type".to_string(), - }); - } - self.0 - .try_copy(&store) - .map(|new_memory| Self::new_from_existing(new_store, new_memory.into())) - } - - pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternMemory) -> Self { - Self(memory_impl::Memory::from_vm_extern(store, vm_extern)) - } - - /// Checks whether this `Memory` can be used with the given context. - pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { - self.0.is_from_store(store) - } - - /// Attempts to clone this memory (if its clonable) - pub fn try_clone(&self, store: &impl AsStoreRef) -> Result { - self.0.try_clone(store) - } - - /// Attempts to clone this memory (if its clonable) in a new store - /// (cloned memory will be shared between those that clone it) - pub fn share_in_store( - &self, - store: &impl AsStoreRef, - new_store: &mut impl AsStoreMut, - ) -> Result { - if !self.ty(store).shared { - // We should only be able to duplicate in a new store if the memory is shared - return Err(MemoryError::InvalidMemory { - reason: "memory is not a shared memory type".to_string(), - }); - } - self.0 - .try_clone(&store) - .map(|new_memory| Self::new_from_existing(new_store, new_memory)) - } - - /// Get a [`SharedMemory`]. - /// - /// Only returns `Some(_)` if the memory is shared, and if the target - /// backend supports shared memory operations. - /// - /// See [`SharedMemory`] and its methods for more information. - pub fn as_shared(&self, store: &impl AsStoreRef) -> Option { - if !self.ty(store).shared { - return None; - } - self.0.as_shared(store) - } - - /// To `VMExtern`. - pub(crate) fn to_vm_extern(&self) -> VMExtern { - self.0.to_vm_extern() - } -} - -impl std::cmp::Eq for Memory {} - -impl<'a> Exportable<'a> for Memory { - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { - match _extern { - Extern::Memory(memory) => Ok(memory), - _ => Err(ExportError::IncompatibleType), - } - } -} - -/// Location in a WebAssembly memory. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct MemoryLocation { - // NOTE: must be expanded to an enum that also supports 64bit memory in - // the future - // That's why this is private. - pub(crate) address: u32, -} - -impl MemoryLocation { - /// Create a new memory location for a 32bit memory. - pub fn new_32(address: u32) -> Self { - Self { address } - } -} - -impl From for MemoryLocation { - fn from(value: u32) -> Self { - Self::new_32(value) - } -} - -/// See [`SharedMemory`]. -pub(crate) trait SharedMemoryOps { - /// See [`SharedMemory::disable_atomics`]. - fn disable_atomics(&self) -> Result<(), MemoryError> { - Err(MemoryError::AtomicsNotSupported) - } - - /// See [`SharedMemory::wake_all_atomic_waiters`]. - fn wake_all_atomic_waiters(&self) -> Result<(), MemoryError> { - Err(MemoryError::AtomicsNotSupported) - } - - /// See [`SharedMemory::notify`]. - fn notify(&self, _dst: MemoryLocation, _count: u32) -> Result { - Err(AtomicsError::Unimplemented) - } - - /// See [`SharedMemory::wait`]. - fn wait( - &self, - _dst: MemoryLocation, - _timeout: Option, - ) -> Result { - Err(AtomicsError::Unimplemented) - } -} - -/// A handle that exposes operations only relevant for shared memories. -/// -/// Enables interaction independent from the [`crate::Store`], and thus allows calling -/// some methods an instane is running. -/// -/// **NOTE**: Not all methods are supported by all backends. -#[derive(Clone)] -pub struct SharedMemory { - memory: Memory, - ops: std::sync::Arc, -} - -impl std::fmt::Debug for SharedMemory { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("SharedMemory").finish() - } -} - -impl SharedMemory { - /// Get the underlying memory. - pub fn memory(&self) -> &Memory { - &self.memory - } - - /// Create a new handle from ops. - #[allow(unused)] - pub(crate) fn new(memory: Memory, ops: impl SharedMemoryOps + Send + Sync + 'static) -> Self { - Self { - memory, - ops: std::sync::Arc::new(ops), - } - } - - /// Notify up to `count` waiters waiting for the memory location. - pub fn notify(&self, location: MemoryLocation, count: u32) -> Result { - self.ops.notify(location, count) - } - - /// Wait for the memory location to be notified. - pub fn wait( - &self, - location: MemoryLocation, - timeout: Option, - ) -> Result { - self.ops.wait(location, timeout) - } - - /// Disable atomics for this memory. - /// - /// All subsequent atomic wait calls will produce a trap. - /// - /// This can be used or forced shutdown of instances that continuously try - /// to wait on atomics. - /// - /// NOTE: this operation might not be supported by all memory implementations. - /// In that case, this function will return an error. - pub fn disable_atomics(&self) -> Result<(), MemoryError> { - self.ops.disable_atomics() - } - - /// Wake up all atomic waiters. - /// - /// This can be used to force-resume waiting execution. - /// - /// NOTE: this operation might not be supported by all memory implementations. - /// In that case, this function will return an error. - pub fn wake_all_atomic_waiters(&self) -> Result<(), MemoryError> { - self.ops.wake_all_atomic_waiters() - } -} - -/// Underlying buffer for a memory. -#[derive(Debug, Copy, Clone)] -pub(crate) struct MemoryBuffer<'a>(pub(crate) memory_impl::MemoryBuffer<'a>); - -impl<'a> MemoryBuffer<'a> { - #[allow(unused)] - pub(crate) fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> { - self.0.read(offset, buf) - } - - #[allow(unused)] - pub(crate) fn read_uninit<'b>( - &self, - offset: u64, - buf: &'b mut [MaybeUninit], - ) -> Result<&'b mut [u8], MemoryAccessError> { - self.0.read_uninit(offset, buf) - } - - #[allow(unused)] - pub(crate) fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { - self.0.write(offset, data) - } -} diff --git a/lib/api/src/instance.rs b/lib/api/src/instance.rs deleted file mode 100644 index 42ae1949e3e..00000000000 --- a/lib/api/src/instance.rs +++ /dev/null @@ -1,115 +0,0 @@ -use crate::exports::Exports; -use crate::module::Module; -use crate::{Extern, InstantiationError}; -use std::fmt; - -use crate::imports::Imports; -use crate::store::AsStoreMut; - -#[cfg(feature = "wasm-c-api")] -use crate::c_api::instance as instance_imp; -#[cfg(feature = "js")] -use crate::js::instance as instance_imp; -#[cfg(feature = "jsc")] -use crate::jsc::instance as instance_imp; -#[cfg(feature = "sys")] -use crate::sys::instance as instance_imp; - -/// A WebAssembly Instance is a stateful, executable -/// instance of a WebAssembly [`Module`]. -/// -/// Instance objects contain all the exported WebAssembly -/// functions, memories, tables and globals that allow -/// interacting with WebAssembly. -/// -/// Spec: -#[derive(Clone, PartialEq, Eq)] -pub struct Instance { - pub(crate) _inner: instance_imp::Instance, - pub(crate) module: Module, - /// The exports for an instance. - pub exports: Exports, -} - -impl Instance { - /// Creates a new `Instance` from a WebAssembly [`Module`] and a - /// set of imports using [`Imports`] or the [`imports!`] macro helper. - /// - /// [`imports!`]: crate::imports! - /// [`Imports!`]: crate::Imports! - /// - /// ``` - /// # use wasmer::{imports, Store, Module, Global, Value, Instance}; - /// # use wasmer::FunctionEnv; - /// # fn main() -> anyhow::Result<()> { - /// let mut store = Store::default(); - /// let env = FunctionEnv::new(&mut store, ()); - /// let module = Module::new(&store, "(module)")?; - /// let imports = imports!{ - /// "host" => { - /// "var" => Global::new(&mut store, Value::I32(2)) - /// } - /// }; - /// let instance = Instance::new(&mut store, &module, &imports)?; - /// # Ok(()) - /// # } - /// ``` - /// - /// ## Errors - /// - /// The function can return [`InstantiationError`]s. - /// - /// Those are, as defined by the spec: - /// * Link errors that happen when plugging the imports into the instance - /// * Runtime errors that happen when running the module `start` function. - #[allow(clippy::result_large_err)] - pub fn new( - store: &mut impl AsStoreMut, - module: &Module, - imports: &Imports, - ) -> Result { - let (_inner, exports) = instance_imp::Instance::new(store, module, imports)?; - Ok(Self { - _inner, - module: module.clone(), - exports, - }) - } - - /// Creates a new `Instance` from a WebAssembly [`Module`] and a - /// vector of imports. - /// - /// ## Errors - /// - /// The function can return [`InstantiationError`]s. - /// - /// Those are, as defined by the spec: - /// * Link errors that happen when plugging the imports into the instance - /// * Runtime errors that happen when running the module `start` function. - #[allow(clippy::result_large_err)] - pub fn new_by_index( - store: &mut impl AsStoreMut, - module: &Module, - externs: &[Extern], - ) -> Result { - let (_inner, exports) = instance_imp::Instance::new_by_index(store, module, externs)?; - Ok(Self { - _inner, - module: module.clone(), - exports, - }) - } - - /// Gets the [`Module`] associated with this instance. - pub fn module(&self) -> &Module { - &self.module - } -} - -impl fmt::Debug for Instance { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Instance") - .field("exports", &self.exports) - .finish() - } -} diff --git a/lib/api/src/js/engine.rs b/lib/api/src/js/engine.rs deleted file mode 100644 index b56d104ec12..00000000000 --- a/lib/api/src/js/engine.rs +++ /dev/null @@ -1,21 +0,0 @@ -/// A WebAssembly `Universal` Engine. -#[derive(Clone, Debug)] -pub struct Engine; - -impl Engine { - pub(crate) fn deterministic_id(&self) -> &str { - // All js engines have the same id - "js-generic" - } -} - -impl Default for Engine { - fn default() -> Self { - Engine - } -} - -/// Returns the default engine for the JS engine -pub(crate) fn default_engine() -> Engine { - Engine::default() -} diff --git a/lib/api/src/js/errors.rs b/lib/api/src/js/errors.rs deleted file mode 100644 index b4f6e02049f..00000000000 --- a/lib/api/src/js/errors.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::js::trap::Trap; -use crate::RuntimeError; -use wasm_bindgen::prelude::*; - -impl From for RuntimeError { - fn from(trap: Trap) -> Self { - if trap.is::() { - return trap.downcast::().unwrap(); - } - let wasm_trace = vec![]; - let trap_code = None; - // let (wasm_trace, trap_code) = wasmer_compiler::get_trace_and_trapcode(&trap); - RuntimeError::new_from_source(trap, wasm_trace, trap_code) - } -} - -impl From for JsValue { - fn from(_err: RuntimeError) -> Self { - // err.inner.source.into() - unimplemented!(); - } -} - -pub(crate) fn raise(error: Box) -> ! { - let error = Trap::user(error); - let js_error: JsValue = error.into(); - wasm_bindgen::throw_val(js_error) -} diff --git a/lib/api/src/js/externals/mod.rs b/lib/api/src/js/externals/mod.rs deleted file mode 100644 index 3575fe23bb5..00000000000 --- a/lib/api/src/js/externals/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub(crate) mod function; -pub(crate) mod global; -pub(crate) mod memory; -pub(crate) mod memory_view; -pub(crate) mod table; diff --git a/lib/api/src/js/mem_access.rs b/lib/api/src/js/mem_access.rs deleted file mode 100644 index f8a822d1325..00000000000 --- a/lib/api/src/js/mem_access.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::access::{RefCow, SliceCow, WasmRefAccess, WasmSliceAccess}; -use crate::{MemoryAccessError, WasmRef, WasmSlice}; -use std::mem::{self, MaybeUninit}; -use std::slice; - -impl<'a, T> WasmSliceAccess<'a, T> -where - T: wasmer_types::ValueType, -{ - pub(crate) fn new(slice: WasmSlice<'a, T>) -> Result { - let buf = slice.read_to_vec()?; - Ok(Self { - slice, - buf: SliceCow::Owned(buf, false), - }) - } -} - -impl<'a, T> WasmRefAccess<'a, T> -where - T: wasmer_types::ValueType, -{ - pub(crate) fn new(ptr: WasmRef<'a, T>) -> Result { - let mut out = MaybeUninit::uninit(); - let buf = - unsafe { slice::from_raw_parts_mut(out.as_mut_ptr() as *mut u8, mem::size_of::()) }; - ptr.buffer.read(ptr.offset, buf)?; - let val = unsafe { out.assume_init() }; - - Ok(Self { - ptr, - buf: RefCow::Owned(val, false), - }) - } -} - -impl<'a, T> WasmRefAccess<'a, T> -where - T: wasmer_types::ValueType, -{ - /// Reads the address pointed to by this `WasmPtr` in a memory. - #[inline] - #[allow(clippy::clone_on_copy)] - pub fn read(&self) -> T - where - T: Clone, - { - self.as_ref().clone() - } - - /// Writes to the address pointed to by this `WasmPtr` in a memory. - #[inline] - pub fn write(&mut self, val: T) { - let mut data = MaybeUninit::new(val); - let data = unsafe { - slice::from_raw_parts_mut( - data.as_mut_ptr() as *mut MaybeUninit, - mem::size_of::(), - ) - }; - val.zero_padding_bytes(data); - let data = unsafe { slice::from_raw_parts(data.as_ptr() as *const _, data.len()) }; - self.ptr.buffer.write(self.ptr.offset, data).unwrap() - } -} diff --git a/lib/api/src/js/mod.rs b/lib/api/src/js/mod.rs deleted file mode 100644 index 0584da25f29..00000000000 --- a/lib/api/src/js/mod.rs +++ /dev/null @@ -1,41 +0,0 @@ -#[cfg(all(feature = "std", feature = "core"))] -compile_error!( - "The `std` and `core` features are both enabled, which is an error. Please enable only once." -); - -#[cfg(all(not(feature = "std"), not(feature = "core")))] -compile_error!("Both the `std` and `core` features are disabled. Please enable one of them."); - -#[cfg(feature = "core")] -pub(crate) extern crate alloc; - -mod lib { - #[cfg(feature = "core")] - pub mod std { - pub use crate::alloc::{borrow, boxed, str, string, sync, vec}; - pub use core::fmt; - pub use hashbrown as collections; - } - - #[cfg(feature = "std")] - pub mod std { - pub use std::{borrow, boxed, collections, fmt, str, string, sync, vec}; - } -} - -mod as_js; -pub(crate) mod engine; -pub(crate) mod errors; -pub(crate) mod extern_ref; -pub(crate) mod externals; -pub(crate) mod instance; -mod js_handle; -pub(crate) mod mem_access; -pub(crate) mod module; -pub(crate) mod store; -pub(crate) mod trap; -pub(crate) mod typed_function; -pub(crate) mod vm; -mod wasm_bindgen_polyfill; - -pub use self::{as_js::AsJs, js_handle::current_thread_id, module::ModuleTypeHints}; diff --git a/lib/api/src/js/store.rs b/lib/api/src/js/store.rs deleted file mode 100644 index 035f880f2d3..00000000000 --- a/lib/api/src/js/store.rs +++ /dev/null @@ -1,289 +0,0 @@ -use crate::engine::{AsEngineRef, Engine, EngineRef}; - -pub(crate) use objects::{InternalStoreHandle, StoreObject}; -pub use objects::{StoreHandle, StoreObjects}; - -#[derive(Debug)] -pub(crate) struct Store { - pub(crate) engine: Engine, -} - -impl Store { - pub(crate) fn new(engine: Engine) -> Self { - Self { engine } - } - - pub(crate) fn engine(&self) -> &Engine { - &self.engine - } -} - -impl AsEngineRef for Store { - fn as_engine_ref(&self) -> EngineRef<'_> { - EngineRef::new(&self.engine) - } -} - -mod objects { - use std::{fmt, marker::PhantomData, num::NonZeroUsize}; - - use wasm_bindgen::JsValue; - - use crate::js::vm::{VMFunctionEnvironment, VMGlobal}; - - pub use wasmer_types::StoreId; - - /// Trait to represent an object managed by a context. This is implemented on - /// the VM types managed by the context. - pub trait StoreObject: Sized { - fn list(store: &StoreObjects) -> &Vec; - fn list_mut(store: &mut StoreObjects) -> &mut Vec; - } - - macro_rules! impl_store_object { - ($($field:ident => $ty:ty,)*) => { - $( - impl StoreObject for $ty { - fn list(store: &StoreObjects) -> &Vec { - &store.$field - } - fn list_mut(store: &mut StoreObjects) -> &mut Vec { - &mut store.$field - } - } - )* - }; -} - - impl_store_object! { - // Note: we store the globals in order to be able to access them later via - // `StoreObjects::iter_globals`. - globals => VMGlobal, - // functions => VMFunction, - // tables => VMTable, - // memories => VMMemory, - // The function environments are the only things attached to a store, - // since the other JS objects (table, globals, memory and functions) - // live in the JS VM Store by default - function_environments => VMFunctionEnvironment, - } - - /// Set of objects managed by a context. - #[derive(Default, Debug)] - pub struct StoreObjects { - id: StoreId, - globals: Vec, - function_environments: Vec, - } - - impl StoreObjects { - /// Returns the ID of this context. - pub fn id(&self) -> StoreId { - self.id - } - - /// Sets the ID of this store - pub fn set_id(&mut self, id: StoreId) { - self.id = id; - } - - /// Returns a pair of mutable references from two handles. - /// - /// Panics if both handles point to the same object. - pub fn get_2_mut( - &mut self, - a: InternalStoreHandle, - b: InternalStoreHandle, - ) -> (&mut T, &mut T) { - assert_ne!(a.index(), b.index()); - let list = T::list_mut(self); - if a.index() < b.index() { - let (low, high) = list.split_at_mut(b.index()); - (&mut low[a.index()], &mut high[0]) - } else { - let (low, high) = list.split_at_mut(a.index()); - (&mut high[0], &mut low[a.index()]) - } - } - - /// Return an immutable iterator over all globals - pub fn iter_globals(&self) -> core::slice::Iter { - self.globals.iter() - } - - /// Return an vector of all globals and converted to u128 - pub fn as_u128_globals(&self) -> Vec { - self.iter_globals() - .map(|v| v.global.value().as_f64().unwrap() as u128) - .collect() - } - - /// Set a global, at index idx. Will panic if idx is out of range - /// Safety: the caller should check that the raw value is compatible - /// with destination VMGlobal type - pub fn set_global_unchecked(&self, idx: usize, new_val: u128) { - assert!(idx < self.globals.len()); - - let g = &self.globals[idx].global; - let cur_val = g.value().as_f64().unwrap(); - let new_val = new_val as f64; - if cur_val != new_val { - let new_value = JsValue::from(new_val); - g.set_value(&new_value); - } - } - } - - /// Handle to an object managed by a context. - /// - /// Internally this is just an integer index into a context. A reference to the - /// context must be passed in separately to access the actual object. - pub struct StoreHandle { - id: StoreId, - internal: InternalStoreHandle, - } - - impl core::cmp::PartialEq for StoreHandle { - fn eq(&self, other: &Self) -> bool { - self.id == other.id - } - } - - impl std::hash::Hash for StoreHandle { - fn hash(&self, state: &mut H) { - self.id.hash(state); - self.internal.idx.hash(state); - } - } - - impl Clone for StoreHandle { - fn clone(&self) -> Self { - Self { - id: self.id, - internal: self.internal, - } - } - } - - impl fmt::Debug for StoreHandle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StoreHandle") - .field("id", &self.id) - .field("internal", &self.internal.index()) - .finish() - } - } - - impl StoreHandle { - /// Moves the given object into a context and returns a handle to it. - pub fn new(store: &mut StoreObjects, val: T) -> Self { - Self { - id: store.id, - internal: InternalStoreHandle::new(store, val), - } - } - - /// Returns a reference to the object that this handle points to. - pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T { - assert_eq!(self.id, store.id, "object used with the wrong context"); - self.internal.get(store) - } - - /// Returns a mutable reference to the object that this handle points to. - pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T { - assert_eq!(self.id, store.id, "object used with the wrong context"); - self.internal.get_mut(store) - } - - /// Returns the internal handle contains within this handle. - pub fn internal_handle(&self) -> InternalStoreHandle { - self.internal - } - - /// Returns the ID of the context associated with the handle. - #[allow(unused)] - pub fn store_id(&self) -> StoreId { - self.id - } - - /// Overrides the store id with a new ID - #[allow(unused)] - pub fn set_store_id(&mut self, id: StoreId) { - self.id = id; - } - - /// Constructs a `StoreHandle` from a `StoreId` and an `InternalStoreHandle`. - /// - /// # Safety - /// Handling `InternalStoreHandle` values is unsafe because they do not track context ID. - pub unsafe fn from_internal(id: StoreId, internal: InternalStoreHandle) -> Self { - Self { id, internal } - } - } - - /// Internal handle to an object owned by the current context. - /// - /// Unlike `StoreHandle` this does not track the context ID: it is only - /// intended to be used within objects already owned by a context. - #[repr(transparent)] - pub struct InternalStoreHandle { - // Use a NonZero here to reduce the size of Option. - idx: NonZeroUsize, - marker: PhantomData T>, - } - - impl Clone for InternalStoreHandle { - fn clone(&self) -> Self { - *self - } - } - impl Copy for InternalStoreHandle {} - - impl fmt::Debug for InternalStoreHandle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("InternalStoreHandle") - .field("idx", &self.idx) - .finish() - } - } - impl PartialEq for InternalStoreHandle { - fn eq(&self, other: &Self) -> bool { - self.idx == other.idx - } - } - impl Eq for InternalStoreHandle {} - - impl InternalStoreHandle { - /// Moves the given object into a context and returns a handle to it. - pub fn new(store: &mut StoreObjects, val: T) -> Self { - let list = T::list_mut(store); - let idx = NonZeroUsize::new(list.len() + 1).unwrap(); - list.push(val); - Self { - idx, - marker: PhantomData, - } - } - - /// Returns a reference to the object that this handle points to. - pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T { - &T::list(store)[self.idx.get() - 1] - } - - /// Returns a mutable reference to the object that this handle points to. - pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T { - &mut T::list_mut(store)[self.idx.get() - 1] - } - - pub(crate) fn index(&self) -> usize { - self.idx.get() - } - - pub(crate) fn from_index(idx: usize) -> Option { - NonZeroUsize::new(idx).map(|idx| Self { - idx, - marker: PhantomData, - }) - } - } -} diff --git a/lib/api/src/js/vm.rs b/lib/api/src/js/vm.rs deleted file mode 100644 index 316e7d21f12..00000000000 --- a/lib/api/src/js/vm.rs +++ /dev/null @@ -1,298 +0,0 @@ -//! This module is mainly used to create the `VM` types that will hold both -//! the JS values of the `Memory`, `Table`, `Global` and `Function` and also -//! it's types. -//! This module should not be needed any longer (with the exception of the memory) -//! once the type reflection is added to the WebAssembly JS API. -//! https://github.com/WebAssembly/js-types/ - -use crate::externals::{Extern, Function, Global, Memory, Table, VMExternToExtern}; -use crate::store::{AsStoreMut, AsStoreRef}; -use js_sys::{ - Function as JsFunction, - WebAssembly::{self, Memory as JsMemory, Table as JsTable}, -}; -use serde::{Deserialize, Serialize}; -use std::{any::Any, fmt}; -use tracing::trace; -use wasm_bindgen::{JsCast, JsValue}; -use wasmer_types::{ - FunctionType, GlobalType, MemoryError, MemoryType, Pages, RawValue, TableType, WASM_PAGE_SIZE, -}; - -use crate::js::{js_handle::JsHandle, wasm_bindgen_polyfill::Global as JsGlobal}; - -/// Represents linear memory that is managed by the javascript runtime -#[derive(Clone, Debug, PartialEq)] -pub struct VMMemory { - pub(crate) memory: JsHandle, - pub(crate) ty: MemoryType, -} - -unsafe impl Send for VMMemory {} -unsafe impl Sync for VMMemory {} - -#[derive(Serialize, Deserialize)] -struct DummyBuffer { - #[serde(rename = "byteLength")] - byte_length: u32, -} - -impl VMMemory { - /// Creates a new memory directly from a WebAssembly javascript object - pub fn new(memory: JsMemory, ty: MemoryType) -> Self { - Self { - memory: JsHandle::new(memory), - ty, - } - } - - /// Returns the size of the memory buffer in pages - pub fn get_runtime_size(&self) -> u32 { - let dummy: DummyBuffer = match serde_wasm_bindgen::from_value(self.memory.buffer()) { - Ok(o) => o, - Err(_) => return 0, - }; - if dummy.byte_length == 0 { - return 0; - } - dummy.byte_length / WASM_PAGE_SIZE as u32 - } - - /// Attempts to clone this memory (if its clonable) - pub(crate) fn try_clone(&self) -> Result { - Ok(self.clone()) - } - - /// Copies this memory to a new memory - pub fn copy(&mut self) -> Result { - let new_memory = crate::js::externals::memory::Memory::js_memory_from_type(&self.ty)?; - - let src = crate::js::externals::memory_view::MemoryView::new_raw(&self.memory); - let amount = src.data_size() as usize; - - trace!(%amount, "memory copy started"); - - let mut dst = crate::js::externals::memory_view::MemoryView::new_raw(&new_memory); - let dst_size = dst.data_size() as usize; - - if amount > dst_size { - let delta = amount - dst_size; - let pages = ((delta - 1) / WASM_PAGE_SIZE) + 1; - - let our_js_memory: &crate::js::externals::memory::JSMemory = - JsCast::unchecked_from_js_ref(&new_memory); - our_js_memory.grow(pages as u32).map_err(|err| { - if err.is_instance_of::() { - let cur_pages = dst_size; - MemoryError::CouldNotGrow { - current: Pages(cur_pages as u32), - attempted_delta: Pages(pages as u32), - } - } else { - MemoryError::Generic(err.as_string().unwrap()) - } - })?; - - dst = crate::js::externals::memory_view::MemoryView::new_raw(&new_memory); - } - - src.copy_to_memory(amount as u64, &dst).map_err(|err| { - wasmer_types::MemoryError::Generic(format!("failed to copy the memory - {}", err)) - })?; - - trace!("memory copy finished (size={})", dst.size().bytes().0); - - Ok(Self { - memory: JsHandle::new(new_memory), - ty: self.ty.clone(), - }) - } -} - -impl From for JsValue { - fn from(value: VMMemory) -> Self { - JsValue::from(value.memory) - } -} - -impl From for (JsValue, MemoryType) { - fn from(value: VMMemory) -> Self { - (JsValue::from(value.memory), value.ty) - } -} - -/// The shared memory is the same as the normal memory -pub type VMSharedMemory = VMMemory; - -/// The VM Global type -#[derive(Clone, Debug, PartialEq)] -pub struct VMGlobal { - pub(crate) global: JsGlobal, - pub(crate) ty: GlobalType, -} - -impl VMGlobal { - pub(crate) fn new(global: JsGlobal, ty: GlobalType) -> Self { - Self { global, ty } - } -} - -unsafe impl Send for VMGlobal {} -unsafe impl Sync for VMGlobal {} - -/// The VM Table type -#[derive(Clone, Debug, PartialEq)] -pub struct VMTable { - pub(crate) table: JsTable, - pub(crate) ty: TableType, -} - -unsafe impl Send for VMTable {} -unsafe impl Sync for VMTable {} - -impl VMTable { - pub(crate) fn new(table: JsTable, ty: TableType) -> Self { - Self { table, ty } - } - - /// Get the table size at runtime - pub fn get_runtime_size(&self) -> u32 { - self.table.length() - } -} - -/// The VM Function type -#[derive(Clone)] -pub struct VMFunction { - pub(crate) function: JsHandle, - pub(crate) ty: FunctionType, -} - -unsafe impl Send for VMFunction {} -unsafe impl Sync for VMFunction {} - -impl VMFunction { - pub(crate) fn new(function: JsFunction, ty: FunctionType) -> Self { - Self { - function: JsHandle::new(function), - ty, - } - } -} - -impl PartialEq for VMFunction { - fn eq(&self, other: &Self) -> bool { - self.function == other.function - } -} - -impl fmt::Debug for VMFunction { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("VMFunction") - .field("function", &self.function) - .finish() - } -} - -/// The value of an export passed from one instance to another. -pub enum VMExtern { - /// A function export value. - Function(VMFunction), - - /// A table export value. - Table(VMTable), - - /// A memory export value. - Memory(VMMemory), - - /// A global export value. - Global(VMGlobal), -} - -impl VMExternToExtern for VMExtern { - fn to_extern(self, store: &mut impl AsStoreMut) -> Extern { - match self { - Self::Function(f) => Extern::Function(Function::from_vm_extern(store, f)), - Self::Memory(m) => Extern::Memory(Memory::from_vm_extern(store, m)), - Self::Global(g) => Extern::Global(Global::from_vm_extern(store, g)), - Self::Table(t) => Extern::Table(Table::from_vm_extern(store, t)), - } - } -} - -pub type VMInstance = WebAssembly::Instance; - -/// Underlying FunctionEnvironment used by a `VMFunction`. -#[derive(Debug)] -pub struct VMFunctionEnvironment { - contents: Box, -} - -impl VMFunctionEnvironment { - /// Wraps the given value to expose it to Wasm code as a function context. - pub fn new(val: impl Any + Send + 'static) -> Self { - Self { - contents: Box::new(val), - } - } - - #[allow(clippy::should_implement_trait)] - /// Returns a reference to the underlying value. - pub fn as_ref(&self) -> &(dyn Any + Send + 'static) { - &*self.contents - } - - #[allow(clippy::should_implement_trait)] - /// Returns a mutable reference to the underlying value. - pub fn as_mut(&mut self) -> &mut (dyn Any + Send + 'static) { - &mut *self.contents - } -} - -pub(crate) struct VMExternRef; - -impl VMExternRef { - /// Converts the `VMExternRef` into a `RawValue`. - pub fn into_raw(self) -> RawValue { - unimplemented!(); - } - - /// Extracts a `VMExternRef` from a `RawValue`. - /// - /// # Safety - /// `raw` must be a valid `VMExternRef` instance. - pub unsafe fn from_raw(_raw: RawValue) -> Option { - unimplemented!(); - } -} - -#[repr(C)] -pub struct VMFunctionBody(u8); - -#[repr(transparent)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub(crate) struct VMFuncRef; - -impl VMFuncRef { - /// Converts the `VMFuncRef` into a `RawValue`. - pub fn into_raw(self) -> RawValue { - unimplemented!(); - } - - /// Extracts a `VMFuncRef` from a `RawValue`. - /// - /// # Safety - /// `raw.funcref` must be a valid pointer. - pub unsafe fn from_raw(_raw: RawValue) -> Option { - unimplemented!(); - } -} - -pub struct VMTrampoline; - -pub(crate) type VMExternTable = VMTable; -pub(crate) type VMExternMemory = VMMemory; -pub(crate) type VMExternGlobal = VMGlobal; -pub(crate) type VMExternFunction = VMFunction; - -pub type VMFunctionCallback = *const VMFunctionBody; diff --git a/lib/api/src/jsc/errors.rs b/lib/api/src/jsc/errors.rs deleted file mode 100644 index ffc39a10c7e..00000000000 --- a/lib/api/src/jsc/errors.rs +++ /dev/null @@ -1,23 +0,0 @@ -use rusty_jsc::JSValue; - -use crate::jsc::trap::Trap; -use crate::RuntimeError; - -impl From for RuntimeError { - fn from(trap: Trap) -> Self { - if trap.is::() { - return trap.downcast::().unwrap(); - } - let wasm_trace = vec![]; - let trap_code = None; - // let (wasm_trace, trap_code) = wasmer_compiler::get_trace_and_trapcode(&trap); - RuntimeError::new_from_source(trap, wasm_trace, trap_code) - } -} - -impl From for JSValue { - fn from(_err: RuntimeError) -> Self { - // err.inner.source.into() - unimplemented!(); - } -} diff --git a/lib/api/src/jsc/externals/function.rs b/lib/api/src/jsc/externals/function.rs deleted file mode 100644 index bfe64f5b940..00000000000 --- a/lib/api/src/jsc/externals/function.rs +++ /dev/null @@ -1,582 +0,0 @@ -use crate::errors::RuntimeError; -use crate::externals::function::{HostFunction, HostFunctionKind, WithEnv, WithoutEnv}; -use crate::function_env::{FunctionEnv, FunctionEnvMut}; -use crate::jsc::as_js::{param_from_js, AsJs}; -use crate::jsc::engine::JSC; -use crate::jsc::store::{InternalStoreHandle, StoreHandle}; -use crate::jsc::trap::Trap; -use crate::jsc::vm::{VMExtern, VMFuncRef, VMFunction, VMFunctionCallback, VMFunctionEnvironment}; -use crate::native_type::{FromToNativeWasmType, IntoResult, NativeWasmTypeInto, WasmTypeList}; -use crate::store::{AsStoreMut, AsStoreRef, StoreMut}; -use crate::value::Value; -use std::fmt; -use std::marker::PhantomData; -use std::panic::{self, AssertUnwindSafe}; - -use wasmer_types::{FunctionType, RawValue}; - -use rusty_jsc::{ - callback, callback_closure, JSContext, JSObject, JSObjectCallAsFunctionCallback, JSValue, -}; - -#[derive(Clone, PartialEq)] -pub struct Function { - pub(crate) handle: VMFunction, -} - -// Function can't be Send in js because it dosen't support `structuredClone` -// https://developer.mozilla.org/en-US/docs/Web/API/structuredClone -// unsafe impl Send for Function {} - -impl From for Function { - fn from(handle: VMFunction) -> Self { - Self { handle } - } -} - -impl Function { - /// To `VMExtern`. - pub fn to_vm_extern(&self) -> VMExtern { - VMExtern::Function(self.handle.clone()) - } - - #[allow(clippy::cast_ptr_alignment)] - pub fn new_with_env( - store: &mut impl AsStoreMut, - env: &FunctionEnv, - ty: FT, - func: F, - ) -> Self - where - FT: Into, - F: Fn(FunctionEnvMut<'_, T>, &[Value]) -> Result, RuntimeError> - + 'static - + Send - + Sync, - { - let store = store.as_store_mut(); - let context = store.jsc().context(); - let function_type = ty.into(); - - let new_function_type = function_type.clone(); - let raw_env = env.clone(); - - let callback = callback_closure!(&context, move |ctx: JSContext, - function: JSObject, - this: JSObject, - args: &[JSValue]| - -> Result { - let global = ctx.get_global_object(); - let store_ptr = global - .get_property(&ctx, "__store_ptr".to_string()) - .to_number(&ctx) - .unwrap(); - - let mut store = unsafe { StoreMut::from_raw(store_ptr as usize as *mut _) }; - - let env: FunctionEnvMut = raw_env.clone().into_mut(&mut store); - - let wasm_arguments = new_function_type - .params() - .iter() - .enumerate() - .map(|(i, param)| param_from_js(&ctx, param, &args[i])) - .collect::>(); - let results = func(env, &wasm_arguments).map_err(|e| { - let value = format!("{}", e); - JSValue::string(&ctx, value) - })?; - match new_function_type.results().len() { - 0 => Ok(JSValue::undefined(&ctx)), - 1 => Ok(results[0].as_jsvalue(&mut store)), - _ => Ok(JSObject::new_array( - &ctx, - &results - .into_iter() - .map(|result| result.as_jsvalue(&mut store)) - .collect::>(), - )? - .to_jsvalue()), - } - }); - - let vm_function = VMFunction::new(callback, function_type); - Self { - handle: vm_function, - } - } - - /// Creates a new host `Function` from a native function. - pub fn new_typed(store: &mut impl AsStoreMut, func: F) -> Self - where - F: HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync, - Args: WasmTypeList, - Rets: WasmTypeList, - { - let store = store.as_store_mut(); - let function = WasmFunction::::new(func); - let callback = function.callback(store.jsc().context()); - - let ty = function.ty(); - let vm_function = VMFunction::new(callback, ty); - Self { - handle: vm_function, - } - } - - pub fn new_typed_with_env( - store: &mut impl AsStoreMut, - env: &FunctionEnv, - func: F, - ) -> Self - where - F: HostFunction, - Args: WasmTypeList, - Rets: WasmTypeList, - { - let store = store.as_store_mut(); - let context = store.jsc().context(); - let function = WasmFunction::::new(func); - let callback = function.callback(store.jsc().context()); - - let bind = callback - .get_property(&context, "bind".to_string()) - .to_object(&context) - .unwrap(); - let callback_with_env = bind - .call( - &context, - Some(&callback), - &[ - JSValue::undefined(&context), - JSValue::number(&context, env.handle.internal_handle().index() as f64), - ], - ) - .unwrap() - .to_object(&context) - .unwrap(); - - let ty = function.ty(); - let vm_function = VMFunction::new(callback_with_env, ty); - Self { - handle: vm_function, - } - } - - pub fn ty(&self, _store: &impl AsStoreRef) -> FunctionType { - self.handle.ty.clone() - } - - pub fn call_raw( - &self, - _store: &mut impl AsStoreMut, - _params: Vec, - ) -> Result, RuntimeError> { - // There is no optimal call_raw in JSC, so we just - // simply rely the call - // self.call(store, params) - unimplemented!(); - } - - pub fn call( - &self, - store: &mut impl AsStoreMut, - params: &[Value], - ) -> Result, RuntimeError> { - let store_mut = store.as_store_mut(); - let engine = store_mut.engine(); - let context = engine.0.context(); - - let mut global = context.get_global_object(); - let store_ptr = store_mut.as_raw() as usize; - global.set_property( - &context, - "__store_ptr".to_string(), - JSValue::number(&context, store_ptr as _), - ); - - let params_list = params - .iter() - .map(|v| v.as_jsvalue(&store_mut)) - .collect::>(); - let result = { - let mut r; - // TODO: This loop is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 - loop { - let store_mut = store.as_store_mut(); - let engine = store_mut.engine(); - let context = engine.0.context(); - r = self.handle.function.call(&context, None, ¶ms_list); - if let Some(callback) = store_mut.inner.on_called.take() { - match callback(store_mut) { - Ok(wasmer_types::OnCalledAction::InvokeAgain) => { - continue; - } - Ok(wasmer_types::OnCalledAction::Finish) => { - break; - } - Ok(wasmer_types::OnCalledAction::Trap(trap)) => { - return Err(RuntimeError::user(trap)) - } - Err(trap) => return Err(RuntimeError::user(trap)), - } - } - break; - } - r? - }; - let result_types = self.handle.ty.results(); - let store_mut = store.as_store_mut(); - let engine = store_mut.engine(); - let context = engine.0.context(); - match result_types.len() { - 0 => Ok(Box::new([])), - 1 => { - let value = param_from_js(&context, &result_types[0], &result); - Ok(vec![value].into_boxed_slice()) - } - n => { - let result = result.to_object(&context).unwrap(); - Ok((0..n) - .map(|i| { - let js_val = result.get_property_at_index(&context, i as _).unwrap(); - param_from_js(&context, &result_types[i], &js_val) - }) - .collect::>() - .into_boxed_slice()) - } - } - } - - pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, internal: VMFunction) -> Self { - Self { handle: internal } - } - - pub(crate) fn vm_funcref(&self, _store: &impl AsStoreRef) -> VMFuncRef { - unimplemented!(); - } - - pub(crate) unsafe fn from_vm_funcref( - _store: &mut impl AsStoreMut, - _funcref: VMFuncRef, - ) -> Self { - unimplemented!(); - } - - /// Checks whether this `Function` can be used with the given context. - pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { - true - } -} - -impl fmt::Debug for Function { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.debug_struct("Function").finish() - } -} - -/// Represents a low-level Wasm static host function. See -/// `super::Function::new` and `super::Function::new_env` to learn -/// more. -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub struct WasmFunction { - callback: JSObjectCallAsFunctionCallback, - _phantom: PhantomData<(Args, Rets)>, -} - -unsafe impl Send for WasmFunction {} - -impl WasmFunction -where - Args: WasmTypeList, - Rets: WasmTypeList, -{ - /// Creates a new `WasmFunction`. - #[allow(dead_code)] - pub fn new(function: F) -> Self - where - F: HostFunction, - T: Sized, - { - Self { - callback: function.function_callback(), - _phantom: PhantomData, - } - } - - /// Get the function type of this `WasmFunction`. - #[allow(dead_code)] - pub fn ty(&self) -> FunctionType { - FunctionType::new(Args::wasm_types(), Rets::wasm_types()) - } - - /// Get the address of this `WasmFunction`. - #[allow(dead_code)] - pub fn callback(&self, context: &JSContext) -> JSObject { - JSObject::new_function_with_callback(context, "FunctionCallback".to_string(), self.callback) - } -} - -macro_rules! impl_host_function { - ( [$c_struct_representation:ident] - $c_struct_name:ident, - $( $x:ident ),* ) => { - - // Implement `HostFunction` for a function with a [`FunctionEnvMut`] that has the same - // arity than the tuple. - #[allow(unused_parens)] - impl< $( $x, )* Rets, RetsAsResult, T, Func > - HostFunction - for - Func - where - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - RetsAsResult: IntoResult, - T: Send + 'static, - Func: Fn(FunctionEnvMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static, - { - #[allow(non_snake_case)] - fn function_callback(&self) -> VMFunctionCallback { - #[callback] - fn fn_callback( - ctx: JSContext, - function: JSObject, - this_object: JSObject, - arguments: &[JSValue], - ) -> Result - where - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - RetsAsResult: IntoResult, - Func: Fn(FunctionEnvMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static, - T: Send + 'static, - { - use std::convert::TryInto; - - let func: &Func = &*(&() as *const () as *const Func); - let global = ctx.get_global_object(); - let store_ptr = global.get_property(&ctx, "__store_ptr".to_string()).to_number(&ctx).unwrap(); - if store_ptr.is_nan() { - panic!("Store pointer is invalid. Received {}", store_ptr as usize) - } - let mut store = StoreMut::from_raw(store_ptr as usize as *mut _); - - let handle_index = arguments[0].to_number(&ctx).unwrap() as usize; - let handle: StoreHandle = StoreHandle::from_internal(store.objects_mut().id(), InternalStoreHandle::from_index(handle_index).unwrap()); - let env: FunctionEnvMut = FunctionEnv::from_handle(handle).into_mut(&mut store); - - let result = panic::catch_unwind(AssertUnwindSafe(|| { - type JSArray<'a> = &'a [JSValue; count_idents!( $( $x ),* )]; - let args_without_store: JSArray = arguments[1..].try_into().unwrap(); - let [ $( $x ),* ] = args_without_store; - let mut store = StoreMut::from_raw(store_ptr as usize as *mut _); - func(env, $( FromToNativeWasmType::from_native( $x::Native::from_raw(&mut store, RawValue { u128: { - // TODO: This may not be the fastest way, but JSC doesn't expose a BigInt interface - // so the only thing we can do is parse from the string repr - if $x.is_number(&ctx) { - $x.to_number(&ctx).unwrap() as _ - } - else { - $x.to_string(&ctx).unwrap().to_string().parse::().unwrap() - } - } }) ) ),* ).into_result() - })); - - match result { - Ok(Ok(result)) => { - match Rets::size() { - 0 => {Ok(JSValue::undefined(&ctx))}, - 1 => { - // unimplemented!(); - - let ty = Rets::wasm_types()[0]; - let mut arr = result.into_array(&mut store); - // Value::from_raw(&store, ty, arr[0]) - let val = Value::from_raw(&mut store, ty, arr.as_mut()[0]); - let value: JSValue = val.as_jsvalue(&store); - Ok(value) - // *mut_rets = val.as_raw(&mut store); - } - _n => { - // if !results.is_array(&context) { - // panic!("Expected results to be an array.") - // } - let mut arr = result.into_array(&mut store); - let result_values = Rets::wasm_types().iter().enumerate().map(|(i, ret_type)| { - let raw = arr.as_mut()[i]; - Value::from_raw(&mut store, *ret_type, raw).as_jsvalue(&mut store) - }).collect::>(); - Ok(JSObject::new_array(&ctx, &result_values).unwrap().to_jsvalue()) - } - } - }, - #[cfg(feature = "std")] - Ok(Err(err)) => { - let trap: Trap = Trap::user(Box::new(err)); - Err(trap.into_jsvalue(&ctx)) - }, - #[cfg(feature = "core")] - Ok(Err(err)) => { - let trap: Trap = Trap::user(Box::new(err)); - Err(trap.into_jsvalue(&ctx)) - }, - Err(panic) => { - Err(JSValue::string(&ctx, format!("panic: {:?}", panic))) - // We can't just resume the unwind, because it will put - // JavacriptCore in a bad state, so we need to transform - // the error - - // std::panic::resume_unwind(panic) - }, - } - - } - Some(fn_callback:: as _) - } - } - - // Implement `HostFunction` for a function that has the same arity than the tuple. - #[allow(unused_parens)] - impl< $( $x, )* Rets, RetsAsResult, Func > - HostFunction<(), ( $( $x ),* ), Rets, WithoutEnv> - for - Func - where - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - RetsAsResult: IntoResult, - Func: Fn($( $x , )*) -> RetsAsResult + 'static, - { - #[allow(non_snake_case)] - fn function_callback(&self) -> VMFunctionCallback { - - #[callback] - fn fn_callback<$( $x, )* Rets, RetsAsResult, Func>( - ctx: JSContext, - function: JSObject, - this_object: JSObject, - arguments: &[JSValue], - ) -> Result - where - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - RetsAsResult: IntoResult, - Func: Fn($( $x , )*) -> RetsAsResult + 'static, - // $( $x: NativeWasmTypeInto, )* - { - use std::convert::TryInto; - - let func: &Func = &*(&() as *const () as *const Func); - let global = ctx.get_global_object(); - let store_ptr = global.get_property(&ctx, "__store_ptr".to_string()).to_number(&ctx).unwrap(); - if store_ptr.is_nan() { - panic!("Store pointer is invalid. Received {}", store_ptr as usize) - } - - let mut store = StoreMut::from_raw(store_ptr as usize as *mut _); - let result = panic::catch_unwind(AssertUnwindSafe(|| { - type JSArray<'a> = &'a [JSValue; count_idents!( $( $x ),* )]; - let args_without_store: JSArray = arguments.try_into().unwrap(); - let [ $( $x ),* ] = args_without_store; - func($( FromToNativeWasmType::from_native( $x::Native::from_raw(&mut store, RawValue { u128: { - // TODO: This may not be the fastest way, but JSC doesn't expose a BigInt interface - // so the only thing we can do is parse from the string repr - if $x.is_number(&ctx) { - $x.to_number(&ctx).unwrap() as _ - } - else { - $x.to_string(&ctx).unwrap().to_string().parse::().unwrap() - } - } }) ) ),* ).into_result() - })); - - match result { - Ok(Ok(result)) => { - match Rets::size() { - 0 => {Ok(JSValue::undefined(&ctx))}, - 1 => { - let ty = Rets::wasm_types()[0]; - let mut arr = result.into_array(&mut store); - let val = Value::from_raw(&mut store, ty, arr.as_mut()[0]); - let value: JSValue = val.as_jsvalue(&store); - Ok(value) - } - _n => { - let mut arr = result.into_array(&mut store); - let result_values = Rets::wasm_types().iter().enumerate().map(|(i, ret_type)| { - let raw = arr.as_mut()[i]; - Value::from_raw(&mut store, *ret_type, raw).as_jsvalue(&mut store) - }).collect::>(); - Ok(JSObject::new_array(&ctx, &result_values).unwrap().to_jsvalue()) - } - } - }, - #[cfg(feature = "std")] - Ok(Err(err)) => { - let trap: Trap = Trap::user(Box::new(err)); - Err(trap.into_jsvalue(&ctx)) - }, - #[cfg(feature = "core")] - Ok(Err(err)) => { - let trap: Trap = Trap::user(Box::new(err)); - Err(trap.into_jsvalue(&ctx)) - }, - Err(panic) => { - Err(JSValue::string(&ctx, format!("panic: {:?}", panic))) - // We can't just resume the unwind, because it will put - // JavacriptCore in a bad state, so we need to transform - // the error - - // std::panic::resume_unwind(panic) - }, - } - - } - Some(fn_callback::< $( $x, )* Rets, RetsAsResult, Self > as _) - } - } - }; - } - -// Black-magic to count the number of identifiers at compile-time. -macro_rules! count_idents { - ( $($idents:ident),* ) => { - { - #[allow(dead_code, non_camel_case_types)] - enum Idents { $( $idents, )* __CountIdentsLast } - const COUNT: usize = Idents::__CountIdentsLast as usize; - COUNT - } - }; -} - -// Here we go! Let's generate all the C struct, `WasmTypeList` -// implementations and `HostFunction` implementations. -impl_host_function!([C] S0,); -impl_host_function!([transparent] S1, A1); -impl_host_function!([C] S2, A1, A2); -impl_host_function!([C] S3, A1, A2, A3); -impl_host_function!([C] S4, A1, A2, A3, A4); -impl_host_function!([C] S5, A1, A2, A3, A4, A5); -impl_host_function!([C] S6, A1, A2, A3, A4, A5, A6); -impl_host_function!([C] S7, A1, A2, A3, A4, A5, A6, A7); -impl_host_function!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8); -impl_host_function!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9); -impl_host_function!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); -impl_host_function!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); -impl_host_function!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); -impl_host_function!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); -impl_host_function!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); -impl_host_function!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); -impl_host_function!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16); -impl_host_function!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17); -impl_host_function!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18); -impl_host_function!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19); -impl_host_function!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20); -impl_host_function!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21); -impl_host_function!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22); -impl_host_function!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23); -impl_host_function!([C] S24, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24); -impl_host_function!([C] S25, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25); -impl_host_function!([C] S26, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26); diff --git a/lib/api/src/jsc/externals/mod.rs b/lib/api/src/jsc/externals/mod.rs deleted file mode 100644 index 3575fe23bb5..00000000000 --- a/lib/api/src/jsc/externals/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub(crate) mod function; -pub(crate) mod global; -pub(crate) mod memory; -pub(crate) mod memory_view; -pub(crate) mod table; diff --git a/lib/api/src/jsc/mem_access.rs b/lib/api/src/jsc/mem_access.rs deleted file mode 100644 index f878ed6e5cf..00000000000 --- a/lib/api/src/jsc/mem_access.rs +++ /dev/null @@ -1,92 +0,0 @@ -use crate::access::{RefCow, SliceCow, WasmRefAccess, WasmSliceAccess}; -use crate::{MemoryAccessError, WasmRef, WasmSlice}; -use std::mem; - -impl<'a, T> WasmSliceAccess<'a, T> -where - T: wasmer_types::ValueType, -{ - pub(crate) fn new(slice: WasmSlice<'a, T>) -> Result { - let total_len = slice - .len - .checked_mul(mem::size_of::() as u64) - .ok_or(MemoryAccessError::Overflow)?; - let end = slice - .offset - .checked_add(total_len) - .ok_or(MemoryAccessError::Overflow)?; - if end > slice.buffer.0.len as u64 { - tracing::warn!( - "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", - total_len, - end, - slice.buffer.0.len - ); - return Err(MemoryAccessError::HeapOutOfBounds); - } - let buf = unsafe { - let buf_ptr: *mut u8 = slice.buffer.0.base.add(slice.offset as usize); - let buf_ptr: *mut T = std::mem::transmute(buf_ptr); - std::slice::from_raw_parts_mut(buf_ptr, slice.len as usize) - }; - Ok(Self { - slice, - buf: SliceCow::Borrowed(buf), - }) - } -} - -impl<'a, T> WasmRefAccess<'a, T> -where - T: wasmer_types::ValueType, -{ - pub(crate) fn new(ptr: WasmRef<'a, T>) -> Result { - let total_len = mem::size_of::() as u64; - let end = ptr - .offset - .checked_add(total_len) - .ok_or(MemoryAccessError::Overflow)?; - if end > ptr.buffer.0.len as u64 { - tracing::warn!( - "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", - total_len, - end, - ptr.buffer.0.len - ); - return Err(MemoryAccessError::HeapOutOfBounds); - } - let val = unsafe { - let val_ptr: *mut u8 = ptr.buffer.0.base.add(ptr.offset as usize); - let val_ptr: *mut T = std::mem::transmute(val_ptr); - &mut *val_ptr - }; - Ok(Self { - ptr, - buf: RefCow::Borrowed(val), - }) - } -} - -impl<'a, T> WasmRefAccess<'a, T> -where - T: wasmer_types::ValueType, -{ - /// Reads the address pointed to by this `WasmPtr` in a memory. - #[inline] - #[allow(clippy::clone_on_copy)] - pub fn read(&self) -> T - where - T: Clone, - { - self.as_ref().clone() - } - - /// Writes to the address pointed to by this `WasmPtr` in a memory. - #[inline] - pub fn write(&mut self, val: T) { - // Note: Zero padding is not required here as its a typed copy which does - // not leak the bytes into the memory - // https://stackoverflow.com/questions/61114026/does-stdptrwrite-transfer-the-uninitialized-ness-of-the-bytes-it-writes - *(self.as_mut()) = val; - } -} diff --git a/lib/api/src/jsc/mod.rs b/lib/api/src/jsc/mod.rs deleted file mode 100644 index 3a195e32258..00000000000 --- a/lib/api/src/jsc/mod.rs +++ /dev/null @@ -1,39 +0,0 @@ -//! The JavascriptCore integration in the Wasmer API - -#[cfg(all(feature = "std", feature = "core"))] -compile_error!( - "The `std` and `core` features are both enabled, which is an error. Please enable only once." -); - -#[cfg(all(not(feature = "std"), not(feature = "core")))] -compile_error!("Both the `std` and `core` features are disabled. Please enable one of them."); - -#[cfg(feature = "core")] -pub(crate) extern crate alloc; - -mod lib { - #[cfg(feature = "core")] - pub mod std { - pub use crate::alloc::{borrow, boxed, str, string, sync, vec}; - pub use core::fmt; - pub use hashbrown as collections; - } - - #[cfg(feature = "std")] - pub mod std { - pub use std::{borrow, boxed, collections, fmt, str, string, sync, vec}; - } -} - -pub(crate) mod as_js; -pub(crate) mod engine; -pub(crate) mod errors; -pub(crate) mod extern_ref; -pub(crate) mod externals; -pub(crate) mod instance; -pub(crate) mod mem_access; -pub(crate) mod module; -pub(crate) mod store; -pub(crate) mod trap; -pub(crate) mod typed_function; -pub(crate) mod vm; diff --git a/lib/api/src/jsc/store.rs b/lib/api/src/jsc/store.rs deleted file mode 100644 index 03a3e4b2985..00000000000 --- a/lib/api/src/jsc/store.rs +++ /dev/null @@ -1,287 +0,0 @@ -use crate::engine::{AsEngineRef, Engine, EngineRef}; - -pub(crate) use objects::{InternalStoreHandle, StoreObject}; -pub use objects::{StoreHandle, StoreObjects}; - -#[derive(Debug)] -pub(crate) struct Store { - pub(crate) engine: Engine, -} - -impl Store { - pub(crate) fn new(engine: Engine) -> Self { - Self { engine } - } - - pub(crate) fn engine(&self) -> &Engine { - &self.engine - } -} - -impl AsEngineRef for Store { - fn as_engine_ref(&self) -> EngineRef<'_> { - EngineRef::new(&self.engine) - } -} - -mod objects { - use rusty_jsc::{JSContext, JSObject, JSValue}; - - use crate::jsc::vm::{VMFunctionEnvironment, VMGlobal}; - use std::{fmt, marker::PhantomData, num::NonZeroUsize}; - pub use wasmer_types::StoreId; - - /// Trait to represent an object managed by a context. This is implemented on - /// the VM types managed by the context. - pub trait StoreObject: Sized { - fn list(store: &StoreObjects) -> &Vec; - fn list_mut(store: &mut StoreObjects) -> &mut Vec; - } - - macro_rules! impl_store_object { - ($($field:ident => $ty:ty,)*) => { - $( - impl StoreObject for $ty { - fn list(store: &StoreObjects) -> &Vec { - &store.$field - } - fn list_mut(store: &mut StoreObjects) -> &mut Vec { - &mut store.$field - } - } - )* - }; -} - - impl_store_object! { - // Note: we store the globals in order to be able to access them later via - // `StoreObjects::iter_globals`. - globals => VMGlobal, - // functions => VMFunction, - // tables => VMTable, - // memories => VMMemory, - // The function environments are the only things attached to a store, - // since the other JS objects (table, globals, memory and functions) - // live in the JS VM Store by default - function_environments => VMFunctionEnvironment, - } - - /// Set of objects managed by a context. - #[derive(Default, Debug)] - pub struct StoreObjects { - id: StoreId, - globals: Vec, - function_environments: Vec, - } - - impl StoreObjects { - /// Returns the ID of this context. - pub fn id(&self) -> StoreId { - self.id - } - - /// Sets the ID of this store - pub fn set_id(&mut self, id: StoreId) { - self.id = id; - } - - /// Returns a pair of mutable references from two handles. - /// - /// Panics if both handles point to the same object. - pub fn get_2_mut( - &mut self, - a: InternalStoreHandle, - b: InternalStoreHandle, - ) -> (&mut T, &mut T) { - assert_ne!(a.index(), b.index()); - let list = T::list_mut(self); - if a.index() < b.index() { - let (low, high) = list.split_at_mut(b.index()); - (&mut low[a.index()], &mut high[0]) - } else { - let (low, high) = list.split_at_mut(a.index()); - (&mut high[0], &mut low[a.index()]) - } - } - - /// Return an immutable iterator over all globals - pub fn iter_globals(&self) -> core::slice::Iter { - self.globals.iter() - } - - /// Return an vector of all globals and converted to u128 - pub fn as_u128_globals(&self) -> Vec { - vec![] - // self.iter_globals() - // .map(|v| v.global.value().as_f64().unwrap() as u128) - // .collect() - } - - /// Set a global, at index idx. Will panic if idx is out of range - /// Safety: the caller should check taht the raw value is compatible - /// with destination VMGlobal type - pub fn set_global_unchecked(&self, idx: usize, new_val: u128) { - assert!(idx < self.globals.len()); - // let g = &self.globals[idx].global; - // let cur_val = g.value().as_f64().unwrap(); - // let new_val = new_val as f64; - // if cur_val != new_val { - // let new_value = JSValue::from(new_val); - // g.set_value(&new_value); - // } - } - } - - /// Handle to an object managed by a context. - /// - /// Internally this is just an integer index into a context. A reference to the - /// context must be passed in separately to access the actual object. - pub struct StoreHandle { - id: StoreId, - internal: InternalStoreHandle, - } - - impl core::cmp::PartialEq for StoreHandle { - fn eq(&self, other: &Self) -> bool { - self.id == other.id - } - } - - impl std::hash::Hash for StoreHandle { - fn hash(&self, state: &mut H) { - self.id.hash(state); - self.internal.idx.hash(state); - } - } - - impl Clone for StoreHandle { - fn clone(&self) -> Self { - Self { - id: self.id, - internal: self.internal, - } - } - } - - impl fmt::Debug for StoreHandle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StoreHandle") - .field("id", &self.id) - .field("internal", &self.internal.index()) - .finish() - } - } - - impl StoreHandle { - /// Moves the given object into a context and returns a handle to it. - pub fn new(store: &mut StoreObjects, val: T) -> Self { - Self { - id: store.id, - internal: InternalStoreHandle::new(store, val), - } - } - - /// Returns a reference to the object that this handle points to. - pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T { - assert_eq!(self.id, store.id, "object used with the wrong context"); - self.internal.get(store) - } - - /// Returns a mutable reference to the object that this handle points to. - pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T { - assert_eq!(self.id, store.id, "object used with the wrong context"); - self.internal.get_mut(store) - } - - /// Returns the internal handle contains within this handle. - pub fn internal_handle(&self) -> InternalStoreHandle { - self.internal - } - - /// Returns the ID of the context associated with the handle. - #[allow(unused)] - pub fn store_id(&self) -> StoreId { - self.id - } - - /// Overrides the store id with a new ID - #[allow(unused)] - pub fn set_store_id(&mut self, id: StoreId) { - self.id = id; - } - - /// Constructs a `StoreHandle` from a `StoreId` and an `InternalStoreHandle`. - /// - /// # Safety - /// Handling `InternalStoreHandle` values is unsafe because they do not track context ID. - pub unsafe fn from_internal(id: StoreId, internal: InternalStoreHandle) -> Self { - Self { id, internal } - } - } - - /// Internal handle to an object owned by the current context. - /// - /// Unlike `StoreHandle` this does not track the context ID: it is only - /// intended to be used within objects already owned by a context. - #[repr(transparent)] - pub struct InternalStoreHandle { - // Use a NonZero here to reduce the size of Option. - idx: NonZeroUsize, - marker: PhantomData T>, - } - - impl Clone for InternalStoreHandle { - fn clone(&self) -> Self { - *self - } - } - impl Copy for InternalStoreHandle {} - - impl fmt::Debug for InternalStoreHandle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("InternalStoreHandle") - .field("idx", &self.idx) - .finish() - } - } - impl PartialEq for InternalStoreHandle { - fn eq(&self, other: &Self) -> bool { - self.idx == other.idx - } - } - impl Eq for InternalStoreHandle {} - - impl InternalStoreHandle { - /// Moves the given object into a context and returns a handle to it. - pub fn new(store: &mut StoreObjects, val: T) -> Self { - let list = T::list_mut(store); - let idx = NonZeroUsize::new(list.len() + 1).unwrap(); - list.push(val); - Self { - idx, - marker: PhantomData, - } - } - - /// Returns a reference to the object that this handle points to. - pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T { - &T::list(store)[self.idx.get() - 1] - } - - /// Returns a mutable reference to the object that this handle points to. - pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T { - &mut T::list_mut(store)[self.idx.get() - 1] - } - - pub(crate) fn index(&self) -> usize { - self.idx.get() - } - - pub(crate) fn from_index(idx: usize) -> Option { - NonZeroUsize::new(idx).map(|idx| Self { - idx, - marker: PhantomData, - }) - } - } -} diff --git a/lib/api/src/jsc/vm.rs b/lib/api/src/jsc/vm.rs deleted file mode 100644 index ba29499176e..00000000000 --- a/lib/api/src/jsc/vm.rs +++ /dev/null @@ -1,274 +0,0 @@ -//! This module is mainly used to create the `VM` types that will hold both -//! the JS values of the `Memory`, `Table`, `Global` and `Function` and also -//! it's types. -//! This module should not be needed any longer (with the exception of the memory) -//! once the type reflection is added to the WebAssembly JS API. -//! https://github.com/WebAssembly/js-types/ - -use crate::externals::{Extern, Function, Global, Memory, Table, VMExternToExtern}; -use crate::store::{AsStoreMut, AsStoreRef}; -use rusty_jsc::{JSObject, JSObjectCallAsFunctionCallback, JSValue}; -use std::any::Any; -use std::fmt; -use tracing::trace; -use wasmer_types::RawValue; -use wasmer_types::{ - FunctionType, GlobalType, MemoryError, MemoryType, Pages, TableType, WASM_PAGE_SIZE, -}; - -/// Represents linear memory that is managed by the javascript runtime -#[derive(Clone, Debug, PartialEq)] -pub struct VMMemory { - pub(crate) memory: JSObject, - pub(crate) ty: MemoryType, -} - -unsafe impl Send for VMMemory {} -unsafe impl Sync for VMMemory {} - -impl VMMemory { - /// Creates a new memory directly from a WebAssembly javascript object - pub fn new(memory: JSObject, ty: MemoryType) -> Self { - Self { memory, ty } - } - - /// Returns the size of the memory buffer in pages - pub fn get_runtime_size(&self) -> u32 { - unimplemented!(); - // let dummy: DummyBuffer = match serde_wasm_bindgen::from_value(self.memory.buffer()) { - // Ok(o) => o, - // Err(_) => return 0, - // }; - // if dummy.byte_length == 0 { - // return 0; - // } - // dummy.byte_length / WASM_PAGE_SIZE as u32 - } - - /// Attempts to clone this memory (if its clonable) - pub(crate) fn try_clone(&self) -> Result { - Ok(self.clone()) - } - - /// Copies this memory to a new memory - pub fn copy(&self, store: &impl AsStoreRef) -> Result { - let new_memory = - crate::jsc::externals::memory::Memory::js_memory_from_type(&store, &self.ty)?; - - trace!("memory copy started"); - - let src = crate::jsc::externals::memory_view::MemoryView::new_raw(&self.memory, store); - let amount = src.data_size() as usize; - let mut dst = crate::jsc::externals::memory_view::MemoryView::new_raw(&new_memory, store); - let dst_size = dst.data_size() as usize; - - // if amount > dst_size { - // let delta = amount - dst_size; - // let pages = ((delta - 1) / WASM_PAGE_SIZE) + 1; - - // let our_js_memory: &crate::jsc::externals::memory::JSMemory = - // JsCast::unchecked_from_js_ref(&new_memory); - // our_js_memory.grow(pages as u32).map_err(|err| { - // if err.is_instance_of::() { - // let cur_pages = dst_size; - // MemoryError::CouldNotGrow { - // current: Pages(cur_pages as u32), - // attempted_delta: Pages(pages as u32), - // } - // } else { - // MemoryError::Generic(err.as_string().unwrap()) - // } - // })?; - - // dst = crate::jsc::externals::memory_view::MemoryView::new_raw(&new_memory); - // } - - src.copy_to_memory(amount as u64, &dst).map_err(|err| { - wasmer_types::MemoryError::Generic(format!("failed to copy the memory - {}", err)) - })?; - - trace!("memory copy finished (size={})", dst.size().bytes().0); - - Ok(Self { - memory: new_memory, - ty: self.ty.clone(), - }) - } -} - -// impl From for JSValue { -// fn from(value: VMMemory) -> Self { -// JSValue::from(value.memory) -// } -// } - -/// The shared memory is the same as the normal memory -pub type VMSharedMemory = VMMemory; - -/// The VM Global type -#[derive(Clone, Debug, PartialEq)] -pub struct VMGlobal { - pub(crate) global: JSObject, - pub(crate) ty: GlobalType, -} - -impl VMGlobal { - pub(crate) fn new(global: JSObject, ty: GlobalType) -> Self { - Self { global, ty } - } -} - -unsafe impl Send for VMGlobal {} -unsafe impl Sync for VMGlobal {} - -/// The VM Table type -#[derive(Clone, Debug, PartialEq)] -pub struct VMTable { - pub(crate) table: JSObject, - pub(crate) ty: TableType, -} - -unsafe impl Send for VMTable {} -unsafe impl Sync for VMTable {} - -impl VMTable { - pub(crate) fn new(table: JSObject, ty: TableType) -> Self { - Self { table, ty } - } - - /// Get the table size at runtime - pub fn get_runtime_size(&self) -> u32 { - unimplemented!(); - // self.table.length() - } -} - -/// The VM Function type -#[derive(Clone)] -pub struct VMFunction { - pub(crate) function: JSObject, - pub(crate) ty: FunctionType, -} - -unsafe impl Send for VMFunction {} -unsafe impl Sync for VMFunction {} - -impl VMFunction { - pub(crate) fn new(function: JSObject, ty: FunctionType) -> Self { - Self { function, ty } - } -} - -impl PartialEq for VMFunction { - fn eq(&self, other: &Self) -> bool { - self.function == other.function - } -} - -impl fmt::Debug for VMFunction { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("VMFunction") - .field("function", &self.function) - .finish() - } -} - -/// The value of an export passed from one instance to another. -pub enum VMExtern { - /// A function export value. - Function(VMFunction), - - /// A table export value. - Table(VMTable), - - /// A memory export value. - Memory(VMMemory), - - /// A global export value. - Global(VMGlobal), -} - -impl VMExternToExtern for VMExtern { - fn to_extern(self, store: &mut impl AsStoreMut) -> Extern { - match self { - Self::Function(f) => Extern::Function(Function::from_vm_extern(store, f)), - Self::Memory(m) => Extern::Memory(Memory::from_vm_extern(store, m)), - Self::Global(g) => Extern::Global(Global::from_vm_extern(store, g)), - Self::Table(t) => Extern::Table(Table::from_vm_extern(store, t)), - } - } -} - -pub type VMInstance = JSObject; - -/// Underlying FunctionEnvironment used by a `VMFunction`. -#[derive(Debug)] -pub struct VMFunctionEnvironment { - contents: Box, -} - -impl VMFunctionEnvironment { - /// Wraps the given value to expose it to Wasm code as a function context. - pub fn new(val: impl Any + Send + 'static) -> Self { - Self { - contents: Box::new(val), - } - } - - #[allow(clippy::should_implement_trait)] - /// Returns a reference to the underlying value. - pub fn as_ref(&self) -> &(dyn Any + Send + 'static) { - &*self.contents - } - - #[allow(clippy::should_implement_trait)] - /// Returns a mutable reference to the underlying value. - pub fn as_mut(&mut self) -> &mut (dyn Any + Send + 'static) { - &mut *self.contents - } -} - -pub(crate) struct VMExternRef; - -impl VMExternRef { - /// Converts the `VMExternRef` into a `RawValue`. - pub fn into_raw(self) -> RawValue { - unimplemented!(); - } - - /// Extracts a `VMExternRef` from a `RawValue`. - /// - /// # Safety - /// `raw` must be a valid `VMExternRef` instance. - pub unsafe fn from_raw(_raw: RawValue) -> Option { - unimplemented!(); - } -} - -#[repr(transparent)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub(crate) struct VMFuncRef; - -impl VMFuncRef { - /// Converts the `VMFuncRef` into a `RawValue`. - pub fn into_raw(self) -> RawValue { - unimplemented!(); - } - - /// Extracts a `VMFuncRef` from a `RawValue`. - /// - /// # Safety - /// `raw.funcref` must be a valid pointer. - pub unsafe fn from_raw(_raw: RawValue) -> Option { - unimplemented!(); - } -} - -pub struct VMTrampoline; - -pub(crate) type VMExternTable = VMTable; -pub(crate) type VMExternMemory = VMMemory; -pub(crate) type VMExternGlobal = VMGlobal; -pub(crate) type VMExternFunction = VMFunction; - -pub type VMFunctionCallback = JSObjectCallAsFunctionCallback; diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs index 1911951a9c5..d680789f201 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -9,7 +9,12 @@ rustdoc::broken_intra_doc_links )] #![warn(unused_import_braces)] -#![allow(clippy::new_without_default, ambiguous_wide_pointer_comparisons)] +#![allow( + clippy::new_without_default, + ambiguous_wide_pointer_comparisons, + unreachable_patterns, + unused +)] #![warn( clippy::float_arithmetic, clippy::mut_mut, @@ -23,7 +28,7 @@ //! [`Wasmer`](https://wasmer.io/) is the most popular //! [WebAssembly](https://webassembly.org/) runtime for Rust. It supports //! JIT (Just In Time) and AOT (Ahead Of Time) compilation as well as -//! pluggable compilers suited to your needs. +//! pluggable compilers suited to your needs and interpreters. //! //! It's designed to be safe and secure, and runnable in any kind of environment. //! @@ -74,6 +79,10 @@ //! * [`wasmer-compiler-llvm`] provides a deeply optimized executable //! code with the fastest runtime speed, ideal for production. //! +//! * **Interpreters** - Wasmer supports interpeters such as [`wamr`] and [`wasmi`]. +//! +//! * **Other runtimes** - Wasmer supports [`v8`]. +//! //! * **Headless mode** — Once a WebAssembly module has been compiled, it //! is possible to serialize it in a file for example, and later execute //! it with Wasmer with headless mode turned on. Headless Wasmer has no @@ -239,31 +248,41 @@ //! The engine is a system that uses a compiler to make a WebAssembly //! module executable. //! -//! ## Compilers +//! ## Runtimes //! -//! A compiler is a system that handles the details of making a Wasm -//! module executable. For example, by generating native machine code -//! for each Wasm function. +//! A runtime is a system that handles the details of making a Wasm module executable. We support +//! multiple kinds of runtimes: compilers, which generate native machine code for each Wasm +//! function and interpreter, in which no native machine code is generated and can be used on +//! platforms where JIT compilation is not allowed, such as iOS. //! //! # Cargo Features //! -//! This crate comes in 2 flavors: -//! //! 1. `sys` #![cfg_attr(feature = "sys", doc = "(enabled),")] #![cfg_attr(not(feature = "sys"), doc = "(disabled),")] //! where `wasmer` will be compiled to a native executable //! which provides compilers, engines, a full VM etc. -//! 2. `js` +//! By default, the `singlepass` and `cranelift` backends are enabled. +//! +//! 2. `v8` +#![cfg_attr(feature = "v8", doc = "(enabled),")] +#![cfg_attr(not(feature = "v8"), doc = "(disabled),")] +//! where `wasmer` will be compiled to a native executable +//! where the `v8` runtime is used for execution. +//! +//! 3. `wamr` +#![cfg_attr(feature = "wamr", doc = "(enabled),")] +#![cfg_attr(not(feature = "wamr"), doc = "(disabled),")] +//! where `wasmer` will be compiled to a native executable +//! where `wamr` (in interpreter mode) is used for execution. +//! +//! 4. `js` #![cfg_attr(feature = "js", doc = "(enabled),")] #![cfg_attr(not(feature = "js"), doc = "(disabled),")] //! where `wasmer` will be compiled to WebAssembly to run in a //! JavaScript host (see [Using Wasmer in a JavaScript //! environment](#using-wasmer-in-a-javascript-environment)). //! -//! Consequently, we can group the features by the `sys` or `js` -//! features. -//! #![cfg_attr( feature = "sys", doc = "## Features for the `sys` feature group (enabled)" @@ -301,6 +320,10 @@ #![cfg_attr(not(feature = "compiler"), doc = "(disabled),")] //! enables compilation with the wasmer engine. //! +//! Notice that the `sys`, `wamr` and `v8` features are composable together, +//! so a single build of Wasmer using `llvm`, `cranelift`, `singlepass`, `wamr`, and `v8` +//! (or any combination of them) is possible. +//! #![cfg_attr( feature = "js", doc = "## Features for the `js` feature group (enabled)" @@ -385,155 +408,112 @@ //! [`wasmer-wasix`]: https://docs.rs/wasmer-wasix/ //! [`wasm-pack`]: https://github.com/rustwasm/wasm-pack/ //! [`wasm-bindgen`]: https://github.com/rustwasm/wasm-bindgen +//! [`v8`]: https://v8.dev/ +//! [`wamr`]: https://github.com/bytecodealliance/wasm-micro-runtime +//! [`wasmi`]: https://github.com/wasmi-labs/wasmi -#[cfg(all( - not(feature = "sys"), - not(feature = "js"), - not(feature = "jsc"), - not(feature = "wasm-c-api") -))] -compile_error!( - "One of: `sys`, `js`, `jsc` or `wasm-c-api` features must be enabled. Please, pick one." -); - -#[cfg(all(feature = "sys", feature = "js"))] -compile_error!( - "Cannot have both `sys` and `js` features enabled at the same time. Please, pick one." -); - -#[cfg(all(feature = "js", feature = "jsc"))] -compile_error!( - "Cannot have both `js` and `jsc` features enabled at the same time. Please, pick one." -); - -#[cfg(all(feature = "js", feature = "wasm-c-api"))] -compile_error!( - "Cannot have both `js` and `wasm-c-api` features enabled at the same time. Please, pick one." -); - -#[cfg(all(feature = "jsc", feature = "wasm-c-api"))] -compile_error!( - "Cannot have both `jsc` and `wasm-c-api` features enabled at the same time. Please, pick one." -); - -#[cfg(all(feature = "sys", feature = "jsc"))] -compile_error!( - "Cannot have both `sys` and `jsc` features enabled at the same time. Please, pick one." -); - -#[cfg(all(feature = "sys", feature = "wasm-c-api"))] -compile_error!( - "Cannot have both `sys` and `wasm-c-api` features enabled at the same time. Please, pick one." -); - -#[cfg(all(feature = "sys", target_arch = "wasm32"))] -compile_error!("The `sys` feature must be enabled only for non-`wasm32` target."); - -#[cfg(all(feature = "jsc", target_arch = "wasm32"))] -compile_error!("The `jsc` feature must be enabled only for non-`wasm32` target."); - -#[cfg(all(feature = "js", not(target_arch = "wasm32")))] +#[cfg(not(any( + feature = "sys", + feature = "js", + feature = "jsc", + feature = "wamr", + feature = "v8", + feature = "wasmi" +)))] compile_error!( - "The `js` feature must be enabled only for the `wasm32` target (either `wasm32-unknown-unknown` or `wasm32-wasi`)." + "One of: `sys`, `js`, `jsc` `wamr`, `wasmi` or `v8` features must be enabled. Please, pick one." ); -mod access; -mod engine; -mod errors; -mod exports; -mod extern_ref; -mod externals; -mod function_env; -mod imports; -mod instance; -mod into_bytes; -mod mem_access; -mod module; -mod native_type; -mod ptr; -mod store; -mod typed_function; -mod value; -pub mod vm; - -#[cfg(any(feature = "wasm-types-polyfill", feature = "jsc"))] -mod module_info_polyfill; +mod utils; +pub use utils::*; -#[cfg(feature = "sys")] -/// The `sys` engine. -pub mod sys; -#[cfg(feature = "sys")] -/// Re-export `sys` definitions. -pub use sys::*; +mod entities; +pub use entities::memory::{location::MemoryLocation, MemoryView}; +pub use entities::*; -#[cfg(feature = "js")] -/// The `js` engine. -mod js; -#[cfg(feature = "js")] -/// Re-export `js` definitions. -pub use js::*; +mod error; +pub use error::*; -#[cfg(feature = "jsc")] -/// The `jsc` engine. -mod jsc; -#[cfg(feature = "jsc")] -/// Re-export `jsc` definitions. -pub use jsc::*; +mod rt; +pub use rt::*; +mod vm; -#[cfg(feature = "wasm-c-api")] -/// The `c-api` engine. -mod c_api; -#[cfg(feature = "wasm-c-api")] -/// Re-export `c-api` definitions. -#[allow(unused_imports)] -pub use c_api::*; - -pub use crate::externals::{ - Extern, Function, Global, HostFunction, Memory, MemoryLocation, MemoryView, SharedMemory, Table, -}; -pub use access::WasmSliceAccess; -pub use engine::{AsEngineRef, Engine, EngineRef}; -pub use errors::{AtomicsError, InstantiationError, LinkError, RuntimeError}; -pub use exports::{ExportError, Exportable, Exports, ExportsIterator}; -pub use extern_ref::ExternRef; -pub use function_env::{FunctionEnv, FunctionEnvMut}; -pub use imports::Imports; -pub use instance::Instance; -pub use into_bytes::IntoBytes; -pub use mem_access::{MemoryAccessError, WasmRef, WasmSlice, WasmSliceIter}; -pub use module::{IoCompileError, Module}; -pub use native_type::{FromToNativeWasmType, NativeWasmTypeInto, WasmTypeList}; -pub use ptr::{Memory32, Memory64, MemorySize, WasmPtr, WasmPtr64}; -pub use store::{ - AsStoreMut, AsStoreRef, OnCalledHandler, Store, StoreId, StoreMut, StoreObjects, StoreRef, -}; -#[cfg(feature = "sys")] -pub use store::{TrapHandlerFn, Tunables}; -#[cfg(any(feature = "sys", feature = "jsc", feature = "wasm-c-api"))] -pub use target_lexicon::{Architecture, CallingConvention, OperatingSystem, Triple, HOST}; -pub use typed_function::TypedFunction; -pub use value::Value; - -// Reexport from other modules - -pub use wasmer_derive::ValueType; - -#[cfg(any(feature = "sys", feature = "jsc", feature = "wasm-c-api"))] -pub use wasmer_compiler::types::target::{CpuFeature, Target}; - -// TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 pub use wasmer_types::{ is_wasm, Bytes, CompileError, DeserializeError, ExportIndex, ExportType, ExternType, FrameInfo, - FunctionType, GlobalInit, GlobalType, ImportType, LocalFunctionIndex, MemoryError, MemoryType, - MiddlewareError, Mutability, OnCalledAction, Pages, ParseCpuFeatureError, SerializeError, - TableType, Type, ValueType, WasmError, WasmResult, WASM_MAX_PAGES, WASM_MIN_PAGES, + FunctionType, GlobalInit, GlobalType, ImportType, LocalFunctionIndex, MemoryError, MemoryStyle, + MemoryType, Mutability, OnCalledAction, Pages, ParseCpuFeatureError, SerializeError, + TableStyle, TableType, Type, ValueType, WasmError, WasmResult, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE, }; -#[cfg(feature = "wat")] -pub use wat::parse_bytes as wat2wasm; #[cfg(feature = "wasmparser")] pub use wasmparser; -/// Version number of this crate. -pub const VERSION: &str = env!("CARGO_PKG_VERSION"); +#[cfg(feature = "wat")] +pub use wat::parse_bytes as wat2wasm; + +pub use wasmer_derive::ValueType; + +#[cfg(any( + all( + feature = "sys-default", + any( + feature = "js-default", + feature = "jsc-default", + feature = "wamr-default", + feature = "v8-default", + feature = "wasmi-default" + ) + ), + all( + feature = "js-default", + any( + feature = "sys-default", + feature = "jsc-default", + feature = "wamr-default", + feature = "v8-default", + feature = "wasmi-default" + ) + ), + all( + feature = "jsc-default", + any( + feature = "sys-default", + feature = "js-default", + feature = "wamr-default", + feature = "v8-default", + feature = "wasmi-default" + ) + ), + all( + feature = "wamr-default", + any( + feature = "sys-default", + feature = "js-default", + feature = "jsc-default", + feature = "v8-default", + feature = "wasmi-default" + ) + ), + all( + feature = "v8-default", + any( + feature = "sys-default", + feature = "js-default", + feature = "jsc-default", + feature = "wasmi-default", + feature = "wasmi-default" + ) + ), + all( + feature = "v8-default", + any( + feature = "sys-default", + feature = "js-default", + feature = "jsc-default", + feature = "wasmi-default", + feature = "wamr-default" + ) + ) +))] +compile_error!("Multiple *-default features selected. Please, pick one only!"); diff --git a/lib/api/src/rt/js/entities/engine.rs b/lib/api/src/rt/js/entities/engine.rs new file mode 100644 index 00000000000..3c1de461e11 --- /dev/null +++ b/lib/api/src/rt/js/entities/engine.rs @@ -0,0 +1,56 @@ +/// The engine for the JavaScript runtime. +#[derive(Clone, Debug)] +pub struct Engine; + +impl Engine { + pub(crate) fn deterministic_id(&self) -> &str { + // All js engines have the same id + "js-generic" + } +} + +impl Default for Engine { + fn default() -> Self { + Engine + } +} + +/// Returns the default engine for the JS engine +pub(crate) fn default_engine() -> Engine { + Engine::default() +} + +impl crate::Engine { + /// Consume [`self`] into a [`crate::rt::js::engine::Engine`]. + pub fn into_js(self) -> crate::rt::js::engine::Engine { + match self.rt { + crate::RuntimeEngine::Js(s) => s, + _ => panic!("Not a `js` engine!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::js::engine::Engine`]. + pub fn as_js(&self) -> &crate::rt::js::engine::Engine { + match self.rt { + crate::RuntimeEngine::Js(ref s) => s, + _ => panic!("Not a `js` engine!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::js::engine::Engine`]. + pub fn as_js_mut(&mut self) -> &mut crate::rt::js::engine::Engine { + match self.rt { + crate::RuntimeEngine::Js(ref mut s) => s, + _ => panic!("Not a `js` engine!"), + } + } +} + +impl Into for Engine { + fn into(self) -> crate::Engine { + crate::Engine { + rt: crate::RuntimeEngine::Js(self), + id: crate::Engine::atomic_next_engine_id(), + } + } +} diff --git a/lib/api/src/js/extern_ref.rs b/lib/api/src/rt/js/entities/external.rs similarity index 96% rename from lib/api/src/js/extern_ref.rs rename to lib/api/src/rt/js/entities/external.rs index fc0771e6123..37da6fd0afc 100644 --- a/lib/api/src/js/extern_ref.rs +++ b/lib/api/src/rt/js/entities/external.rs @@ -5,6 +5,7 @@ use crate::store::{AsStoreMut, AsStoreRef}; #[derive(Debug, Clone)] #[repr(transparent)] +/// A WebAssembly `extern ref` in `js`. pub struct ExternRef; impl ExternRef { diff --git a/lib/api/src/rt/js/entities/function/env.rs b/lib/api/src/rt/js/entities/function/env.rs new file mode 100644 index 00000000000..4bf0f9f7f52 --- /dev/null +++ b/lib/api/src/rt/js/entities/function/env.rs @@ -0,0 +1,207 @@ +use std::{any::Any, fmt::Debug, marker::PhantomData}; + +use crate::{ + js::{store::StoreHandle, vm::VMFunctionEnvironment}, + store::{AsStoreMut, AsStoreRef, StoreRef}, + StoreMut, +}; + +#[derive(Debug)] +#[repr(transparent)] +/// An opaque reference to a function environment. +/// The function environment data is owned by the `Store`. +pub struct FunctionEnv { + pub(crate) handle: StoreHandle, + marker: PhantomData, +} + +impl FunctionEnv { + /// Make a new FunctionEnv + pub fn new(store: &mut impl AsStoreMut, value: T) -> Self + where + T: Any + Send + 'static + Sized, + { + Self { + handle: StoreHandle::new( + store.as_store_mut().objects_mut().as_js_mut(), + VMFunctionEnvironment::new(value), + ), + marker: PhantomData, + } + } + + /// Get the data as reference + pub fn as_ref<'a>(&self, store: &'a impl AsStoreRef) -> &'a T + where + T: Any + Send + 'static + Sized, + { + self.handle + .get(store.as_store_ref().objects().as_js()) + .as_ref() + .downcast_ref::() + .unwrap() + } + + pub(crate) fn from_handle(handle: StoreHandle) -> Self { + Self { + handle, + marker: PhantomData, + } + } + + /// Get the data as mutable + pub fn as_mut<'a>(&self, store: &'a mut impl AsStoreMut) -> &'a mut T + where + T: Any + Send + 'static + Sized, + { + self.handle + .get_mut(store.objects_mut().as_js_mut()) + .as_mut() + .downcast_mut::() + .unwrap() + } + + /// Convert it into a `FunctionEnvMut` + pub fn into_mut(self, store: &mut impl AsStoreMut) -> FunctionEnvMut + where + T: Any + Send + 'static + Sized, + { + FunctionEnvMut { + store_mut: store.as_store_mut(), + func_env: self, + } + } +} + +impl PartialEq for FunctionEnv { + fn eq(&self, other: &Self) -> bool { + self.handle == other.handle + } +} + +impl Eq for FunctionEnv {} + +impl std::hash::Hash for FunctionEnv { + fn hash(&self, state: &mut H) { + self.handle.hash(state); + self.marker.hash(state); + } +} + +impl Clone for FunctionEnv { + fn clone(&self) -> Self { + Self { + handle: self.handle.clone(), + marker: self.marker, + } + } +} + +/// A temporary handle to a [`FunctionEnv`]. +pub struct FunctionEnvMut<'a, T: 'a> { + pub(crate) store_mut: StoreMut<'a>, + pub(crate) func_env: FunctionEnv, +} + +impl<'a, T> Debug for FunctionEnvMut<'a, T> +where + T: Send + Debug + 'static, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.func_env.as_ref(&self.store_mut).fmt(f) + } +} + +impl FunctionEnvMut<'_, T> { + /// Returns a reference to the host state in this function environement. + pub fn data(&self) -> &T { + self.func_env.as_ref(&self.store_mut) + } + + /// Returns a mutable- reference to the host state in this function environement. + pub fn data_mut(&mut self) -> &mut T { + self.func_env.as_mut(&mut self.store_mut) + } + + /// Borrows a new immmutable reference + pub fn as_ref(&self) -> FunctionEnv { + self.func_env.clone() + } + + /// Borrows a new mutable reference + pub fn as_mut(&mut self) -> FunctionEnvMut<'_, T> { + FunctionEnvMut { + store_mut: self.store_mut.as_store_mut(), + func_env: self.func_env.clone(), + } + } + + /// Borrows a new mutable reference of both the attached Store and host state + pub fn data_and_store_mut(&mut self) -> (&mut T, StoreMut) { + let data = self.func_env.as_mut(&mut self.store_mut) as *mut T; + // telling the borrow check to close his eyes here + // this is still relatively safe to do as func_env are + // stored in a specific vec of Store, separate from the other objects + // and not really directly accessible with the StoreMut + let data = unsafe { &mut *data }; + (data, self.store_mut.as_store_mut()) + } +} + +impl AsStoreRef for FunctionEnvMut<'_, T> { + fn as_store_ref(&self) -> StoreRef<'_> { + StoreRef { + inner: self.store_mut.inner, + } + } +} + +impl AsStoreMut for FunctionEnvMut<'_, T> { + fn as_store_mut(&mut self) -> StoreMut<'_> { + StoreMut { + inner: self.store_mut.inner, + } + } + + fn objects_mut(&mut self) -> &mut crate::StoreObjects { + self.store_mut.objects_mut() + } +} + +impl crate::FunctionEnv { + /// Consume [`self`] into [`crate::rt::js::function::env::FunctionEnv`]. + pub fn into_js(self) -> FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::Js(s) => s, + _ => panic!("Not a `js` function env!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::js::function::env::FunctionEnv`]. + pub fn as_js(&self) -> &FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::Js(ref s) => s, + _ => panic!("Not a `js` function env!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::js::function::env::FunctionEnv`]. + pub fn as_js_mut(&mut self) -> &mut FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::Js(ref mut s) => s, + _ => panic!("Not a `js` function env!"), + } + } +} + +impl<'a, T> From> for crate::FunctionEnvMut<'a, T> { + fn from(value: FunctionEnvMut<'a, T>) -> Self { + crate::FunctionEnvMut(crate::RuntimeFunctionEnvMut::Js(value)) + } +} + +impl From> for crate::FunctionEnv { + fn from(value: FunctionEnv) -> Self { + crate::FunctionEnv(crate::RuntimeFunctionEnv::Js(value)) + } +} diff --git a/lib/api/src/js/externals/function.rs b/lib/api/src/rt/js/entities/function/mod.rs similarity index 63% rename from lib/api/src/js/externals/function.rs rename to lib/api/src/rt/js/entities/function/mod.rs index 1d588ff9d0c..01a9e99491b 100644 --- a/lib/api/src/js/externals/function.rs +++ b/lib/api/src/rt/js/entities/function/mod.rs @@ -1,44 +1,33 @@ -use crate::errors::RuntimeError; -use crate::externals::function::{HostFunction, HostFunctionKind, WithEnv, WithoutEnv}; -use crate::function_env::{FunctionEnv, FunctionEnvMut}; -use crate::js::as_js::{param_from_js, AsJs}; /* ValFuncRef */ -use crate::js::store::{InternalStoreHandle, StoreHandle}; -use crate::js::vm::{VMExtern, VMFuncRef, VMFunction, VMFunctionCallback, VMFunctionEnvironment}; -use crate::native_type::{FromToNativeWasmType, IntoResult, NativeWasmTypeInto, WasmTypeList}; -use crate::store::{AsStoreMut, AsStoreRef, StoreMut}; -use crate::value::Value; -use std::fmt; -use std::iter::FromIterator; +pub(crate) mod env; +pub(crate) mod typed; use std::marker::PhantomData; -use std::panic::{self, AssertUnwindSafe}; -use wasmer_types::{FunctionType, NativeWasmType, RawValue}; +pub(crate) use typed::*; -use js_sys::{Array, Function as JSFunction}; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; +use js_sys::{Array, Function as JsFunction}; +use wasm_bindgen::{prelude::*, JsCast}; +use wasmer_types::{FunctionType, RawValue}; -#[inline] -fn result_to_js(val: &Value) -> JsValue { - match val { - Value::I32(i) => JsValue::from_f64(*i as _), - Value::I64(i) => JsValue::from_f64(*i as _), - Value::F32(f) => JsValue::from_f64(*f as _), - Value::F64(f) => JsValue::from_f64(*f), - Value::V128(f) => JsValue::from_f64(*f as _), - val => unimplemented!( - "The value `{:?}` is not yet supported in the JS Function API", - val - ), - } -} +use crate::{ + js::{ + utils::convert::{js_value_to_wasmer, wasmer_value_to_js, AsJs as _}, + vm::{function::VMFunction, VMFuncRef, VMFunctionCallback}, + }, + vm::{VMExtern, VMExternFunction}, + AsStoreMut, AsStoreRef, FromToNativeWasmType, FunctionEnv, FunctionEnvMut, HostFunction, + HostFunctionKind, IntoResult, NativeWasmType, NativeWasmTypeInto, RuntimeError, + RuntimeFunction, RuntimeFunctionEnv, RuntimeFunctionEnvMut, StoreMut, Value, WasmTypeList, + WithEnv, WithoutEnv, +}; + +use std::panic::{self, AssertUnwindSafe}; #[inline] -fn results_to_js_array(values: &[Value]) -> Array { - Array::from_iter(values.iter().map(result_to_js)) +fn wasmer_array_to_js_array(values: &[Value]) -> Array { + Array::from_iter(values.iter().map(wasmer_value_to_js)) } -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Eq)] pub struct Function { pub(crate) handle: VMFunction, } @@ -55,8 +44,10 @@ impl From for Function { impl Function { /// To `VMExtern`. - pub fn to_vm_extern(&self) -> VMExtern { - VMExtern::Function(self.handle.clone()) + pub(crate) fn to_vm_extern(&self) -> VMExtern { + VMExtern::Js(crate::js::vm::external::VMExtern::Function( + self.handle.clone(), + )) } #[allow(clippy::cast_ptr_alignment)] @@ -86,7 +77,7 @@ impl Function { .params() .iter() .enumerate() - .map(|(i, param)| param_from_js(param, &args.get(i as u32))) + .map(|(i, param)| js_value_to_wasmer(param, &args.get(i as u32))) .collect::>(); let _results = func(env, &wasm_arguments)?; Ok(()) @@ -100,10 +91,10 @@ impl Function { .params() .iter() .enumerate() - .map(|(i, param)| param_from_js(param, &args.get(i as u32))) + .map(|(i, param)| js_value_to_wasmer(param, &args.get(i as u32))) .collect::>(); let results = func(env, &wasm_arguments)?; - return Ok(result_to_js(&results[0])); + return Ok(wasmer_value_to_js(&results[0])); }) as Box Result>) .into_js_value(), @@ -114,20 +105,20 @@ impl Function { .params() .iter() .enumerate() - .map(|(i, param)| param_from_js(param, &args.get(i as u32))) + .map(|(i, param)| js_value_to_wasmer(param, &args.get(i as u32))) .collect::>(); let results = func(env, &wasm_arguments)?; - return Ok(results_to_js_array(&results)); + return Ok(wasmer_array_to_js_array(&results)); }) as Box Result>) .into_js_value(), }; let dyn_func = - JSFunction::new_with_args("f", "return f(Array.prototype.slice.call(arguments, 1))"); + JsFunction::new_with_args("f", "return f(Array.prototype.slice.call(arguments, 1))"); let binded_func = dyn_func.bind1(&JsValue::UNDEFINED, &wrapped_func); let vm_function = VMFunction::new(binded_func, func_ty); - Self::from_vm_extern(&mut store, vm_function) + Self::from_vm_extern(&mut store, VMExternFunction::Js(vm_function)) } /// Creates a new host `Function` from a native function. @@ -183,7 +174,7 @@ impl Function { let binded_func = func.bind2( &JsValue::UNDEFINED, &JsValue::from_f64(store.as_raw() as *mut u8 as usize as f64), - &JsValue::from_f64(env.handle.internal_handle().index() as f64), + &JsValue::from_f64(env.as_js().handle.internal_handle().index() as f64), ); let ty = function.ty(); let vm_function = VMFunction::new(binded_func, ty); @@ -256,7 +247,7 @@ impl Function { match result_types.len() { 0 => Ok(Box::new([])), 1 => { - let value = param_from_js(&result_types[0], &result); + let value = js_value_to_wasmer(&result_types[0], &result); Ok(vec![value].into_boxed_slice()) } _n => { @@ -264,15 +255,17 @@ impl Function { Ok(result_array .iter() .enumerate() - .map(|(i, js_val)| param_from_js(&result_types[i], &js_val)) + .map(|(i, js_val)| js_value_to_wasmer(&result_types[i], &js_val)) .collect::>() .into_boxed_slice()) } } } - pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, internal: VMFunction) -> Self { - Self { handle: internal } + pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, internal: VMExternFunction) -> Self { + Self { + handle: internal.into_js(), + } } pub(crate) fn vm_funcref(&self, _store: &impl AsStoreRef) -> VMFuncRef { @@ -297,8 +290,8 @@ impl Function { } } -impl fmt::Debug for Function { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { +impl std::fmt::Debug for Function { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.debug_struct("Function").finish() } } @@ -327,7 +320,7 @@ where T: Sized, { Self { - address: function.function_callback(), + address: function.function_callback(crate::Runtime::Js).into_js(), _phantom: PhantomData, } } @@ -345,116 +338,123 @@ where } } +impl crate::Function { + /// Consume [`self`] into [`crate::rt::js::function::Function`]. + pub fn into_js(self) -> crate::rt::js::function::Function { + match self.0 { + RuntimeFunction::Js(s) => s, + _ => panic!("Not a `js` function!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::js::function::Function`]. + pub fn as_js(&self) -> &crate::rt::js::function::Function { + match self.0 { + RuntimeFunction::Js(ref s) => s, + _ => panic!("Not a `js` function!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::js::function::Function`]. + pub fn as_js_mut(&mut self) -> &mut crate::rt::js::function::Function { + match self.0 { + RuntimeFunction::Js(ref mut s) => s, + _ => panic!("Not a `js` function!"), + } + } +} + macro_rules! impl_host_function { - ( [$c_struct_representation:ident] - $c_struct_name:ident, - $( $x:ident ),* ) => { - - // Implement `HostFunction` for a function with a [`FunctionEnvMut`] that has the same - // arity than the tuple. - #[allow(unused_parens)] - impl< $( $x, )* Rets, RetsAsResult, T, Func > - HostFunction - for - Func + ([$c_struct_representation:ident] $c_struct_name:ident, $( $x:ident ),* ) => { + paste::paste! { + #[allow(non_snake_case)] + pub(crate) fn [] + <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, Func: Fn($( $x , )*) -> RetsAsResult + 'static> + (this: &Func) -> crate::rt::js::vm::VMFunctionCallback { + + /// This is a function that wraps the real host + /// function. Its address will be used inside the + /// runtime. + unsafe extern "C" fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func>( store_ptr: usize, $( $x: <$x::Native as NativeWasmType>::Abi, )* ) -> Rets::CStruct where $( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, - T: Send + 'static, - Func: Fn(FunctionEnvMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static, + Func: Fn($( $x , )*) -> RetsAsResult + 'static, { - #[allow(non_snake_case)] - fn function_callback(&self) -> VMFunctionCallback { - /// This is a function that wraps the real host - /// function. Its address will be used inside the - /// runtime. - unsafe extern "C" fn func_wrapper( store_ptr: usize, handle_index: usize, $( $x: <$x::Native as NativeWasmType>::Abi, )* ) -> Rets::CStruct - where - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - RetsAsResult: IntoResult, - T: Send + 'static, - Func: Fn(FunctionEnvMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static, - { - let mut store = StoreMut::from_raw(store_ptr as *mut _); - let mut store2 = StoreMut::from_raw(store_ptr as *mut _); - - let result = { - // let env: &Env = unsafe { &*(ptr as *const u8 as *const Env) }; - let func: &Func = &*(&() as *const () as *const Func); - panic::catch_unwind(AssertUnwindSafe(|| { - let handle: StoreHandle = StoreHandle::from_internal(store2.objects_mut().id(), InternalStoreHandle::from_index(handle_index).unwrap()); - let env: FunctionEnvMut = FunctionEnv::from_handle(handle).into_mut(&mut store2); - func(env, $( FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut store, $x)) ),* ).into_result() - })) - }; - - match result { - Ok(Ok(result)) => return result.into_c_struct(&mut store), - #[allow(deprecated)] - #[cfg(feature = "std")] - Ok(Err(trap)) => crate::js::errors::raise(Box::new(trap)), - #[cfg(feature = "core")] - #[allow(deprecated)] - Ok(Err(trap)) => crate::js::errors::raise(Box::new(trap)), - Err(_panic) => unimplemented!(), - } - } - - func_wrapper::< T, $( $x, )* Rets, RetsAsResult, Self > as VMFunctionCallback + // let env: &Env = unsafe { &*(ptr as *const u8 as *const Env) }; + let func: &Func = &*(&() as *const () as *const Func); + let mut store = StoreMut::from_raw(store_ptr as *mut _); + + let result = panic::catch_unwind(AssertUnwindSafe(|| { + func($( FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut store, $x)) ),* ).into_result() + })); + + match result { + Ok(Ok(result)) => return result.into_c_struct(&mut store), + #[cfg(feature = "std")] + #[allow(deprecated)] + Ok(Err(trap)) => crate::rt::js::error::raise(Box::new(trap)), + #[cfg(feature = "core")] + #[allow(deprecated)] + Ok(Err(trap)) => crate::rt::js::error::raise(Box::new(trap)), + Err(_panic) => unimplemented!(), } } - // Implement `HostFunction` for a function that has the same arity than the tuple. - #[allow(unused_parens)] - impl< $( $x, )* Rets, RetsAsResult, Func > - HostFunction<(), ( $( $x ),* ), Rets, WithoutEnv> - for - Func + func_wrapper::< $( $x, )* Rets, RetsAsResult, Func> as _ + + } + + + #[allow(non_snake_case)] + pub(crate) fn [] + <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, T: Send + 'static, Func: Fn(FunctionEnvMut, $( $x , )*) -> RetsAsResult + 'static> + (this: &Func) -> crate::rt::js::vm::VMFunctionCallback { + + /// This is a function that wraps the real host + /// function. Its address will be used inside the + /// runtime. + unsafe extern "C" fn func_wrapper( store_ptr: usize, handle_index: usize, $( $x: <$x::Native as NativeWasmType>::Abi, )* ) -> Rets::CStruct where $( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, - Func: Fn($( $x , )*) -> RetsAsResult + 'static, + T: Send + 'static, + Func: Fn(FunctionEnvMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static, { - #[allow(non_snake_case)] - fn function_callback(&self) -> VMFunctionCallback { - /// This is a function that wraps the real host - /// function. Its address will be used inside the - /// runtime. - unsafe extern "C" fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func>( store_ptr: usize, $( $x: <$x::Native as NativeWasmType>::Abi, )* ) -> Rets::CStruct - where - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - RetsAsResult: IntoResult, - Func: Fn($( $x , )*) -> RetsAsResult + 'static, - { - // let env: &Env = unsafe { &*(ptr as *const u8 as *const Env) }; - let func: &Func = &*(&() as *const () as *const Func); - let mut store = StoreMut::from_raw(store_ptr as *mut _); - - let result = panic::catch_unwind(AssertUnwindSafe(|| { - func($( FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut store, $x)) ),* ).into_result() - })); - - match result { - Ok(Ok(result)) => return result.into_c_struct(&mut store), - #[cfg(feature = "std")] - #[allow(deprecated)] - Ok(Err(trap)) => crate::js::errors::raise(Box::new(trap)), - #[cfg(feature = "core")] - #[allow(deprecated)] - Ok(Err(trap)) => crate::js::errors::raise(Box::new(trap)), - Err(_panic) => unimplemented!(), - } - } - - func_wrapper::< $( $x, )* Rets, RetsAsResult, Self > as VMFunctionCallback + let mut store = StoreMut::from_raw(store_ptr as *mut _); + let mut store2 = StoreMut::from_raw(store_ptr as *mut _); + + let result = { + // let env: &Env = unsafe { &*(ptr as *const u8 as *const Env) }; + let func: &Func = &*(&() as *const () as *const Func); + panic::catch_unwind(AssertUnwindSafe(|| { + let handle: crate::rt::js::store::StoreHandle = + crate::rt::js::store::StoreHandle::from_internal(store2.objects_mut().id(), crate::rt::js::store::InternalStoreHandle::from_index(handle_index).unwrap()); + let env: crate::rt::js::function::env::FunctionEnvMut = crate::rt::js::function::env::FunctionEnv::from_handle(handle).into_mut(&mut store2); + func(RuntimeFunctionEnvMut::Js(env).into(), $( FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut store, $x)) ),* ).into_result() + })) + }; + + match result { + Ok(Ok(result)) => return result.into_c_struct(&mut store), + #[allow(deprecated)] + #[cfg(feature = "std")] + Ok(Err(trap)) => crate::js::error::raise(Box::new(trap)), + #[cfg(feature = "core")] + #[allow(deprecated)] + Ok(Err(trap)) => crate::js::error::raise(Box::new(trap)), + Err(_panic) => unimplemented!(), } } - }; - } + + func_wrapper::< T, $( $x, )* Rets, RetsAsResult, Func > as _ + } + + } + }; +} // Here we go! Let's generate all the C struct, `WasmTypeList` // implementations and `HostFunction` implementations. diff --git a/lib/api/src/js/typed_function.rs b/lib/api/src/rt/js/entities/function/typed.rs similarity index 81% rename from lib/api/src/js/typed_function.rs rename to lib/api/src/rt/js/entities/function/typed.rs index 22cd2bf9c58..029dd229c12 100644 --- a/lib/api/src/js/typed_function.rs +++ b/lib/api/src/rt/js/entities/function/typed.rs @@ -7,12 +7,11 @@ //! let add_one = instance.exports.get_function("function_name")?; //! let add_one_native: TypedFunction = add_one.typed().unwrap(); //! ``` -use crate::native_type::NativeWasmTypeInto; -use crate::Value; -use crate::{AsStoreMut, TypedFunction}; -use crate::{FromToNativeWasmType, RuntimeError, WasmTypeList}; -// use std::panic::{catch_unwind, AssertUnwindSafe}; -use crate::js::as_js::{param_from_js, AsJs}; +use crate::{ + js::utils::convert::{js_value_to_wasmer, AsJs}, + AsStoreMut, FromToNativeWasmType, NativeWasmType, NativeWasmTypeInto, RuntimeError, + TypedFunction, Value, WasmTypeList, +}; use js_sys::Array; use std::iter::FromIterator; use wasm_bindgen::JsValue; @@ -28,18 +27,18 @@ macro_rules! impl_native_traits { { /// Call the typed func and return results. #[allow(clippy::too_many_arguments)] - pub fn call(&self, mut store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result where - $( $x: FromToNativeWasmType + NativeWasmTypeInto, )* + pub fn call_js(&self, mut store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result where + $( $x: FromToNativeWasmType, )* { #[allow(unused_unsafe)] let params_list: Vec<_> = unsafe { - vec![ $( ($x::WASM_TYPE, $x.into_raw(store) ) ),* ] + vec![ $( (<$x::Native as NativeWasmType>::WASM_TYPE, $x.to_native().into_raw(store) ) ),* ] }; let results = { let mut r; // TODO: This loop is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 loop { - r = self.func.0.handle.function.apply( + r = self.func.as_js().handle.function.apply( &JsValue::UNDEFINED, unsafe { &Array::from_iter(params_list.clone() @@ -66,7 +65,7 @@ macro_rules! impl_native_traits { 0 => {}, 1 => unsafe { let ty = Rets::wasm_types()[0]; - let val = param_from_js(&ty, &results); + let val = js_value_to_wasmer(&ty, &results); *mut_rets = val.as_raw(&mut store); } _n => { @@ -74,7 +73,7 @@ macro_rules! impl_native_traits { for (i, ret_type) in Rets::wasm_types().iter().enumerate() { let ret = results.get(i as u32); unsafe { - let val = param_from_js(&ret_type, &ret); + let val = js_value_to_wasmer(&ret_type, &ret); let slot = mut_rets.add(i); *slot = val.as_raw(&mut store); } @@ -83,6 +82,14 @@ macro_rules! impl_native_traits { } Ok(unsafe { Rets::from_array(store, rets_list_array) }) } + + #[doc(hidden)] + #[allow(missing_docs)] + #[allow(unused_mut)] + #[allow(clippy::too_many_arguments)] + pub fn call_raw_js(&self, store: &mut impl AsStoreMut, mut params_list: Vec ) -> Result { + todo!("Raw calls from js are not supported yet!") + } } }; } diff --git a/lib/api/src/js/externals/global.rs b/lib/api/src/rt/js/entities/global.rs similarity index 73% rename from lib/api/src/js/externals/global.rs rename to lib/api/src/rt/js/entities/global.rs index d9be955f60a..e9b68ed79b2 100644 --- a/lib/api/src/js/externals/global.rs +++ b/lib/api/src/rt/js/entities/global.rs @@ -1,14 +1,14 @@ -use crate::errors::RuntimeError; -use crate::js::wasm_bindgen_polyfill::Global as JSGlobal; -use crate::store::{AsStoreMut, AsStoreRef}; -use crate::value::Value; -use crate::vm::{VMExtern, VMGlobal}; -use crate::GlobalType; -use crate::Mutability; -use wasm_bindgen::JsValue; -use wasmer_types::{RawValue, Type}; +use wasm_bindgen::{prelude::*, JsValue}; +use wasmer_types::{GlobalType, Mutability, RawValue, Type}; -#[derive(Debug, Clone, PartialEq)] +use crate::{ + js::{utils::polyfill::Global as JsGlobal, vm::VMGlobal}, + vm::{VMExtern, VMExternGlobal}, + AsStoreMut, AsStoreRef, RuntimeError, RuntimeGlobal, Value, +}; + +#[derive(Debug, Clone, PartialEq, Eq)] +/// A WebAssembly `global` in `js`. pub struct Global { pub(crate) handle: VMGlobal, } @@ -19,7 +19,7 @@ pub struct Global { impl Global { pub(crate) fn to_vm_extern(&self) -> VMExtern { - VMExtern::Global(self.handle.clone()) + VMExtern::Js(crate::js::vm::VMExtern::Global(self.handle.clone())) } /// Create a `Global` with the initial value [`Value`] and the provided [`Mutability`]. @@ -54,10 +54,10 @@ impl Global { &mutability.is_mutable().into(), )?; - let js_global = JSGlobal::new(&descriptor, &value).unwrap(); + let js_global = JsGlobal::new(&descriptor, &value).unwrap(); let vm_global = VMGlobal::new(js_global, global_ty); - Ok(Self::from_vm_extern(store, vm_global)) + Ok(Self::from_vm_extern(store, VMExternGlobal::Js(vm_global))) } pub fn ty(&self, _store: &impl AsStoreRef) -> GlobalType { @@ -127,13 +127,41 @@ impl Global { Ok(()) } - pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_global: VMGlobal) -> Self { + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_global: VMExternGlobal) -> Self { use crate::js::store::StoreObject; - VMGlobal::list_mut(store.objects_mut()).push(vm_global.clone()); - Self { handle: vm_global } + VMGlobal::list_mut(store.objects_mut().as_js_mut()).push(vm_global.as_js().clone()); + Self { + handle: vm_global.into_js(), + } } pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { true } } + +impl crate::Global { + /// Consume [`self`] into [`crate::rt::js::global::Global`]. + pub fn into_js(self) -> crate::rt::js::global::Global { + match self.0 { + RuntimeGlobal::Js(s) => s, + _ => panic!("Not a `js` global!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::js::global::Global`]. + pub fn as_js(&self) -> &crate::rt::js::global::Global { + match self.0 { + RuntimeGlobal::Js(ref s) => s, + _ => panic!("Not a `js` global!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::js::global::Global`]. + pub fn as_js_mut(&mut self) -> &mut crate::rt::js::global::Global { + match self.0 { + RuntimeGlobal::Js(ref mut s) => s, + _ => panic!("Not a `js` global!"), + } + } +} diff --git a/lib/api/src/js/instance.rs b/lib/api/src/rt/js/entities/instance.rs similarity index 66% rename from lib/api/src/js/instance.rs rename to lib/api/src/rt/js/entities/instance.rs index 49726418d4c..0fd79371999 100644 --- a/lib/api/src/js/instance.rs +++ b/lib/api/src/rt/js/entities/instance.rs @@ -1,14 +1,15 @@ -use crate::exports::Exports; -use crate::imports::Imports; -use crate::js::as_js::AsJs; -use crate::js::vm::VMInstance; -use crate::module::Module; -use crate::store::AsStoreMut; -use crate::Extern; -use crate::{errors::InstantiationError, js::js_handle::JsHandle}; use js_sys::WebAssembly; +use crate::{ + js::{ + utils::{convert::AsJs, js_handle::JsHandle}, + vm::VMInstance, + }, + AsStoreMut, Exports, Extern, Imports, InstantiationError, Module, RuntimeInstance, +}; + #[derive(Clone, PartialEq, Eq)] +/// A WebAssembly `instance` in `js`. pub struct Instance { pub(crate) _handle: JsHandle, } @@ -24,7 +25,7 @@ impl Instance { imports: &Imports, ) -> Result<(Self, Exports), InstantiationError> { let instance = module - .0 + .as_js() .instantiate(&mut store, imports) .map_err(|e| InstantiationError::Start(e))?; @@ -74,3 +75,29 @@ impl Instance { Ok((instance, exports)) } } + +impl crate::Instance { + /// Consume [`self`] into [`crate::rt::js::instance::Instance`]. + pub fn into_js(self) -> crate::rt::js::instance::Instance { + match self._inner { + RuntimeInstance::Js(s) => s, + _ => panic!("Not a `js` global!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::js::instance::Instance`]. + pub fn as_js(&self) -> &crate::rt::js::instance::Instance { + match self._inner { + RuntimeInstance::Js(ref s) => s, + _ => panic!("Not a `js` global!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference to [`crate::rt::js::instance::Instance`]. + pub fn as_js_mut(&mut self) -> &mut crate::rt::js::instance::Instance { + match self._inner { + RuntimeInstance::Js(ref mut s) => s, + _ => panic!("Not a `js` global!"), + } + } +} diff --git a/lib/api/src/rt/js/entities/memory/buffer.rs b/lib/api/src/rt/js/entities/memory/buffer.rs new file mode 100644 index 00000000000..127cc59dd42 --- /dev/null +++ b/lib/api/src/rt/js/entities/memory/buffer.rs @@ -0,0 +1,76 @@ +use super::Memory; +use crate::{js::store::StoreObjects, MemoryAccessError}; +use std::{marker::PhantomData, mem::MaybeUninit, slice}; +use tracing::warn; + +/// Underlying buffer for a memory. +#[derive(Copy, Clone, Debug)] +pub(crate) struct MemoryBuffer<'a> { + pub(crate) base: *mut js_sys::Uint8Array, + pub(crate) marker: PhantomData<(&'a Memory, &'a StoreObjects)>, +} + +impl<'a> MemoryBuffer<'a> { + pub(crate) fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> { + let end = offset + .checked_add(buf.len() as u64) + .ok_or(MemoryAccessError::Overflow)?; + let view = unsafe { &*(self.base) }; + if end > view.length().into() { + warn!( + "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", + buf.len(), + end, + view.length() + ); + return Err(MemoryAccessError::HeapOutOfBounds); + } + view.subarray(offset as _, end as _) + .copy_to(unsafe { &mut slice::from_raw_parts_mut(buf.as_mut_ptr(), buf.len()) }); + Ok(()) + } + + pub(crate) fn read_uninit<'b>( + &self, + offset: u64, + buf: &'b mut [MaybeUninit], + ) -> Result<&'b mut [u8], MemoryAccessError> { + let end = offset + .checked_add(buf.len() as u64) + .ok_or(MemoryAccessError::Overflow)?; + let view = unsafe { &*(self.base) }; + if end > view.length().into() { + warn!( + "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", + buf.len(), + end, + view.length() + ); + return Err(MemoryAccessError::HeapOutOfBounds); + } + let buf_ptr = buf.as_mut_ptr() as *mut u8; + view.subarray(offset as _, end as _) + .copy_to(unsafe { &mut slice::from_raw_parts_mut(buf_ptr, buf.len()) }); + + Ok(unsafe { slice::from_raw_parts_mut(buf_ptr, buf.len()) }) + } + + pub(crate) fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { + let end = offset + .checked_add(data.len() as u64) + .ok_or(MemoryAccessError::Overflow)?; + let view = unsafe { &mut *(self.base) }; + if end > view.length().into() { + warn!( + "attempted to write ({} bytes) beyond the bounds of the memory view ({} > {})", + data.len(), + end, + view.length() + ); + return Err(MemoryAccessError::HeapOutOfBounds); + } + view.subarray(offset as _, end as _).copy_from(data); + + Ok(()) + } +} diff --git a/lib/api/src/rt/js/entities/memory/js.rs b/lib/api/src/rt/js/entities/memory/js.rs new file mode 100644 index 00000000000..272df644a0f --- /dev/null +++ b/lib/api/src/rt/js/entities/memory/js.rs @@ -0,0 +1,34 @@ +use js_sys::wasm_bindgen; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern "C" { + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory) + #[wasm_bindgen(js_namespace = WebAssembly, extends = js_sys::Object, typescript_type = "WebAssembly.Memory")] + #[derive(Clone, Debug, PartialEq, Eq)] + pub type JSMemory; + + /// The `grow()` protoype method of the `Memory` object increases the + /// size of the memory instance by a specified number of WebAssembly + /// pages. + /// + /// Takes the number of pages to grow (64KiB in size) and returns the + /// previous size of memory, in pages. + /// + /// # Reimplementation + /// + /// We re-implement `WebAssembly.Memory.grow` because it is + /// different from what `wasm-bindgen` declares. It marks the function + /// as `catch`, which means it can throw an exception. + /// + /// See [the opened patch](https://github.com/rustwasm/wasm-bindgen/pull/2599). + /// + /// # Exceptions + /// + /// A `RangeError` is thrown if adding pages would exceed the maximum + /// memory. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory/grow) + #[wasm_bindgen(catch, method, js_namespace = WebAssembly)] + pub fn grow(this: &JSMemory, pages: u32) -> Result; +} diff --git a/lib/api/src/js/externals/memory.rs b/lib/api/src/rt/js/entities/memory/mod.rs similarity index 51% rename from lib/api/src/js/externals/memory.rs rename to lib/api/src/rt/js/entities/memory/mod.rs index ebd8b3e3751..e21d6f80153 100644 --- a/lib/api/src/js/externals/memory.rs +++ b/lib/api/src/rt/js/entities/memory/mod.rs @@ -1,54 +1,22 @@ -use crate::js::vm::{VMExtern, VMMemory}; -use crate::mem_access::MemoryAccessError; -use crate::store::{AsStoreMut, AsStoreRef, StoreObjects}; -use crate::MemoryType; -use std::marker::PhantomData; -use std::mem::MaybeUninit; -use std::slice; +pub(crate) mod js; +pub(crate) use js::*; -use tracing::warn; +pub(crate) mod view; +pub(crate) use view::*; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasmer_types::Pages; - -use super::memory_view::MemoryView; +pub(crate) mod buffer; +pub(crate) use buffer::*; -pub use wasmer_types::MemoryError; +use wasm_bindgen::JsCast; +use wasmer_types::{MemoryError, MemoryType, Pages}; -#[wasm_bindgen] -extern "C" { - /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory) - #[wasm_bindgen(js_namespace = WebAssembly, extends = js_sys::Object, typescript_type = "WebAssembly.Memory")] - #[derive(Clone, Debug, PartialEq, Eq)] - pub type JSMemory; +use crate::{ + js::vm::memory::VMMemory, + vm::{VMExtern, VMExternMemory}, + AsStoreMut, AsStoreRef, RuntimeMemory, +}; - /// The `grow()` protoype method of the `Memory` object increases the - /// size of the memory instance by a specified number of WebAssembly - /// pages. - /// - /// Takes the number of pages to grow (64KiB in size) and returns the - /// previous size of memory, in pages. - /// - /// # Reimplementation - /// - /// We re-implement `WebAssembly.Memory.grow` because it is - /// different from what `wasm-bindgen` declares. It marks the function - /// as `catch`, which means it can throw an exception. - /// - /// See [the opened patch](https://github.com/rustwasm/wasm-bindgen/pull/2599). - /// - /// # Exceptions - /// - /// A `RangeError` is thrown if adding pages would exceed the maximum - /// memory. - /// - /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory/grow) - #[wasm_bindgen(catch, method, js_namespace = WebAssembly)] - pub fn grow(this: &JSMemory, pages: u32) -> Result; -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq)] pub struct Memory { pub(crate) handle: VMMemory, } @@ -71,7 +39,7 @@ unsafe impl Sync for Memory {} impl Memory { pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result { let vm_memory = VMMemory::new(Self::js_memory_from_type(&ty)?, ty); - Ok(Self::from_vm_extern(store, vm_memory)) + Ok(Self::from_vm_extern(store, VMExternMemory::Js(vm_memory))) } pub(crate) fn js_memory_from_type( @@ -103,11 +71,11 @@ impl Memory { } pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { - Self::from_vm_extern(new_store, memory) + Self::from_vm_extern(new_store, VMExternMemory::Js(memory)) } pub(crate) fn to_vm_extern(&self) -> VMExtern { - VMExtern::Memory(self.handle.clone()) + VMExtern::Js(crate::js::vm::VMExtern::Memory(self.handle.clone())) } pub fn ty(&self, _store: &impl AsStoreRef) -> MemoryType { @@ -163,8 +131,10 @@ impl Memory { Ok(()) } - pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, internal: VMMemory) -> Self { - Self { handle: internal } + pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, internal: VMExternMemory) -> Self { + Self { + handle: internal.into_js(), + } } /// Cloning memory will create another reference to the same memory that @@ -184,7 +154,7 @@ impl Memory { true } - pub fn as_shared(&self, _store: &impl AsStoreRef) -> Option { + pub fn as_shared(&self, _store: &impl AsStoreRef) -> Option { // Not supported. None } @@ -198,84 +168,38 @@ impl std::cmp::PartialEq for Memory { impl From for crate::Memory { fn from(value: Memory) -> Self { - crate::Memory(value) + crate::Memory(crate::RuntimeMemory::Js(value)) } } impl From for Memory { fn from(value: crate::Memory) -> Self { - value.0 + value.into_js() } } -/// Underlying buffer for a memory. -#[derive(Copy, Clone, Debug)] -pub(crate) struct MemoryBuffer<'a> { - pub(crate) base: *mut js_sys::Uint8Array, - pub(crate) marker: PhantomData<(&'a Memory, &'a StoreObjects)>, -} - -impl<'a> MemoryBuffer<'a> { - pub(crate) fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> { - let end = offset - .checked_add(buf.len() as u64) - .ok_or(MemoryAccessError::Overflow)?; - let view = unsafe { &*(self.base) }; - if end > view.length().into() { - warn!( - "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", - buf.len(), - end, - view.length() - ); - return Err(MemoryAccessError::HeapOutOfBounds); +impl crate::Memory { + /// Consume [`self`] into a [`crate::rt::js::mem::Memory`]. + pub fn into_js(self) -> crate::rt::js::memory::Memory { + match self.0 { + RuntimeMemory::Js(s) => s, + _ => panic!("Not a `js` memory!"), } - view.subarray(offset as _, end as _) - .copy_to(unsafe { &mut slice::from_raw_parts_mut(buf.as_mut_ptr(), buf.len()) }); - Ok(()) } - pub(crate) fn read_uninit<'b>( - &self, - offset: u64, - buf: &'b mut [MaybeUninit], - ) -> Result<&'b mut [u8], MemoryAccessError> { - let end = offset - .checked_add(buf.len() as u64) - .ok_or(MemoryAccessError::Overflow)?; - let view = unsafe { &*(self.base) }; - if end > view.length().into() { - warn!( - "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", - buf.len(), - end, - view.length() - ); - return Err(MemoryAccessError::HeapOutOfBounds); + /// Convert a reference to [`self`] into a reference to [`crate::rt::js::mem::Memory`]. + pub fn as_js(&self) -> &crate::rt::js::memory::Memory { + match self.0 { + RuntimeMemory::Js(ref s) => s, + _ => panic!("Not a `js` memory!"), } - let buf_ptr = buf.as_mut_ptr() as *mut u8; - view.subarray(offset as _, end as _) - .copy_to(unsafe { &mut slice::from_raw_parts_mut(buf_ptr, buf.len()) }); - - Ok(unsafe { slice::from_raw_parts_mut(buf_ptr, buf.len()) }) } - pub(crate) fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { - let end = offset - .checked_add(data.len() as u64) - .ok_or(MemoryAccessError::Overflow)?; - let view = unsafe { &mut *(self.base) }; - if end > view.length().into() { - warn!( - "attempted to write ({} bytes) beyond the bounds of the memory view ({} > {})", - data.len(), - end, - view.length() - ); - return Err(MemoryAccessError::HeapOutOfBounds); + /// Convert a mutable reference to [`self`] into a mutable reference to [`crate::rt::js::mem::Memory`]. + pub fn as_js_mut(&mut self) -> &mut crate::rt::js::memory::Memory { + match self.0 { + RuntimeMemory::Js(ref mut s) => s, + _ => panic!("Not a `js` memory!"), } - view.subarray(offset as _, end as _).copy_from(data); - - Ok(()) } } diff --git a/lib/api/src/js/externals/memory_view.rs b/lib/api/src/rt/js/entities/memory/view.rs similarity index 97% rename from lib/api/src/js/externals/memory_view.rs rename to lib/api/src/rt/js/entities/memory/view.rs index f4830add65f..e4c451624d8 100644 --- a/lib/api/src/js/externals/memory_view.rs +++ b/lib/api/src/rt/js/entities/memory/view.rs @@ -1,12 +1,11 @@ -use std::{convert::TryInto, marker::PhantomData, mem::MaybeUninit, ops::Range, slice}; +use std::{marker::PhantomData, mem::MaybeUninit, ops::Range, slice}; + use wasm_bindgen::JsCast; use wasmer_types::{Bytes, Pages}; -use crate::{ - js::externals::memory::{Memory, MemoryBuffer}, - mem_access::MemoryAccessError, - store::AsStoreRef, -}; +use crate::{AsStoreRef, MemoryAccessError}; + +use super::{Memory, MemoryBuffer}; /// A WebAssembly `memory` view. /// diff --git a/lib/api/src/rt/js/entities/mod.rs b/lib/api/src/rt/js/entities/mod.rs new file mode 100644 index 00000000000..6a280e5f757 --- /dev/null +++ b/lib/api/src/rt/js/entities/mod.rs @@ -0,0 +1,9 @@ +pub(crate) mod engine; +pub(crate) mod external; +pub(crate) mod function; +pub(crate) mod global; +pub(crate) mod instance; +pub(crate) mod memory; +pub(crate) mod module; +pub(crate) mod store; +pub(crate) mod table; diff --git a/lib/api/src/js/module.rs b/lib/api/src/rt/js/entities/module.rs similarity index 86% rename from lib/api/src/js/module.rs rename to lib/api/src/rt/js/entities/module.rs index adc61d6b08b..a86dce23a4b 100644 --- a/lib/api/src/js/module.rs +++ b/lib/api/src/rt/js/entities/module.rs @@ -1,20 +1,22 @@ -use crate::errors::RuntimeError; -use crate::imports::Imports; -use crate::js::AsJs; -use crate::store::AsStoreMut; -use crate::vm::VMInstance; -use crate::Extern; -use crate::IntoBytes; -use crate::{errors::InstantiationError, js::js_handle::JsHandle}; -use crate::{AsEngineRef, ExportType, ImportType}; +use std::path::Path; + use bytes::Bytes; use js_sys::{Reflect, Uint8Array, WebAssembly}; -use std::path::Path; use tracing::{debug, warn}; -use wasm_bindgen::JsValue; +use wasm_bindgen::{prelude::*, JsValue}; use wasmer_types::{ - CompileError, DeserializeError, ExportsIterator, ExternType, FunctionType, GlobalType, - ImportsIterator, MemoryType, ModuleInfo, Mutability, Pages, SerializeError, TableType, Type, + CompileError, DeserializeError, ExportType, ExportsIterator, ExternType, FunctionType, + GlobalType, ImportType, ImportsIterator, MemoryType, ModuleInfo, Mutability, Pages, + SerializeError, TableType, Type, +}; + +use crate::{ + js::{ + utils::{convert::AsJs as _, js_handle::JsHandle}, + vm::VMInstance, + }, + AsEngineRef, AsStoreMut, Extern, Imports, InstantiationError, IntoBytes, RuntimeError, + RuntimeModule, }; /// WebAssembly in the browser doesn't yet output the descriptor/types @@ -85,7 +87,7 @@ impl Module { // The module is now validated, so we can safely parse it's types #[cfg(feature = "wasm-types-polyfill")] let (type_hints, name) = { - let info = crate::module_info_polyfill::translate_module(&binary[..]).unwrap(); + let info = crate::polyfill::translate_module(&binary[..]).unwrap(); ( Some(ModuleTypeHints { @@ -277,7 +279,7 @@ impl Module { // .unwrap_or(false) } - pub fn imports<'a>(&'a self) -> ImportsIterator + 'a> { + pub fn imports<'a>(&'a self) -> ImportsIterator + 'a>> { let imports = WebAssembly::Module::imports(&self.module); let iter = imports .iter() @@ -332,7 +334,7 @@ impl Module { }) .collect::>() .into_iter(); - ImportsIterator::new(iter, imports.length() as usize) + ImportsIterator::new(Box::new(iter), imports.length() as usize) } /// Set the type hints for this module. @@ -371,7 +373,7 @@ impl Module { Ok(()) } - pub fn exports<'a>(&'a self) -> ExportsIterator + 'a> { + pub fn exports<'a>(&'a self) -> ExportsIterator + 'a>> { let exports = WebAssembly::Module::exports(&self.module); let iter = exports .iter() @@ -425,18 +427,23 @@ impl Module { }) .collect::>() .into_iter(); - ExportsIterator::new(iter, exports.length() as usize) + ExportsIterator::new(Box::new(iter), exports.length() as usize) } - pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator> + 'a { - WebAssembly::Module::custom_sections(&self.module, name) - .iter() - .map(move |buf_val| { - let typebuf: js_sys::Uint8Array = js_sys::Uint8Array::new(&buf_val); - typebuf.to_vec().into_boxed_slice() - }) - .collect::>>() - .into_iter() + pub fn custom_sections<'a>( + &'a self, + name: &'a str, + ) -> Box> + 'a> { + Box::new( + WebAssembly::Module::custom_sections(&self.module, name) + .iter() + .map(move |buf_val| { + let typebuf: js_sys::Uint8Array = js_sys::Uint8Array::new(&buf_val); + typebuf.to_vec().into_boxed_slice() + }) + .collect::>>() + .into_iter(), + ) } pub(crate) fn info(&self) -> &ModuleInfo { @@ -459,17 +466,48 @@ impl From for Module { impl From<(WebAssembly::Module, T)> for crate::module::Module { fn from((module, binary): (WebAssembly::Module, T)) -> crate::module::Module { - unsafe { crate::module::Module(Module::from_js_module(module, binary.into_bytes())) } + unsafe { + crate::module::Module(RuntimeModule::Js(Module::from_js_module( + module, + binary.into_bytes(), + ))) + } } } impl From for crate::module::Module { fn from(module: WebAssembly::Module) -> crate::module::Module { - crate::module::Module(module.into()) + crate::module::Module(RuntimeModule::Js(module.into())) } } impl From for WebAssembly::Module { fn from(value: crate::module::Module) -> Self { - value.0.module.into_inner() + value.into_js().module.into_inner() + } +} + +impl crate::Module { + /// Consume [`self`] into a reference [`crate::rt::js::module::Module`]. + pub fn into_js(self) -> crate::rt::js::module::Module { + match self.0 { + RuntimeModule::Js(s) => s, + _ => panic!("Not a `js` module!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::js::module::Module`]. + pub fn as_js(&self) -> &crate::rt::js::module::Module { + match self.0 { + RuntimeModule::Js(ref s) => s, + _ => panic!("Not a `js` module!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::js::module::Module`]. + pub fn as_js_mut(&mut self) -> &mut crate::rt::js::module::Module { + match self.0 { + RuntimeModule::Js(ref mut s) => s, + _ => panic!("Not a `js` module!"), + } } } diff --git a/lib/api/src/rt/js/entities/store/handle.rs b/lib/api/src/rt/js/entities/store/handle.rs new file mode 100644 index 00000000000..686da2ce411 --- /dev/null +++ b/lib/api/src/rt/js/entities/store/handle.rs @@ -0,0 +1,158 @@ +use std::{marker::PhantomData, num::NonZeroUsize}; + +use wasmer_types::StoreId; + +use super::{obj::StoreObject, StoreObjects}; + +/// Handle to an object managed by a context. +/// +/// Internally this is just an integer index into a context. A reference to the +/// context must be passed in separately to access the actual object. +pub struct StoreHandle { + id: StoreId, + internal: InternalStoreHandle, +} + +impl core::cmp::PartialEq for StoreHandle { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl std::hash::Hash for StoreHandle { + fn hash(&self, state: &mut H) { + self.id.hash(state); + self.internal.idx.hash(state); + } +} + +impl Clone for StoreHandle { + fn clone(&self) -> Self { + Self { + id: self.id, + internal: self.internal, + } + } +} + +impl std::fmt::Debug for StoreHandle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("StoreHandle") + .field("id", &self.id) + .field("internal", &self.internal.index()) + .finish() + } +} + +impl StoreHandle { + /// Moves the given object into a context and returns a handle to it. + pub fn new(store: &mut StoreObjects, val: T) -> Self { + Self { + id: store.id(), + internal: InternalStoreHandle::new(store, val), + } + } + + /// Returns a reference to the object that this handle points to. + pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T { + assert_eq!(self.id, store.id(), "object used with the wrong context"); + self.internal.get(store) + } + + /// Returns a mutable reference to the object that this handle points to. + pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T { + assert_eq!(self.id, store.id(), "object used with the wrong context"); + self.internal.get_mut(store) + } + + /// Returns the internal handle contains within this handle. + pub fn internal_handle(&self) -> InternalStoreHandle { + self.internal + } + + /// Returns the ID of the context associated with the handle. + #[allow(unused)] + pub fn store_id(&self) -> StoreId { + self.id + } + + /// Overrides the store id with a new ID + #[allow(unused)] + pub fn set_store_id(&mut self, id: StoreId) { + self.id = id; + } + + /// Constructs a `StoreHandle` from a `StoreId` and an `InternalStoreHandle`. + /// + /// # Safety + /// Handling `InternalStoreHandle` values is unsafe because they do not track context ID. + pub unsafe fn from_internal(id: StoreId, internal: InternalStoreHandle) -> Self { + Self { id, internal } + } +} + +/// Internal handle to an object owned by the current context. +/// +/// Unlike `StoreHandle` this does not track the context ID: it is only +/// intended to be used within objects already owned by a context. +#[repr(transparent)] +pub struct InternalStoreHandle { + // Use a NonZero here to reduce the size of Option. + idx: NonZeroUsize, + marker: PhantomData T>, +} + +impl Clone for InternalStoreHandle { + fn clone(&self) -> Self { + *self + } +} +impl Copy for InternalStoreHandle {} + +impl std::fmt::Debug for InternalStoreHandle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("InternalStoreHandle") + .field("idx", &self.idx) + .finish() + } +} +impl PartialEq for InternalStoreHandle { + fn eq(&self, other: &Self) -> bool { + self.idx == other.idx + } +} +impl Eq for InternalStoreHandle {} + +impl InternalStoreHandle { + /// Moves the given object into a context and returns a handle to it. + pub fn new(store: &mut StoreObjects, val: T) -> Self { + let list = T::list_mut(store); + let idx = NonZeroUsize::new(list.len() + 1).unwrap(); + list.push(val); + Self { + idx, + marker: PhantomData, + } + } + + /// Returns a reference to the object that this handle points to. + pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T { + &T::list(store)[self.idx.get() - 1] + } + + /// Returns a mutable reference to the object that this handle points to. + pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T { + &mut T::list_mut(store)[self.idx.get() - 1] + } + + pub(crate) fn index(&self) -> usize { + self.idx.get() + } + + pub(crate) fn from_index(idx: usize) -> Option { + NonZeroUsize::new(idx).map(|idx| Self { + idx, + marker: PhantomData, + }) + } +} diff --git a/lib/api/src/rt/js/entities/store/mod.rs b/lib/api/src/rt/js/entities/store/mod.rs new file mode 100644 index 00000000000..4b2ebfd2969 --- /dev/null +++ b/lib/api/src/rt/js/entities/store/mod.rs @@ -0,0 +1,84 @@ +use crate::{AsEngineRef, Engine, EngineRef}; + +pub(crate) mod handle; +pub(crate) mod obj; + +pub(crate) use handle::*; +pub(crate) use obj::*; + +#[derive(Debug)] +pub(crate) struct Store { + pub(crate) engine: Engine, +} + +impl Store { + pub(crate) fn new(engine: Engine) -> Self { + Self { engine } + } + + pub(crate) fn engine(&self) -> &Engine { + &self.engine + } + + pub(crate) fn engine_mut(&mut self) -> &mut Engine { + &mut self.engine + } +} + +impl AsEngineRef for Store { + fn as_engine_ref(&self) -> EngineRef<'_> { + EngineRef::new(&self.engine) + } +} + +impl crate::RuntimeStore { + /// Consume [`self`] into [`crate::rt::js::store::Store`]. + pub fn into_js(self) -> crate::rt::js::store::Store { + match self { + Self::Js(s) => s, + _ => panic!("Not a `js` store!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::js::store::Store`]. + pub fn as_js(&self) -> &crate::rt::js::store::Store { + match self { + Self::Js(s) => s, + _ => panic!("Not a `js` store!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::js::store::Store`]. + pub fn as_js_mut(&mut self) -> &mut crate::rt::js::store::Store { + match self { + Self::Js(s) => s, + _ => panic!("Not a `js` store!"), + } + } + /// Return true if [`self`] is a store from the `js` runtime. + pub fn is_js(&self) -> bool { + matches!(self, Self::Js(_)) + } +} + +impl crate::Store { + /// Consume [`self`] into [`crate::rt::js::store::Store`]. + pub(crate) fn into_js(self) -> crate::rt::js::store::Store { + self.inner.store.into_js() + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::js::store::Store`]. + pub(crate) fn as_js(&self) -> &crate::rt::js::store::Store { + self.inner.store.as_js() + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::js::store::Store`]. + pub(crate) fn as_js_mut(&mut self) -> &mut crate::rt::js::store::Store { + self.inner.store.as_js_mut() + } + + /// Return true if [`self`] is a store from the `js` runtime. + pub fn is_js(&self) -> bool { + self.inner.store.is_js() + } +} diff --git a/lib/api/src/rt/js/entities/store/obj.rs b/lib/api/src/rt/js/entities/store/obj.rs new file mode 100644 index 00000000000..3f67398fcb5 --- /dev/null +++ b/lib/api/src/rt/js/entities/store/obj.rs @@ -0,0 +1,135 @@ +use std::{marker::PhantomData, num::NonZeroUsize}; + +use wasm_bindgen::JsValue; +use wasmer_types::StoreId; + +use crate::js::vm::{function::VMFunctionEnvironment, global::VMGlobal}; + +use super::handle::InternalStoreHandle; + +/// Trait to represent an object managed by a context. This is implemented on +/// the VM types managed by the context. +pub trait StoreObject: Sized { + fn list(store: &StoreObjects) -> &Vec; + fn list_mut(store: &mut StoreObjects) -> &mut Vec; +} + +macro_rules! impl_store_object { + ($($field:ident => $ty:ty,)*) => { + $( + impl StoreObject for $ty { + fn list(store: &StoreObjects) -> &Vec { + &store.$field + } + fn list_mut(store: &mut StoreObjects) -> &mut Vec { + &mut store.$field + } + } + )* + }; +} + +impl_store_object! { + // Note: we store the globals in order to be able to access them later via + // `StoreObjects::iter_globals`. + globals => VMGlobal, + // functions => VMFunction, + // tables => VMTable, + // memories => VMMemory, + // The function environments are the only things attached to a store, + // since the other JS objects (table, globals, memory and functions) + // live in the JS VM Store by default + function_environments => VMFunctionEnvironment, +} + +/// Set of objects managed by a context. +#[derive(Default, Debug)] +pub struct StoreObjects { + id: StoreId, + globals: Vec, + function_environments: Vec, +} + +impl StoreObjects { + /// Returns the ID of this context. + pub fn id(&self) -> StoreId { + self.id + } + + /// Sets the ID of this store + pub fn set_id(&mut self, id: StoreId) { + self.id = id; + } + + /// Returns a pair of mutable references from two handles. + /// + /// Panics if both handles point to the same object. + pub fn get_2_mut( + &mut self, + a: InternalStoreHandle, + b: InternalStoreHandle, + ) -> (&mut T, &mut T) { + assert_ne!(a.index(), b.index()); + let list = T::list_mut(self); + if a.index() < b.index() { + let (low, high) = list.split_at_mut(b.index()); + (&mut low[a.index()], &mut high[0]) + } else { + let (low, high) = list.split_at_mut(a.index()); + (&mut high[0], &mut low[a.index()]) + } + } + + /// Return an immutable iterator over all globals + pub fn iter_globals(&self) -> core::slice::Iter { + self.globals.iter() + } + + /// Return an vector of all globals and converted to u128 + pub fn as_u128_globals(&self) -> Vec { + self.iter_globals() + .map(|v| v.global.value().as_f64().unwrap() as u128) + .collect() + } + + /// Set a global, at index idx. Will panic if idx is out of range + /// Safety: the caller should check that the raw value is compatible + /// with destination VMGlobal type + pub fn set_global_unchecked(&self, idx: usize, new_val: u128) { + assert!(idx < self.globals.len()); + + let g = &self.globals[idx].global; + let cur_val = g.value().as_f64().unwrap(); + let new_val = new_val as f64; + if cur_val != new_val { + let new_value = JsValue::from(new_val); + g.set_value(&new_value); + } + } +} + +impl crate::StoreObjects { + /// Consume [`self`] into [`crate::rt::js::store::StoreObjects`]. + pub fn into_js(self) -> crate::rt::js::store::StoreObjects { + match self { + Self::Js(s) => s, + _ => panic!("Not a `js` store!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::js::store::StoreObjects`]. + pub fn as_js(&self) -> &crate::rt::js::store::StoreObjects { + match self { + Self::Js(s) => s, + _ => panic!("Not a `js` store!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::js::store::StoreObjects`]. + pub fn as_js_mut(&mut self) -> &mut crate::rt::js::store::StoreObjects { + match self { + Self::Js(s) => s, + _ => panic!("Not a `js` store!"), + } + } +} diff --git a/lib/api/src/js/externals/table.rs b/lib/api/src/rt/js/entities/table.rs similarity index 57% rename from lib/api/src/js/externals/table.rs rename to lib/api/src/rt/js/entities/table.rs index f7c2cdda4e6..0ed07d78a50 100644 --- a/lib/api/src/js/externals/table.rs +++ b/lib/api/src/rt/js/entities/table.rs @@ -1,12 +1,12 @@ -use crate::errors::RuntimeError; -use crate::store::{AsStoreMut, AsStoreRef}; -use crate::value::Value; -use crate::vm::VMExternTable; -use crate::vm::{VMExtern, VMFunction, VMTable}; -use crate::{FunctionType, TableType}; +use crate::{ + js::vm::{VMFunction, VMTable}, + vm::{VMExtern, VMExternTable}, + AsStoreMut, AsStoreRef, RuntimeError, RuntimeTable, Value, +}; use js_sys::Function; +use wasmer_types::{FunctionType, TableType}; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Table { pub(crate) handle: VMTable, } @@ -24,7 +24,7 @@ fn get_function(store: &mut impl AsStoreMut, val: Value) -> Result Ok(func.0.handle.function.clone().into_inner()), + Value::FuncRef(Some(ref func)) => Ok(func.as_js().handle.function.clone().into_inner()), // Only funcrefs is supported by the spec atm _ => unimplemented!("The {val:?} is not yet supported"), } @@ -57,7 +57,7 @@ impl Table { } pub fn to_vm_extern(&self) -> VMExtern { - VMExtern::Table(self.handle.clone()) + VMExtern::Js(crate::js::vm::VMExtern::Table(self.handle.clone())) } pub fn ty(&self, _store: &impl AsStoreRef) -> TableType { @@ -68,7 +68,10 @@ impl Table { if let Some(func) = self.handle.table.get(index).ok() { let ty = FunctionType::new(vec![], vec![]); let vm_function = VMFunction::new(func, ty); - let function = crate::Function::from_vm_extern(store, vm_function); + let function = crate::Function::from_vm_extern( + store, + crate::vm::VMExternFunction::Js(vm_function), + ); Some(Value::FuncRef(Some(function))) } else { None @@ -110,10 +113,64 @@ impl Table { } pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, vm_extern: VMExternTable) -> Self { - Self { handle: vm_extern } + Self { + handle: vm_extern.into_js(), + } } pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { true } } + +impl crate::Table { + /// Consume [`self`] into [`crate::rt::js::table::Table`]. + pub fn into_js(self) -> crate::rt::js::table::Table { + match self.0 { + RuntimeTable::Js(s) => s, + _ => panic!("Not a `js` table!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::js::table::Table`]. + pub fn as_js(&self) -> &crate::rt::js::table::Table { + match self.0 { + RuntimeTable::Js(ref s) => s, + _ => panic!("Not a `js` table!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::js::table::Table`]. + pub fn as_js_mut(&mut self) -> &mut crate::rt::js::table::Table { + match self.0 { + RuntimeTable::Js(ref mut s) => s, + _ => panic!("Not a `js` table!"), + } + } +} + +impl crate::RuntimeTable { + /// Consume [`self`] into [`crate::rt::js::table::Table`]. + pub fn into_js(self) -> crate::rt::js::table::Table { + match self { + Self::Js(s) => s, + _ => panic!("Not a `js` table!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::js::table::Table`]. + pub fn as_js(&self) -> &crate::rt::js::table::Table { + match self { + Self::Js(ref s) => s, + _ => panic!("Not a `js` table!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::js::table::Table`]. + pub fn as_js_mut(&mut self) -> &mut crate::rt::js::table::Table { + match self { + Self::Js(ref mut s) => s, + _ => panic!("Not a `js` table!"), + } + } +} diff --git a/lib/api/src/js/trap.rs b/lib/api/src/rt/js/error.rs similarity index 81% rename from lib/api/src/js/trap.rs rename to lib/api/src/rt/js/error.rs index 23a002e066f..652769d1edd 100644 --- a/lib/api/src/js/trap.rs +++ b/lib/api/src/rt/js/error.rs @@ -1,7 +1,4 @@ -use std::{ - error::Error, - fmt::{self, Display}, -}; +use std::error::Error; use js_sys::Reflect; use wasm_bindgen::{prelude::*, JsValue}; @@ -71,8 +68,8 @@ impl std::error::Error for Trap { } } -impl fmt::Display for Trap { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl std::fmt::Display for Trap { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self.inner { InnerTrap::User(e) => write!(f, "user: {e}"), InnerTrap::Js(value) => write!(f, "js: {value}"), @@ -169,11 +166,47 @@ impl From for JsTrap { } } -impl Display for JsTrap { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl std::fmt::Display for JsTrap { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { JsTrap::Message(m) => write!(f, "{m}"), JsTrap::Unknown => write!(f, "unknown"), } } } + +impl From for Trap { + fn from(value: JsTrap) -> Self { + Self { + inner: InnerTrap::Js(value), + } + } +} + +impl From for RuntimeError { + fn from(value: JsTrap) -> Self { + Into::::into(value).into() + } +} + +impl From for RuntimeError { + fn from(trap: Trap) -> Self { + if trap.is::() { + return trap.downcast::().unwrap(); + } + + RuntimeError::new_from_source(crate::RuntimeTrap::Js(trap), vec![], None) + } +} + +impl From for wasm_bindgen::JsValue { + fn from(value: RuntimeError) -> Self { + wasm_bindgen::JsValue::from(value.to_string()) + } +} + +pub(crate) fn raise(error: Box) -> ! { + let error = Trap::user(error); + let js_error: JsValue = error.into(); + wasm_bindgen::throw_val(js_error) +} diff --git a/lib/api/src/rt/js/mod.rs b/lib/api/src/rt/js/mod.rs new file mode 100644 index 00000000000..4ff2a3aed73 --- /dev/null +++ b/lib/api/src/rt/js/mod.rs @@ -0,0 +1,8 @@ +//! Data types, functions and traits for the `sys` runtime. + +pub(crate) mod entities; +pub(crate) mod error; +pub(crate) mod utils; +pub(crate) mod vm; + +pub use entities::*; diff --git a/lib/api/src/js/as_js.rs b/lib/api/src/rt/js/utils/convert.rs similarity index 81% rename from lib/api/src/js/as_js.rs rename to lib/api/src/rt/js/utils/convert.rs index 5068c8b1270..2dede875f54 100644 --- a/lib/api/src/js/as_js.rs +++ b/lib/api/src/rt/js/utils/convert.rs @@ -1,23 +1,25 @@ -//use crate::js::externals::Function; -// use crate::store::{Store, StoreObject}; -// use crate::js::RuntimeError; -use crate::imports::Imports; -use crate::instance::Instance; -use crate::js::instance::Instance as JsInstance; -use crate::js::vm::{VMFunction, VMGlobal, VMMemory, VMTable}; -use crate::js::wasm_bindgen_polyfill::Global as JsGlobal; -use crate::store::{AsStoreMut, AsStoreRef}; -use crate::value::Value; -use crate::Type; -use crate::{Extern, Function, Global, Memory, Table}; -use js_sys::Function as JsFunction; -use js_sys::WebAssembly::{Memory as JsMemory, Table as JsTable}; -use std::collections::HashMap; -use std::convert::TryInto; -use wasm_bindgen::JsCast; -use wasm_bindgen::{JsError, JsValue}; +use std::{collections::HashMap, convert::TryInto}; + +use js_sys::{ + Function as JsFunction, + WebAssembly::{Memory as JsMemory, Table as JsTable}, +}; +use wasm_bindgen::{JsCast, JsError, JsValue}; use wasmer_types::ExternType; +use crate::{ + imports::Imports, + instance::Instance, + js::{ + instance::Instance as JsInstance, + utils::polyfill::Global as JsGlobal, + vm::{VMFunction, VMGlobal, VMMemory, VMTable}, + }, + store::{AsStoreMut, AsStoreRef}, + value::Value, + Extern, Function, Global, Memory, Table, Type, +}; + /// Convert the given type to a [`JsValue`]. pub trait AsJs: Sized { /// The inner definition type from this Javascript object @@ -33,7 +35,7 @@ pub trait AsJs: Sized { } #[inline] -pub fn param_from_js(ty: &Type, js_val: &JsValue) -> Value { +pub fn js_value_to_wasmer(ty: &Type, js_val: &JsValue) -> Value { match ty { Type::I32 => Value::I32(js_val.as_f64().unwrap() as _), Type::I64 => Value::I64(if js_val.is_bigint() { @@ -54,6 +56,21 @@ pub fn param_from_js(ty: &Type, js_val: &JsValue) -> Value { } } +#[inline] +pub fn wasmer_value_to_js(val: &Value) -> JsValue { + match val { + Value::I32(i) => JsValue::from_f64(*i as _), + Value::I64(i) => JsValue::from_f64(*i as _), + Value::F32(f) => JsValue::from_f64(*f as _), + Value::F64(f) => JsValue::from_f64(*f), + Value::V128(f) => JsValue::from_f64(*f as _), + val => unimplemented!( + "The value `{:?}` is not yet supported in the JS Function API", + val + ), + } +} + impl AsJs for Value { type DefinitionType = Type; @@ -64,7 +81,7 @@ impl AsJs for Value { Self::F32(f) => JsValue::from(*f), Self::F64(f) => JsValue::from(*f), Self::V128(v) => JsValue::from(*v), - Self::FuncRef(Some(func)) => func.0.handle.function.clone().into(), + Self::FuncRef(Some(func)) => func.as_js().handle.function.clone().into(), Self::FuncRef(None) => JsValue::null(), Self::ExternRef(_) => unimplemented!(), } @@ -75,7 +92,7 @@ impl AsJs for Value { type_: &Self::DefinitionType, value: &JsValue, ) -> Result { - Ok(param_from_js(type_, value)) + Ok(js_value_to_wasmer(type_, value)) } } @@ -165,10 +182,10 @@ impl AsJs for Extern { fn as_jsvalue(&self, _store: &impl AsStoreRef) -> wasm_bindgen::JsValue { match self { - Self::Memory(memory) => memory.0.handle.memory.clone().into(), - Self::Function(function) => function.0.handle.function.clone().into(), - Self::Table(table) => table.0.handle.table.clone().into(), - Self::Global(global) => global.0.handle.global.clone().into(), + Self::Memory(memory) => memory.as_js().handle.memory.clone().into(), + Self::Function(function) => function.as_js().handle.function.clone().into(), + Self::Table(table) => table.as_js().handle.table.clone().into(), + Self::Global(global) => global.as_js().handle.global.clone().into(), } } @@ -201,7 +218,7 @@ impl AsJs for Extern { impl AsJs for Instance { type DefinitionType = crate::module::Module; fn as_jsvalue(&self, _store: &impl AsStoreRef) -> wasm_bindgen::JsValue { - self._inner._handle.clone().into() + self.as_js()._handle.clone().into() } fn from_jsvalue( @@ -213,7 +230,7 @@ impl AsJs for Instance { let (instance, exports) = JsInstance::from_module_and_instance(store, module, js_instance) .map_err(|e| JsError::new(&format!("Can't get the instance: {:?}", e)))?; Ok(Instance { - _inner: instance, + _inner: crate::RuntimeInstance::Js(instance), module: module.clone(), exports, }) @@ -224,7 +241,7 @@ impl AsJs for Memory { type DefinitionType = crate::MemoryType; fn as_jsvalue(&self, _store: &impl AsStoreRef) -> wasm_bindgen::JsValue { - self.0.handle.memory.clone().into() + self.as_js().handle.memory.clone().into() } fn from_jsvalue( @@ -235,7 +252,7 @@ impl AsJs for Memory { if let Some(memory) = value.dyn_ref::() { Ok(Memory::from_vm_extern( store, - VMMemory::new(memory.clone(), memory_type.clone()), + crate::vm::VMExternMemory::Js(VMMemory::new(memory.clone(), memory_type.clone())), )) } else { Err(JsError::new(&format!( @@ -250,7 +267,7 @@ impl AsJs for Function { type DefinitionType = crate::FunctionType; fn as_jsvalue(&self, _store: &impl AsStoreRef) -> wasm_bindgen::JsValue { - self.0.handle.function.clone().into() + self.as_js().handle.function.clone().into() } fn from_jsvalue( @@ -261,10 +278,10 @@ impl AsJs for Function { if value.is_instance_of::() { Ok(Function::from_vm_extern( store, - VMFunction::new( + crate::vm::VMExternFunction::Js(VMFunction::new( value.clone().unchecked_into::(), function_type.clone(), - ), + )), )) } else { Err(JsError::new(&format!( @@ -279,7 +296,7 @@ impl AsJs for Global { type DefinitionType = crate::GlobalType; fn as_jsvalue(&self, _store: &impl AsStoreRef) -> wasm_bindgen::JsValue { - self.0.handle.global.clone().into() + self.as_js().handle.global.clone().into() } fn from_jsvalue( @@ -290,10 +307,10 @@ impl AsJs for Global { if value.is_instance_of::() { Ok(Global::from_vm_extern( store, - VMGlobal::new( + crate::vm::VMExternGlobal::Js(VMGlobal::new( value.clone().unchecked_into::(), global_type.clone(), - ), + )), )) } else { Err(JsError::new(&format!( @@ -308,7 +325,7 @@ impl AsJs for Table { type DefinitionType = crate::TableType; fn as_jsvalue(&self, _store: &impl AsStoreRef) -> wasm_bindgen::JsValue { - self.0.handle.table.clone().into() + self.as_js().handle.table.clone().into() } fn from_jsvalue( @@ -319,10 +336,10 @@ impl AsJs for Table { if value.is_instance_of::() { Ok(Table::from_vm_extern( store, - VMTable::new( + crate::vm::VMExternTable::Js(VMTable::new( value.clone().unchecked_into::(), table_type.clone(), - ), + )), )) } else { Err(JsError::new(&format!( diff --git a/lib/api/src/js/js_handle.rs b/lib/api/src/rt/js/utils/js_handle.rs similarity index 100% rename from lib/api/src/js/js_handle.rs rename to lib/api/src/rt/js/utils/js_handle.rs diff --git a/lib/api/src/rt/js/utils/mod.rs b/lib/api/src/rt/js/utils/mod.rs new file mode 100644 index 00000000000..96c759004dd --- /dev/null +++ b/lib/api/src/rt/js/utils/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod convert; +pub(crate) mod js_handle; +pub(crate) mod polyfill; diff --git a/lib/api/src/js/wasm_bindgen_polyfill.rs b/lib/api/src/rt/js/utils/polyfill.rs similarity index 100% rename from lib/api/src/js/wasm_bindgen_polyfill.rs rename to lib/api/src/rt/js/utils/polyfill.rs diff --git a/lib/api/src/rt/js/vm/external.rs b/lib/api/src/rt/js/vm/external.rs new file mode 100644 index 00000000000..f273a11fd16 --- /dev/null +++ b/lib/api/src/rt/js/vm/external.rs @@ -0,0 +1,61 @@ +use wasmer_types::RawValue; + +use crate::{AsStoreMut, Extern, VMExternToExtern}; + +use super::{function::VMFunction, global::VMGlobal, memory::VMMemory, table::VMTable}; + +/// The value of an export passed from one instance to another in the `js` VM. +pub enum VMExtern { + /// A function export value. + Function(VMFunction), + + /// A table export value. + Table(VMTable), + + /// A memory export value. + Memory(VMMemory), + + /// A global export value. + Global(VMGlobal), +} + +impl VMExternToExtern for VMExtern { + fn to_extern(self, store: &mut impl AsStoreMut) -> Extern { + match self { + Self::Function(f) => Extern::Function(crate::Function::from_vm_extern( + store, + crate::vm::VMExternFunction::Js(f), + )), + Self::Memory(m) => Extern::Memory(crate::Memory::from_vm_extern( + store, + crate::vm::VMExternMemory::Js(m), + )), + Self::Global(g) => Extern::Global(crate::Global::from_vm_extern( + store, + crate::vm::VMExternGlobal::Js(g), + )), + Self::Table(t) => Extern::Table(crate::Table::from_vm_extern( + store, + crate::vm::VMExternTable::Js(t), + )), + } + } +} + +/// A reference to an external value in the `js` VM. +pub struct VMExternRef; + +impl VMExternRef { + /// Converts the `VMExternRef` into a `RawValue`. + pub fn into_raw(self) -> RawValue { + unimplemented!(); + } + + /// Extracts a `VMExternRef` from a `RawValue`. + /// + /// # Safety + /// `raw` must be a valid `VMExternRef` instance. + pub unsafe fn from_raw(_raw: RawValue) -> Option { + unimplemented!(); + } +} diff --git a/lib/api/src/rt/js/vm/function.rs b/lib/api/src/rt/js/vm/function.rs new file mode 100644 index 00000000000..6f50bfe28ce --- /dev/null +++ b/lib/api/src/rt/js/vm/function.rs @@ -0,0 +1,92 @@ +use std::any::Any; + +use crate::js::utils::js_handle::JsHandle; +use js_sys::Function as JsFunction; +use wasmer_types::{FunctionType, RawValue}; + +/// The VM Function type +#[derive(Clone, Eq)] +pub struct VMFunction { + pub(crate) function: JsHandle, + pub(crate) ty: FunctionType, +} + +unsafe impl Send for VMFunction {} +unsafe impl Sync for VMFunction {} + +impl VMFunction { + pub(crate) fn new(function: JsFunction, ty: FunctionType) -> Self { + Self { + function: JsHandle::new(function), + ty, + } + } +} + +impl PartialEq for VMFunction { + fn eq(&self, other: &Self) -> bool { + self.function == other.function + } +} + +impl std::fmt::Debug for VMFunction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("VMFunction") + .field("function", &self.function) + .finish() + } +} + +/// Underlying FunctionEnvironment used by a `VMFunction`. +#[derive(Debug)] +pub struct VMFunctionEnvironment { + pub(crate) contents: Box, +} + +impl VMFunctionEnvironment { + /// Wraps the given value to expose it to Wasm code as a function context. + pub fn new(val: impl Any + Send + 'static) -> Self { + Self { + contents: Box::new(val), + } + } + + #[allow(clippy::should_implement_trait)] + /// Returns a reference to the underlying value. + pub fn as_ref(&self) -> &(dyn Any + Send + 'static) { + &*self.contents + } + + #[allow(clippy::should_implement_trait)] + /// Returns a mutable reference to the underlying value. + pub fn as_mut(&mut self) -> &mut (dyn Any + Send + 'static) { + &mut *self.contents + } +} + +#[repr(C)] +/// The type of function bodies in the `js` VM. +pub struct VMFunctionBody(u8); + +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +/// The type of function references in the `js` VM. +pub(crate) struct VMFuncRef; + +impl VMFuncRef { + /// Converts the `VMFuncRef` into a `RawValue`. + pub fn into_raw(self) -> RawValue { + unimplemented!(); + } + + /// Extracts a `VMFuncRef` from a `RawValue`. + /// + /// # Safety + /// `raw.funcref` must be a valid pointer. + pub unsafe fn from_raw(_raw: RawValue) -> Option { + unimplemented!(); + } +} + +/// The type of function callbacks in the `js` VM. +pub type VMFunctionCallback = *const VMFunctionBody; diff --git a/lib/api/src/rt/js/vm/global.rs b/lib/api/src/rt/js/vm/global.rs new file mode 100644 index 00000000000..8be40b3f33f --- /dev/null +++ b/lib/api/src/rt/js/vm/global.rs @@ -0,0 +1,18 @@ +use crate::rt::js::utils::polyfill::Global as JsGlobal; +use wasmer_types::GlobalType; + +/// The VM Global type +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct VMGlobal { + pub(crate) global: JsGlobal, + pub(crate) ty: GlobalType, +} + +impl VMGlobal { + pub(crate) fn new(global: JsGlobal, ty: GlobalType) -> Self { + Self { global, ty } + } +} + +unsafe impl Send for VMGlobal {} +unsafe impl Sync for VMGlobal {} diff --git a/lib/api/src/rt/js/vm/memory.rs b/lib/api/src/rt/js/vm/memory.rs new file mode 100644 index 00000000000..706694b7460 --- /dev/null +++ b/lib/api/src/rt/js/vm/memory.rs @@ -0,0 +1,108 @@ +use crate::js::utils::js_handle::JsHandle; +use js_sys::WebAssembly::Memory as JsMemory; +use tracing::trace; +use wasm_bindgen::{JsCast, JsValue}; +use wasmer_types::{MemoryError, MemoryType, Pages, WASM_PAGE_SIZE}; + +/// Represents linear memory that is managed by the javascript runtime +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct VMMemory { + pub(crate) memory: JsHandle, + pub(crate) ty: MemoryType, +} + +unsafe impl Send for VMMemory {} +unsafe impl Sync for VMMemory {} + +#[derive(serde::Serialize, serde::Deserialize)] +struct DummyBuffer { + #[serde(rename = "byteLength")] + byte_length: u32, +} + +impl VMMemory { + /// Creates a new memory directly from a WebAssembly javascript object + pub fn new(memory: JsMemory, ty: MemoryType) -> Self { + Self { + memory: JsHandle::new(memory), + ty, + } + } + + /// Returns the size of the memory buffer in pages + pub fn get_runtime_size(&self) -> u32 { + let dummy: DummyBuffer = match serde_wasm_bindgen::from_value(self.memory.buffer()) { + Ok(o) => o, + Err(_) => return 0, + }; + if dummy.byte_length == 0 { + return 0; + } + dummy.byte_length / WASM_PAGE_SIZE as u32 + } + + /// Attempts to clone this memory (if its clonable) + pub(crate) fn try_clone(&self) -> Result { + Ok(self.clone()) + } + + /// Copies this memory to a new memory + pub fn copy(&mut self) -> Result { + let new_memory = crate::js::memory::Memory::js_memory_from_type(&self.ty)?; + + let src = crate::js::memory::MemoryView::new_raw(&self.memory); + let amount = src.data_size() as usize; + + trace!(%amount, "memory copy started"); + + let mut dst = crate::js::memory::MemoryView::new_raw(&new_memory); + let dst_size = dst.data_size() as usize; + + if amount > dst_size { + let delta = amount - dst_size; + let pages = ((delta - 1) / WASM_PAGE_SIZE) + 1; + + let our_js_memory: &crate::js::memory::JSMemory = + JsCast::unchecked_from_js_ref(&new_memory); + our_js_memory.grow(pages as u32).map_err(|err| { + if err.is_instance_of::() { + let cur_pages = dst_size; + MemoryError::CouldNotGrow { + current: Pages(cur_pages as u32), + attempted_delta: Pages(pages as u32), + } + } else { + MemoryError::Generic(err.as_string().unwrap()) + } + })?; + + dst = crate::js::memory::MemoryView::new_raw(&new_memory); + } + + src.copy_to_memory(amount as u64, &dst).map_err(|err| { + wasmer_types::MemoryError::Generic(format!("failed to copy the memory - {}", err)) + })?; + + trace!("memory copy finished (size={})", dst.size().bytes().0); + + Ok(Self { + memory: JsHandle::new(new_memory), + ty: self.ty.clone(), + }) + } +} + +impl From for JsValue { + fn from(value: VMMemory) -> Self { + JsValue::from(value.memory) + } +} + +impl From for (JsValue, MemoryType) { + fn from(value: VMMemory) -> Self { + (JsValue::from(value.memory), value.ty) + } +} + +/// Shared VM memory, in `js`, is the "normal" memory. +pub type VMSharedMemory = VMMemory; diff --git a/lib/api/src/rt/js/vm/mod.rs b/lib/api/src/rt/js/vm/mod.rs new file mode 100644 index 00000000000..2f4aec6f25f --- /dev/null +++ b/lib/api/src/rt/js/vm/mod.rs @@ -0,0 +1,26 @@ +pub(crate) mod external; +pub(crate) mod function; +pub(crate) mod global; +pub(crate) mod memory; +pub(crate) mod table; +pub use super::error::Trap; + +pub use external::*; +pub use function::*; +pub use global::*; +pub use memory::*; +pub use table::*; + +/// The type of instances in the `js` VM. +pub type VMInstance = js_sys::WebAssembly::Instance; + +pub struct VMTrampoline; + +/// The type of extern tables in the `js` VM. +pub(crate) type VMExternTable = VMTable; +/// The type of extern memories in the `js` VM. +pub(crate) type VMExternMemory = VMMemory; +/// The type of extern globals in the `js` VM. +pub(crate) type VMExternGlobal = VMGlobal; +/// The type of extern functions in the `js` VM. +pub(crate) type VMExternFunction = VMFunction; diff --git a/lib/api/src/rt/js/vm/table.rs b/lib/api/src/rt/js/vm/table.rs new file mode 100644 index 00000000000..a0d059c663e --- /dev/null +++ b/lib/api/src/rt/js/vm/table.rs @@ -0,0 +1,23 @@ +use js_sys::WebAssembly::Table as JsTable; +use wasmer_types::TableType; + +/// The VM Table type +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct VMTable { + pub(crate) table: JsTable, + pub(crate) ty: TableType, +} + +unsafe impl Send for VMTable {} +unsafe impl Sync for VMTable {} + +impl VMTable { + pub(crate) fn new(table: JsTable, ty: TableType) -> Self { + Self { table, ty } + } + + /// Get the table size at runtime + pub fn get_runtime_size(&self) -> u32 { + self.table.length() + } +} diff --git a/lib/api/src/jsc/engine.rs b/lib/api/src/rt/jsc/entities/engine.rs similarity index 74% rename from lib/api/src/jsc/engine.rs rename to lib/api/src/rt/jsc/entities/engine.rs index adbf7a33cd0..834c6a9b17b 100644 --- a/lib/api/src/jsc/engine.rs +++ b/lib/api/src/rt/jsc/entities/engine.rs @@ -1,6 +1,9 @@ -use rusty_jsc::{JSContext, JSObject}; use std::sync::Arc; +use rusty_jsc::{JSContext, JSObject}; + +use crate::{AsEngineRef, AsStoreRef}; + #[derive(Debug)] pub(crate) struct JSCEngine { context: JSContext, @@ -65,7 +68,7 @@ impl Default for JSCEngine { } } -/// A WebAssembly `Universal` Engine. +/// The engine for the JavascriptCore runtime. #[derive(Clone, Debug, Default)] pub struct Engine { inner: Arc, @@ -76,43 +79,43 @@ unsafe impl Sync for Engine {} impl From<&crate::engine::Engine> for Engine { fn from(engine: &crate::engine::Engine) -> Self { - engine.0.clone() + engine.as_jsc().clone() } } -pub(crate) trait JSC { +pub(crate) trait IntoJSC { fn jsc(&self) -> &JSCEngine; } -impl JSC for crate::Engine { +impl IntoJSC for crate::Engine { #[inline] fn jsc(&self) -> &JSCEngine { - &self.0.inner + &self.as_jsc().inner } } -impl JSC for crate::engine::EngineRef<'_> { +impl IntoJSC for crate::engine::EngineRef<'_> { #[inline] fn jsc(&self) -> &JSCEngine { - &self.engine().0.inner + &self.engine().as_jsc().inner } } -impl JSC for crate::store::StoreRef<'_> { +impl IntoJSC for crate::store::StoreRef<'_> { #[inline] fn jsc(&self) -> &JSCEngine { &self.engine().jsc() } } -impl JSC for crate::store::StoreMut<'_> { +impl IntoJSC for crate::store::StoreMut<'_> { #[inline] fn jsc(&self) -> &JSCEngine { &self.engine().jsc() } } -impl JSC for crate::Store { +impl IntoJSC for crate::Store { #[inline] fn jsc(&self) -> &JSCEngine { &self.engine().jsc() @@ -208,13 +211,42 @@ impl Engine { } } -// impl Default for Engine { -// fn default() -> Self { -// Engine -// } -// } - /// Returns the default engine for the JS engine pub(crate) fn default_engine() -> Engine { Engine::default() } + +impl crate::Engine { + /// Consume [`self`] into a [`crate::rt::jsc::engine::Engine`]. + pub fn into_jsc(self) -> crate::rt::jsc::engine::Engine { + match self.rt { + crate::RuntimeEngine::Jsc(s) => s, + _ => panic!("Not a `jsc` engine!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::jsc::engine::Engine`]. + pub fn as_jsc(&self) -> &crate::rt::jsc::engine::Engine { + match self.rt { + crate::RuntimeEngine::Jsc(ref s) => s, + _ => panic!("Not a `jsc` engine!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::jsc::engine::Engine`]. + pub fn as_jsc_mut(&mut self) -> &mut crate::rt::jsc::engine::Engine { + match self.rt { + crate::RuntimeEngine::Jsc(ref mut s) => s, + _ => panic!("Not a `jsc` engine!"), + } + } +} + +impl Into for Engine { + fn into(self) -> crate::Engine { + crate::Engine { + rt: crate::RuntimeEngine::Jsc(self), + id: crate::Engine::atomic_next_engine_id(), + } + } +} diff --git a/lib/api/src/jsc/extern_ref.rs b/lib/api/src/rt/jsc/entities/external.rs similarity index 100% rename from lib/api/src/jsc/extern_ref.rs rename to lib/api/src/rt/jsc/entities/external.rs diff --git a/lib/api/src/rt/jsc/entities/function/env.rs b/lib/api/src/rt/jsc/entities/function/env.rs new file mode 100644 index 00000000000..693e3d374b2 --- /dev/null +++ b/lib/api/src/rt/jsc/entities/function/env.rs @@ -0,0 +1,207 @@ +use std::{any::Any, fmt::Debug, marker::PhantomData}; + +use crate::{ + jsc::{store::StoreHandle, vm::VMFunctionEnvironment}, + store::{AsStoreMut, AsStoreRef, StoreRef}, + StoreMut, +}; + +#[derive(Debug)] +#[repr(transparent)] +/// An opaque reference to a function environment. +/// The function environment data is owned by the `Store`. +pub struct FunctionEnv { + pub(crate) handle: StoreHandle, + marker: PhantomData, +} + +impl FunctionEnv { + /// Make a new FunctionEnv + pub fn new(store: &mut impl AsStoreMut, value: T) -> Self + where + T: Any + Send + 'static + Sized, + { + Self { + handle: StoreHandle::new( + store.as_store_mut().objects_mut().as_jsc_mut(), + VMFunctionEnvironment::new(value), + ), + marker: PhantomData, + } + } + + /// Get the data as reference + pub fn as_ref<'a>(&self, store: &'a impl AsStoreRef) -> &'a T + where + T: Any + Send + 'static + Sized, + { + self.handle + .get(store.as_store_ref().objects().as_jsc()) + .as_ref() + .downcast_ref::() + .unwrap() + } + + pub(crate) fn from_handle(handle: StoreHandle) -> Self { + Self { + handle, + marker: PhantomData, + } + } + + /// Get the data as mutable + pub fn as_mut<'a>(&self, store: &'a mut impl AsStoreMut) -> &'a mut T + where + T: Any + Send + 'static + Sized, + { + self.handle + .get_mut(store.objects_mut().as_jsc_mut()) + .as_mut() + .downcast_mut::() + .unwrap() + } + + /// Convert it into a `FunctionEnvMut` + pub fn into_mut(self, store: &mut impl AsStoreMut) -> FunctionEnvMut + where + T: Any + Send + 'static + Sized, + { + FunctionEnvMut { + store_mut: store.as_store_mut(), + func_env: self, + } + } +} + +impl PartialEq for FunctionEnv { + fn eq(&self, other: &Self) -> bool { + self.handle == other.handle + } +} + +impl Eq for FunctionEnv {} + +impl std::hash::Hash for FunctionEnv { + fn hash(&self, state: &mut H) { + self.handle.hash(state); + self.marker.hash(state); + } +} + +impl Clone for FunctionEnv { + fn clone(&self) -> Self { + Self { + handle: self.handle.clone(), + marker: self.marker, + } + } +} + +/// A temporary handle to a [`FunctionEnv`]. +pub struct FunctionEnvMut<'a, T: 'a> { + pub(crate) store_mut: StoreMut<'a>, + pub(crate) func_env: FunctionEnv, +} + +impl<'a, T> Debug for FunctionEnvMut<'a, T> +where + T: Send + Debug + 'static, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.func_env.as_ref(&self.store_mut).fmt(f) + } +} + +impl FunctionEnvMut<'_, T> { + /// Returns a reference to the host state in this function environement. + pub fn data(&self) -> &T { + self.func_env.as_ref(&self.store_mut) + } + + /// Returns a mutable- reference to the host state in this function environement. + pub fn data_mut(&mut self) -> &mut T { + self.func_env.as_mut(&mut self.store_mut) + } + + /// Borrows a new immmutable reference + pub fn as_ref(&self) -> FunctionEnv { + self.func_env.clone() + } + + /// Borrows a new mutable reference + pub fn as_mut(&mut self) -> FunctionEnvMut<'_, T> { + FunctionEnvMut { + store_mut: self.store_mut.as_store_mut(), + func_env: self.func_env.clone(), + } + } + + /// Borrows a new mutable reference of both the attached Store and host state + pub fn data_and_store_mut(&mut self) -> (&mut T, StoreMut) { + let data = self.func_env.as_mut(&mut self.store_mut) as *mut T; + // telling the borrow check to close his eyes here + // this is still relatively safe to do as func_env are + // stored in a specific vec of Store, separate from the other objects + // and not really directly accessible with the StoreMut + let data = unsafe { &mut *data }; + (data, self.store_mut.as_store_mut()) + } +} + +impl AsStoreRef for FunctionEnvMut<'_, T> { + fn as_store_ref(&self) -> StoreRef<'_> { + StoreRef { + inner: self.store_mut.inner, + } + } +} + +impl AsStoreMut for FunctionEnvMut<'_, T> { + fn as_store_mut(&mut self) -> StoreMut<'_> { + StoreMut { + inner: self.store_mut.inner, + } + } + + fn objects_mut(&mut self) -> &mut crate::StoreObjects { + self.store_mut.objects_mut() + } +} + +impl crate::FunctionEnv { + /// Consume [`self`] into [`crate::rt::jsc::function::env::FunctionEnv`]. + pub fn into_jsc(self) -> FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::Jsc(s) => s, + _ => panic!("Not a `jsc` function env!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::jsc::function::env::FunctionEnv`]. + pub fn as_jsc(&self) -> &FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::Jsc(ref s) => s, + _ => panic!("Not a `jsc` function env!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::jsc::function::env::FunctionEnv`]. + pub fn as_jsc_mut(&mut self) -> &mut FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::Jsc(ref mut s) => s, + _ => panic!("Not a `jsc` function env!"), + } + } +} + +impl<'a, T> From> for crate::FunctionEnvMut<'a, T> { + fn from(value: FunctionEnvMut<'a, T>) -> Self { + crate::FunctionEnvMut(crate::RuntimeFunctionEnvMut::Jsc(value)) + } +} + +impl From> for crate::FunctionEnv { + fn from(value: FunctionEnv) -> Self { + crate::FunctionEnv(crate::RuntimeFunctionEnv::Jsc(value)) + } +} diff --git a/lib/api/src/rt/jsc/entities/function/mod.rs b/lib/api/src/rt/jsc/entities/function/mod.rs new file mode 100644 index 00000000000..908bf2d25a1 --- /dev/null +++ b/lib/api/src/rt/jsc/entities/function/mod.rs @@ -0,0 +1,596 @@ +pub(crate) mod env; +pub(crate) mod typed; + +use rusty_jsc::{ + callback, callback_closure, JSContext, JSObject, JSObjectCallAsFunctionCallback, JSValue, +}; +use std::marker::PhantomData; +use std::panic::{self, AssertUnwindSafe}; +use wasmer_types::{FunctionType, RawValue}; + +use crate::{ + jsc::{ + store::{InternalStoreHandle, StoreHandle}, + utils::convert::{jsc_value_to_wasmer, AsJsc}, + vm::{VMFuncRef, VMFunction, VMFunctionEnvironment}, + }, + vm::VMExtern, + AsStoreMut, AsStoreRef, FromToNativeWasmType, FunctionEnv, FunctionEnvMut, HostFunction, + HostFunctionKind, IntoResult, NativeWasmType, NativeWasmTypeInto, RuntimeError, + RuntimeFunction, RuntimeFunctionEnvMut, StoreMut, Value, WasmTypeList, WithEnv, WithoutEnv, +}; + +use super::engine::IntoJSC; + +pub(crate) use env::*; +pub(crate) use typed::*; + +#[derive(Clone, PartialEq, Eq)] +pub struct Function { + pub(crate) handle: VMFunction, +} + +// Function can't be Send in js because it dosen't support `structuredClone` +// https://developer.mozilla.org/en-US/docs/Web/API/structuredClone +// unsafe impl Send for Function {} + +impl From for Function { + fn from(handle: VMFunction) -> Self { + Self { handle } + } +} + +impl Function { + /// To `VMExtern`. + pub fn to_vm_extern(&self) -> VMExtern { + VMExtern::Jsc(crate::rt::jsc::vm::VMExtern::Function(self.handle.clone())) + } + + #[allow(clippy::cast_ptr_alignment)] + pub fn new_with_env( + store: &mut impl AsStoreMut, + env: &FunctionEnv, + ty: FT, + func: F, + ) -> Self + where + FT: Into, + F: Fn(FunctionEnvMut<'_, T>, &[Value]) -> Result, RuntimeError> + + 'static + + Send + + Sync, + { + let store = store.as_store_mut(); + let context = store.jsc().context(); + let function_type = ty.into(); + + let new_function_type = function_type.clone(); + let raw_env = env.clone(); + + let callback = callback_closure!(&context, move |ctx: JSContext, + function: JSObject, + this: JSObject, + args: &[JSValue]| + -> Result { + let global = ctx.get_global_object(); + let store_ptr = global + .get_property(&ctx, "__store_ptr".to_string()) + .to_number(&ctx) + .unwrap(); + + let mut store = unsafe { StoreMut::from_raw(store_ptr as usize as *mut _) }; + + let env: FunctionEnvMut = raw_env.clone().into_mut(&mut store); + + let wasm_arguments = new_function_type + .params() + .iter() + .enumerate() + .map(|(i, param)| jsc_value_to_wasmer(&ctx, param, &args[i])) + .collect::>(); + let results = func(env, &wasm_arguments).map_err(|e| { + let value = format!("{}", e); + JSValue::string(&ctx, value) + })?; + match new_function_type.results().len() { + 0 => Ok(JSValue::undefined(&ctx)), + 1 => Ok(results[0].as_jsc_value(&mut store)), + _ => Ok(JSObject::new_array( + &ctx, + &results + .into_iter() + .map(|result| result.as_jsc_value(&mut store)) + .collect::>(), + )? + .to_jsvalue()), + } + }); + + let vm_function = VMFunction::new(callback, function_type); + Self { + handle: vm_function, + } + } + + /// Creates a new host `Function` from a native function. + pub fn new_typed(store: &mut impl AsStoreMut, func: F) -> Self + where + F: HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync, + Args: WasmTypeList, + Rets: WasmTypeList, + { + let store = store.as_store_mut(); + let function = WasmFunction::::new(func); + let callback = function.callback(store.jsc().context()); + + let ty = function.ty(); + let vm_function = VMFunction::new(callback, ty); + Self { + handle: vm_function, + } + } + + pub fn new_typed_with_env( + store: &mut impl AsStoreMut, + env: &FunctionEnv, + func: F, + ) -> Self + where + F: HostFunction, + Args: WasmTypeList, + Rets: WasmTypeList, + { + let store = store.as_store_mut(); + let context = store.jsc().context(); + let function = WasmFunction::::new(func); + let callback = function.callback(store.jsc().context()); + + let bind = callback + .get_property(&context, "bind".to_string()) + .to_object(&context) + .unwrap(); + let callback_with_env = bind + .call( + &context, + Some(&callback), + &[ + JSValue::undefined(&context), + JSValue::number( + &context, + env.as_jsc().handle.internal_handle().index() as f64, + ), + ], + ) + .unwrap() + .to_object(&context) + .unwrap(); + + let ty = function.ty(); + let vm_function = VMFunction::new(callback_with_env, ty); + Self { + handle: vm_function, + } + } + + pub fn ty(&self, _store: &impl AsStoreRef) -> FunctionType { + self.handle.ty.clone() + } + + pub fn call_raw( + &self, + _store: &mut impl AsStoreMut, + _params: Vec, + ) -> Result, RuntimeError> { + // There is no optimal call_raw in JSC, so we just + // simply rely the call + // self.call(store, params) + unimplemented!(); + } + + pub fn call( + &self, + store: &mut impl AsStoreMut, + params: &[Value], + ) -> Result, RuntimeError> { + let store_mut = store.as_store_mut(); + let engine = store_mut.engine(); + let context = engine.as_jsc().context(); + + let mut global = context.get_global_object(); + let store_ptr = store_mut.as_raw() as usize; + global.set_property( + &context, + "__store_ptr".to_string(), + JSValue::number(&context, store_ptr as _), + ); + + let params_list = params + .iter() + .map(|v| v.as_jsc_value(&store_mut)) + .collect::>(); + let result = { + let mut r; + // TODO: This loop is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 + loop { + let store_mut = store.as_store_mut(); + let engine = store_mut.engine(); + let context = engine.as_jsc().context(); + r = self.handle.function.call(&context, None, ¶ms_list); + if let Some(callback) = store_mut.inner.on_called.take() { + match callback(store_mut) { + Ok(wasmer_types::OnCalledAction::InvokeAgain) => { + continue; + } + Ok(wasmer_types::OnCalledAction::Finish) => { + break; + } + Ok(wasmer_types::OnCalledAction::Trap(trap)) => { + return Err(RuntimeError::user(trap)) + } + Err(trap) => return Err(RuntimeError::user(trap)), + } + } + break; + } + r? + }; + let result_types = self.handle.ty.results(); + let store_mut = store.as_store_mut(); + let engine = store_mut.engine(); + let context = engine.as_jsc().context(); + match result_types.len() { + 0 => Ok(Box::new([])), + 1 => { + let value = jsc_value_to_wasmer(&context, &result_types[0], &result); + Ok(vec![value].into_boxed_slice()) + } + n => { + let result = result.to_object(&context).unwrap(); + Ok((0..n) + .map(|i| { + let js_val = result.get_property_at_index(&context, i as _).unwrap(); + jsc_value_to_wasmer(&context, &result_types[i], &js_val) + }) + .collect::>() + .into_boxed_slice()) + } + } + } + + pub(crate) fn from_vm_extern( + _store: &mut impl AsStoreMut, + internal: crate::vm::VMExternFunction, + ) -> Self { + Self { + handle: internal.into_jsc(), + } + } + + pub(crate) fn vm_funcref(&self, _store: &impl AsStoreRef) -> VMFuncRef { + unimplemented!(); + } + + pub(crate) unsafe fn from_vm_funcref( + _store: &mut impl AsStoreMut, + _funcref: VMFuncRef, + ) -> Self { + unimplemented!(); + } + + /// Checks whether this `Function` can be used with the given context. + pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} + +impl std::fmt::Debug for Function { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.debug_struct("Function").finish() + } +} + +/// Represents a low-level Wasm static host function. See +/// `super::Function::new` and `super::Function::new_env` to learn +/// more. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct WasmFunction { + callback: JSObjectCallAsFunctionCallback, + _phantom: PhantomData<(Args, Rets)>, +} + +unsafe impl Send for WasmFunction {} + +impl WasmFunction +where + Args: WasmTypeList, + Rets: WasmTypeList, +{ + /// Creates a new `WasmFunction`. + #[allow(dead_code)] + pub fn new(function: F) -> Self + where + F: HostFunction, + T: Sized, + { + Self { + callback: function.function_callback(crate::Runtime::Jsc).into_jsc(), + _phantom: PhantomData, + } + } + + /// Get the function type of this `WasmFunction`. + #[allow(dead_code)] + pub fn ty(&self) -> FunctionType { + FunctionType::new(Args::wasm_types(), Rets::wasm_types()) + } + + /// Get the address of this `WasmFunction`. + #[allow(dead_code)] + pub fn callback(&self, context: &JSContext) -> JSObject { + JSObject::new_function_with_callback(context, "FunctionCallback".to_string(), self.callback) + } +} + +impl crate::Function { + /// Consume [`self`] into [`crate::rt::jsc::function::Function`]. + pub fn into_jsc(self) -> crate::rt::jsc::function::Function { + match self.0 { + RuntimeFunction::Jsc(s) => s, + _ => panic!("Not a `jsc` function!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::jsc::function::Function`]. + pub fn as_jsc(&self) -> &crate::rt::jsc::function::Function { + match self.0 { + RuntimeFunction::Jsc(ref s) => s, + _ => panic!("Not a `jsc` function!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::jsc::function::Function`]. + pub fn as_jsc_mut(&mut self) -> &mut crate::rt::jsc::function::Function { + match self.0 { + RuntimeFunction::Jsc(ref mut s) => s, + _ => panic!("Not a `jsc` function!"), + } + } +} + +// Black-magic to count the number of identifiers at compile-time. +macro_rules! count_idents { + ( $($idents:ident),* ) => { + { + #[allow(dead_code, non_camel_case_types)] + enum Idents { $( $idents, )* __CountIdentsLast } + const COUNT: usize = Idents::__CountIdentsLast as usize; + COUNT + } + }; +} + +macro_rules! impl_host_function { + ([$c_struct_representation:ident] $c_struct_name:ident, $( $x:ident ),* ) => { + paste::paste! { + #[allow(non_snake_case)] + pub(crate) fn [] + <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, Func: Fn($( $x , )*) -> RetsAsResult + 'static> + (this: &Func) -> crate::rt::jsc::vm::VMFunctionCallback { + + #[callback] + fn fn_callback<$( $x, )* Rets, RetsAsResult, Func>( + ctx: JSContext, + function: JSObject, + this_object: JSObject, + arguments: &[JSValue], + ) -> Result + where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + RetsAsResult: IntoResult, + Func: Fn($( $x , )*) -> RetsAsResult + 'static, + // $( $x: NativeWasmTypeInto, )* + { + use std::convert::TryInto; + + let func: &Func = &*(&() as *const () as *const Func); + let global = ctx.get_global_object(); + let store_ptr = global.get_property(&ctx, "__store_ptr".to_string()).to_number(&ctx).unwrap(); + if store_ptr.is_nan() { + panic!("Store pointer is invalid. Received {}", store_ptr as usize) + } + + let mut store = StoreMut::from_raw(store_ptr as usize as *mut _); + let result = panic::catch_unwind(AssertUnwindSafe(|| { + type JSArray<'a> = &'a [JSValue; count_idents!( $( $x ),* )]; + let args_without_store: JSArray = arguments.try_into().unwrap(); + let [ $( $x ),* ] = args_without_store; + func($( FromToNativeWasmType::from_native( $x::Native::from_raw(&mut store, RawValue { u128: { + // TODO: This may not be the fastest way, but JSC doesn't expose a BigInt interface + // so the only thing we can do is parse from the string repr + if $x.is_number(&ctx) { + $x.to_number(&ctx).unwrap() as _ + } + else { + $x.to_string(&ctx).unwrap().to_string().parse::().unwrap() + } + } }) ) ),* ).into_result() + })); + + match result { + Ok(Ok(result)) => { + match Rets::size() { + 0 => {Ok(JSValue::undefined(&ctx))}, + 1 => { + let ty = Rets::wasm_types()[0]; + let mut arr = result.into_array(&mut store); + let val = Value::from_raw(&mut store, ty, arr.as_mut()[0]); + let value: JSValue = val.as_jsc_value(&store); + Ok(value) + } + _n => { + let mut arr = result.into_array(&mut store); + let result_values = Rets::wasm_types().iter().enumerate().map(|(i, ret_type)| { + let raw = arr.as_mut()[i]; + Value::from_raw(&mut store, *ret_type, raw).as_jsc_value(&mut store) + }).collect::>(); + Ok(JSObject::new_array(&ctx, &result_values).unwrap().to_jsvalue()) + } + } + }, + #[cfg(feature = "std")] + Ok(Err(err)) => { + let trap = crate::rt::jsc::error::Trap::user(Box::new(err)); + Err(trap.into_jsc_value(&ctx)) + }, + #[cfg(feature = "core")] + Ok(Err(err)) => { + let trap = crate::rt::jsc::error::Trap::user(Box::new(err)); + Err(trap.into_jsc_value(&ctx)) + }, + Err(panic) => { + Err(JSValue::string(&ctx, format!("panic: {:?}", panic))) + // We can't just resume the unwind, because it will put + // JavacriptCore in a bad state, so we need to transform + // the error + + // std::panic::resume_unwind(panic) + }, + } + + } + Some(fn_callback::< $( $x, )* Rets, RetsAsResult, Func> as _) + } + + + #[allow(non_snake_case)] + pub(crate) fn [] + <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, T: Send + 'static, Func: Fn(FunctionEnvMut, $( $x , )*) -> RetsAsResult + 'static> + (this: &Func) -> crate::rt::jsc::vm::VMFunctionCallback { + + #[callback] + fn fn_callback( + ctx: JSContext, + function: JSObject, + this_object: JSObject, + arguments: &[JSValue], + ) -> Result + where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + RetsAsResult: IntoResult, + Func: Fn(FunctionEnvMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static, + T: Send + 'static, + { + use std::convert::TryInto; + + let func: &Func = &*(&() as *const () as *const Func); + let global = ctx.get_global_object(); + let store_ptr = global.get_property(&ctx, "__store_ptr".to_string()).to_number(&ctx).unwrap(); + if store_ptr.is_nan() { + panic!("Store pointer is invalid. Received {}", store_ptr as usize) + } + let mut store = StoreMut::from_raw(store_ptr as usize as *mut _); + + let handle_index = arguments[0].to_number(&ctx).unwrap() as usize; + let handle: StoreHandle = StoreHandle::from_internal(store.objects_mut().id(), InternalStoreHandle::from_index(handle_index).unwrap()); + let env = crate::rt::jsc::function::env::FunctionEnv::from_handle(handle).into_mut(&mut store); + + let result = panic::catch_unwind(AssertUnwindSafe(|| { + type JSArray<'a> = &'a [JSValue; count_idents!( $( $x ),* )]; + let args_without_store: JSArray = arguments[1..].try_into().unwrap(); + let [ $( $x ),* ] = args_without_store; + let mut store = StoreMut::from_raw(store_ptr as usize as *mut _); + func(RuntimeFunctionEnvMut::Jsc(env).into(), $( FromToNativeWasmType::from_native( $x::Native::from_raw(&mut store, RawValue { u128: { + // TODO: This may not be the fastest way, but JSC doesn't expose a BigInt interface + // so the only thing we can do is parse from the string repr + if $x.is_number(&ctx) { + $x.to_number(&ctx).unwrap() as _ + } + else { + $x.to_string(&ctx).unwrap().to_string().parse::().unwrap() + } + } }) ) ),* ).into_result() + })); + + match result { + Ok(Ok(result)) => { + match Rets::size() { + 0 => {Ok(JSValue::undefined(&ctx))}, + 1 => { + // unimplemented!(); + + let ty = Rets::wasm_types()[0]; + let mut arr = result.into_array(&mut store); + // Value::from_raw(&store, ty, arr[0]) + let val = Value::from_raw(&mut store, ty, arr.as_mut()[0]); + let value: JSValue = val.as_jsc_value(&store); + Ok(value) + // *mut_rets = val.as_raw(&mut store); + } + _n => { + // if !results.is_array(&context) { + // panic!("Expected results to be an array.") + // } + let mut arr = result.into_array(&mut store); + let result_values = Rets::wasm_types().iter().enumerate().map(|(i, ret_type)| { + let raw = arr.as_mut()[i]; + Value::from_raw(&mut store, *ret_type, raw).as_jsc_value(&mut store) + }).collect::>(); + Ok(JSObject::new_array(&ctx, &result_values).unwrap().to_jsvalue()) + } + } + }, + #[cfg(feature = "std")] + Ok(Err(err)) => { + let trap = crate::jsc::vm::Trap::user(Box::new(err)); + Err(trap.into_jsc_value(&ctx)) + }, + #[cfg(feature = "core")] + Ok(Err(err)) => { + let trap = crate::jsc::vm::Trap::user(Box::new(err)); + Err(trap.into_jsc_value(&ctx)) + }, + Err(panic) => { + Err(JSValue::string(&ctx, format!("panic: {:?}", panic))) + }, + } + + } + + Some(fn_callback:: as _) + } + + } + }; +} + +// Here we go! Let's generate all the C struct, `WasmTypeList` +// implementations and `HostFunction` implementations. +impl_host_function!([C] S0,); +impl_host_function!([transparent] S1, A1); +impl_host_function!([C] S2, A1, A2); +impl_host_function!([C] S3, A1, A2, A3); +impl_host_function!([C] S4, A1, A2, A3, A4); +impl_host_function!([C] S5, A1, A2, A3, A4, A5); +impl_host_function!([C] S6, A1, A2, A3, A4, A5, A6); +impl_host_function!([C] S7, A1, A2, A3, A4, A5, A6, A7); +impl_host_function!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8); +impl_host_function!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9); +impl_host_function!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); +impl_host_function!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); +impl_host_function!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); +impl_host_function!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); +impl_host_function!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); +impl_host_function!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); +impl_host_function!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16); +impl_host_function!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17); +impl_host_function!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18); +impl_host_function!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19); +impl_host_function!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20); +impl_host_function!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21); +impl_host_function!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22); +impl_host_function!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23); +impl_host_function!([C] S24, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24); +impl_host_function!([C] S25, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25); +impl_host_function!([C] S26, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26); diff --git a/lib/api/src/jsc/typed_function.rs b/lib/api/src/rt/jsc/entities/function/typed.rs similarity index 82% rename from lib/api/src/jsc/typed_function.rs rename to lib/api/src/rt/jsc/entities/function/typed.rs index ff3cf394619..22a483364bc 100644 --- a/lib/api/src/jsc/typed_function.rs +++ b/lib/api/src/rt/jsc/entities/function/typed.rs @@ -7,15 +7,18 @@ //! let add_one = instance.exports.get_function("function_name")?; //! let add_one_native: TypedFunction = add_one.typed().unwrap(); //! ``` -use crate::jsc::as_js::{param_from_js, AsJs}; -use crate::jsc::engine::JSC; -use crate::jsc::trap::Trap; -use crate::native_type::NativeWasmTypeInto; -use crate::Value; -use crate::{AsStoreMut, TypedFunction}; -use crate::{FromToNativeWasmType, RuntimeError, WasmTypeList}; -use rusty_jsc::JSValue; // use std::panic::{catch_unwind, AssertUnwindSafe}; +use crate::{ + rt::jsc::{ + engine::IntoJSC, + error::Trap, + utils::convert::{jsc_value_to_wasmer, AsJsc}, + }, + AsStoreMut, FromToNativeWasmType, NativeWasmType, NativeWasmTypeInto, RuntimeError, + TypedFunction, Value, WasmTypeList, +}; + +use rusty_jsc::JSValue; use std::iter::FromIterator; use wasmer_types::RawValue; @@ -29,16 +32,15 @@ macro_rules! impl_native_traits { { /// Call the typed func and return results. #[allow(clippy::too_many_arguments)] - pub fn call(&self, mut store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result where - $( $x: FromToNativeWasmType + NativeWasmTypeInto, )* + pub fn call_jsc(&self, mut store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result where + $( $x: FromToNativeWasmType, )* { - // let store_ptr = Value::I64(store.as_store_mut().as_raw() as _).as_jsvalue(store); #[allow(unused_unsafe)] let params_list: Vec<_> = unsafe { vec![ $( { - let raw = $x.into_raw(store); - let value = Value::from_raw(&mut store, $x::WASM_TYPE, raw); - value.as_jsvalue(store) + let raw = $x.to_native().into_raw(store); + let value = Value::from_raw(&mut store, <$x::Native as NativeWasmType>::WASM_TYPE, raw); + value.as_jsc_value(store) } ),* ] }; @@ -56,7 +58,7 @@ macro_rules! impl_native_traits { let store_mut = store.as_store_mut(); let context = store_mut.jsc().context(); - r = self.func.0.handle.function + r = self.func.as_jsc().handle.function .call( &context, None, @@ -80,7 +82,7 @@ macro_rules! impl_native_traits { } let store_mut = store.as_store_mut(); let context = store_mut.jsc().context(); - r.map_err(|e| Trap::from_jsvalue(context, e))? + r.map_err(|e| Trap::from_jsc_value(context, e))? }; let mut rets_list_array = Rets::empty_array(); let mut_rets = rets_list_array.as_mut() as *mut [RawValue] as *mut RawValue; @@ -90,7 +92,7 @@ macro_rules! impl_native_traits { 0 => {}, 1 => unsafe { let ty = Rets::wasm_types()[0]; - let val = param_from_js(&context, &ty, &results); + let val = jsc_value_to_wasmer(&context, &ty, &results); *mut_rets = val.as_raw(&mut store); } _n => { @@ -103,7 +105,7 @@ macro_rules! impl_native_traits { let context = store_mut.jsc().context(); let ret = results.get_property_at_index(&context, i as _).unwrap(); unsafe { - let val = param_from_js(&context, &ret_type, &ret); + let val = jsc_value_to_wasmer(&context, &ret_type, &ret); let slot = mut_rets.add(i); *slot = val.as_raw(&mut store); } @@ -112,6 +114,14 @@ macro_rules! impl_native_traits { } Ok(unsafe { Rets::from_array(store, rets_list_array) }) } + + #[doc(hidden)] + #[allow(missing_docs)] + #[allow(unused_mut)] + #[allow(clippy::too_many_arguments)] + pub fn call_raw_jsc(&self, store: &mut impl AsStoreMut, mut params_list: Vec ) -> Result { + todo!("Raw calls from jsc are not supported yet!") + } } }; } diff --git a/lib/api/src/jsc/externals/global.rs b/lib/api/src/rt/jsc/entities/global.rs similarity index 57% rename from lib/api/src/jsc/externals/global.rs rename to lib/api/src/rt/jsc/entities/global.rs index ca83f52992e..478b2014645 100644 --- a/lib/api/src/jsc/externals/global.rs +++ b/lib/api/src/rt/jsc/entities/global.rs @@ -1,15 +1,18 @@ -use crate::errors::RuntimeError; -use crate::jsc::as_js::param_from_js; -use crate::jsc::as_js::AsJs; -use crate::store::{AsStoreMut, AsStoreRef}; -use crate::value::Value; -use crate::vm::{VMExtern, VMGlobal}; -use crate::GlobalType; -use crate::Mutability; use rusty_jsc::{JSObject, JSValue}; -use wasmer_types::{RawValue, Type}; +use wasmer_types::{GlobalType, Mutability, Type}; -#[derive(Debug, Clone, PartialEq)] +use crate::{ + jsc::{ + utils::convert::{jsc_value_to_wasmer, AsJsc}, + vm::VMGlobal, + }, + vm::VMExtern, + AsStoreMut, AsStoreRef, RuntimeError, RuntimeGlobal, Value, +}; + +use super::store::StoreObject; + +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Global { pub(crate) handle: VMGlobal, } @@ -20,7 +23,7 @@ pub struct Global { impl Global { pub(crate) fn to_vm_extern(&self) -> VMExtern { - VMExtern::Global(self.handle.clone()) + VMExtern::Jsc(crate::rt::jsc::vm::VMExtern::Global(self.handle.clone())) } /// Create a `Global` with the initial value [`Value`] and the provided [`Mutability`]. @@ -38,7 +41,7 @@ impl Global { }; let store_mut = store.as_store_mut(); let engine = store_mut.engine(); - let context = engine.0.context(); + let context = engine.as_jsc().context(); let mut descriptor = JSObject::new(&context); let type_str = match val.ty() { @@ -64,14 +67,16 @@ impl Global { JSValue::boolean(&context, mutability.is_mutable()), ); - let value: JSValue = val.as_jsvalue(&store_mut); + let value: JSValue = val.as_jsc_value(&store_mut); let js_global = engine - .0 + .as_jsc() .wasm_global_type() .construct(&context, &[descriptor.to_jsvalue(), value]) .map_err(|e| >::into(e))?; let vm_global = VMGlobal::new(js_global, global_ty); - Ok(Self::from_vm_extern(store, vm_global)) + crate::rt::jsc::vm::VMGlobal::list_mut(store.objects_mut().as_jsc_mut()) + .push(vm_global.clone()); + Ok(Self { handle: vm_global }) } pub fn ty(&self, _store: &impl AsStoreRef) -> GlobalType { @@ -81,32 +86,63 @@ impl Global { pub fn get(&self, store: &mut impl AsStoreMut) -> Value { let store_mut = store.as_store_mut(); let engine = store_mut.engine(); - let context = engine.0.context(); + let context = engine.as_jsc().context(); let value = self .handle .global .get_property(&context, "value".to_string()); - param_from_js(&context, &self.handle.ty.ty, &value) + jsc_value_to_wasmer(&context, &self.handle.ty.ty, &value) } pub fn set(&self, store: &mut impl AsStoreMut, val: Value) -> Result<(), RuntimeError> { let store_mut = store.as_store_mut(); - let new_value = val.as_jsvalue(&store_mut); + let new_value = val.as_jsc_value(&store_mut); let engine = store_mut.engine(); - let context = engine.0.context(); + let context = engine.as_jsc().context(); self.handle .global .set_property(&context, "value".to_string(), new_value) .map_err(|e| >::into(e)) } - pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_global: VMGlobal) -> Self { - use crate::jsc::store::StoreObject; - VMGlobal::list_mut(store.objects_mut()).push(vm_global.clone()); - Self { handle: vm_global } + pub(crate) fn from_vm_extern( + store: &mut impl AsStoreMut, + vm_global: crate::vm::VMExternGlobal, + ) -> Self { + crate::rt::jsc::vm::VMGlobal::list_mut(store.objects_mut().as_jsc_mut()) + .push(vm_global.as_jsc().clone()); + Self { + handle: vm_global.into_jsc(), + } } pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { true } } + +impl crate::Global { + /// Consume [`self`] into [`crate::rt::jsc::global::Global`]. + pub fn into_jsc(self) -> crate::rt::jsc::global::Global { + match self.0 { + RuntimeGlobal::Jsc(s) => s, + _ => panic!("Not a `jsc` global!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::jsc::global::Global`]. + pub fn as_jsc(&self) -> &crate::rt::jsc::global::Global { + match self.0 { + RuntimeGlobal::Jsc(ref s) => s, + _ => panic!("Not a `jsc` global!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::jsc::global::Global`]. + pub fn as_jsc_mut(&mut self) -> &mut crate::rt::jsc::global::Global { + match self.0 { + RuntimeGlobal::Jsc(ref mut s) => s, + _ => panic!("Not a `jsc` global!"), + } + } +} diff --git a/lib/api/src/jsc/instance.rs b/lib/api/src/rt/jsc/entities/instance.rs similarity index 64% rename from lib/api/src/jsc/instance.rs rename to lib/api/src/rt/jsc/entities/instance.rs index 614eb76e754..f788848f2ac 100644 --- a/lib/api/src/jsc/instance.rs +++ b/lib/api/src/rt/jsc/entities/instance.rs @@ -1,12 +1,9 @@ -use crate::errors::InstantiationError; -use crate::exports::Exports; -use crate::imports::Imports; -use crate::jsc::as_js::AsJs; -use crate::jsc::engine::JSC; -use crate::jsc::vm::VMInstance; -use crate::module::Module; -use crate::store::AsStoreMut; -use crate::Extern; +use crate::{ + jsc::{utils::convert::AsJsc, vm::VMInstance}, + AsStoreMut, Exports, Extern, Imports, InstantiationError, Module, RuntimeInstance, +}; + +use super::engine::IntoJSC; #[derive(Clone, PartialEq, Eq)] pub struct Instance { @@ -24,7 +21,7 @@ impl Instance { imports: &Imports, ) -> Result<(Self, Exports), InstantiationError> { let instance = module - .0 + .as_jsc() .instantiate(&mut store, imports) .map_err(|e| InstantiationError::Start(e))?; @@ -67,7 +64,7 @@ impl Instance { let extern_type = export_type.ty(); // Annotation is here to prevent spurious IDE warnings. let js_export = instance_exports.get_property(&context, name.to_string()); - let extern_ = Extern::from_jsvalue(&mut store, extern_type, &js_export).unwrap(); + let extern_ = Extern::from_jsc_value(&mut store, extern_type, &js_export).unwrap(); Ok((name.to_string(), extern_)) }) .collect::>()?; @@ -75,3 +72,29 @@ impl Instance { Ok((Self { _handle: instance }, exports)) } } + +impl crate::Instance { + /// Consume [`self`] into [`crate::rt::jsc::instance::Instance`]. + pub fn into_jsc(self) -> crate::rt::jsc::instance::Instance { + match self._inner { + RuntimeInstance::Jsc(s) => s, + _ => panic!("Not a `jsc` global!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::jsc::instance::Instance`]. + pub fn as_jsc(&self) -> &crate::rt::jsc::instance::Instance { + match self._inner { + RuntimeInstance::Jsc(ref s) => s, + _ => panic!("Not a `jsc` global!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference to [`crate::rt::jsc::instance::Instance`]. + pub fn as_jsc_mut(&mut self) -> &mut crate::rt::jsc::instance::Instance { + match self._inner { + RuntimeInstance::Jsc(ref mut s) => s, + _ => panic!("Not a `jsc` global!"), + } + } +} diff --git a/lib/api/src/rt/jsc/entities/memory/buffer.rs b/lib/api/src/rt/jsc/entities/memory/buffer.rs new file mode 100644 index 00000000000..ef0846974cb --- /dev/null +++ b/lib/api/src/rt/jsc/entities/memory/buffer.rs @@ -0,0 +1,139 @@ +use std::{marker::PhantomData, mem::MaybeUninit}; + +use tracing::warn; + +use crate::MemoryAccessError; + +use super::MemoryView; + +/// Underlying buffer for a memory. +#[derive(Debug, Copy, Clone)] +pub(crate) struct MemoryBuffer<'a> { + pub(crate) base: *mut u8, + pub(crate) len: usize, + pub(crate) marker: PhantomData<&'a MemoryView<'a>>, +} + +impl<'a> MemoryBuffer<'a> { + pub(crate) fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> { + let end = offset + .checked_add(buf.len() as u64) + .ok_or(MemoryAccessError::Overflow)?; + if end > self.len.try_into().unwrap() { + warn!( + "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", + buf.len(), + end, + self.len + ); + return Err(MemoryAccessError::HeapOutOfBounds); + } + unsafe { + volatile_memcpy_read(self.base.add(offset as usize), buf.as_mut_ptr(), buf.len()); + } + Ok(()) + } + + pub(crate) fn read_uninit<'b>( + &self, + offset: u64, + buf: &'b mut [MaybeUninit], + ) -> Result<&'b mut [u8], MemoryAccessError> { + let end = offset + .checked_add(buf.len() as u64) + .ok_or(MemoryAccessError::Overflow)?; + if end > self.len.try_into().unwrap() { + warn!( + "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", + buf.len(), + end, + self.len + ); + return Err(MemoryAccessError::HeapOutOfBounds); + } + let buf_ptr = buf.as_mut_ptr() as *mut u8; + unsafe { + volatile_memcpy_read(self.base.add(offset as usize), buf_ptr, buf.len()); + } + + Ok(unsafe { std::slice::from_raw_parts_mut(buf_ptr, buf.len()) }) + } + + pub(crate) fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { + let end = offset + .checked_add(data.len() as u64) + .ok_or(MemoryAccessError::Overflow)?; + if end > self.len.try_into().unwrap() { + warn!( + "attempted to write ({} bytes) beyond the bounds of the memory view ({} > {})", + data.len(), + end, + self.len + ); + return Err(MemoryAccessError::HeapOutOfBounds); + } + unsafe { + volatile_memcpy_write(data.as_ptr(), self.base.add(offset as usize), data.len()); + } + Ok(()) + } +} +// We can't use a normal memcpy here because it has undefined behavior if the +// memory is being concurrently modified. So we need to write our own memcpy +// implementation which uses volatile operations. +// +// The implementation of these functions can optimize very well when inlined +// with a fixed length: they should compile down to a single load/store +// instruction for small (8/16/32/64-bit) copies. +#[inline] +unsafe fn volatile_memcpy_read(mut src: *const u8, mut dst: *mut u8, mut len: usize) { + #[inline] + unsafe fn copy_one(src: &mut *const u8, dst: &mut *mut u8, len: &mut usize) { + #[repr(packed)] + struct Unaligned(T); + let val = (*src as *const Unaligned).read_volatile(); + (*dst as *mut Unaligned).write(val); + *src = src.add(std::mem::size_of::()); + *dst = dst.add(std::mem::size_of::()); + *len -= std::mem::size_of::(); + } + + while len >= 8 { + copy_one::(&mut src, &mut dst, &mut len); + } + if len >= 4 { + copy_one::(&mut src, &mut dst, &mut len); + } + if len >= 2 { + copy_one::(&mut src, &mut dst, &mut len); + } + if len >= 1 { + copy_one::(&mut src, &mut dst, &mut len); + } +} +#[inline] +unsafe fn volatile_memcpy_write(mut src: *const u8, mut dst: *mut u8, mut len: usize) { + #[inline] + unsafe fn copy_one(src: &mut *const u8, dst: &mut *mut u8, len: &mut usize) { + #[repr(packed)] + struct Unaligned(T); + let val = (*src as *const Unaligned).read(); + (*dst as *mut Unaligned).write_volatile(val); + *src = src.add(std::mem::size_of::()); + *dst = dst.add(std::mem::size_of::()); + *len -= std::mem::size_of::(); + } + + while len >= 8 { + copy_one::(&mut src, &mut dst, &mut len); + } + if len >= 4 { + copy_one::(&mut src, &mut dst, &mut len); + } + if len >= 2 { + copy_one::(&mut src, &mut dst, &mut len); + } + if len >= 1 { + copy_one::(&mut src, &mut dst, &mut len); + } +} diff --git a/lib/api/src/rt/jsc/entities/memory/mod.rs b/lib/api/src/rt/jsc/entities/memory/mod.rs new file mode 100644 index 00000000000..f26a78d53e2 --- /dev/null +++ b/lib/api/src/rt/jsc/entities/memory/mod.rs @@ -0,0 +1,239 @@ +pub(crate) mod view; +pub(crate) use view::*; + +pub(crate) mod buffer; +pub(crate) use buffer::*; + +use crate::{jsc::vm::VMMemory, vm::VMExtern, AsStoreMut, AsStoreRef, RuntimeMemory}; +use rusty_jsc::{JSObject, JSValue}; +use wasmer_types::{MemoryType, Pages}; +use wasmer_vm::MemoryError; + +#[derive(Debug, Clone)] +pub struct Memory { + pub(crate) handle: VMMemory, +} + +// Only SharedMemories can be Send in js, becuase they support `structuredClone`. +// Normal memories will fail while doing structuredClone. +// In this case, we implement Send just in case as it can be a shared memory. +// https://developer.mozilla.org/en-US/docs/Web/API/structuredClone +// ```js +// const memory = new WebAssembly.Memory({ +// initial: 10, +// maximum: 100, +// shared: true // <--- It must be shared, otherwise structuredClone will fail +// }); +// structuredClone(memory) +// ``` +unsafe impl Send for Memory {} +unsafe impl Sync for Memory {} + +impl Memory { + pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result { + let handle = VMMemory::new(Self::js_memory_from_type(store, &ty)?, ty); + Ok(Self { handle }) + } + + pub(crate) fn js_memory_from_type( + store: &impl AsStoreRef, + ty: &MemoryType, + ) -> Result { + let store_ref = store.as_store_ref(); + let engine = store_ref.engine(); + let context = engine.as_jsc().context(); + + let mut descriptor = JSObject::new(&context); + descriptor.set_property( + &context, + "initial".to_string(), + JSValue::number(&context, ty.minimum.0.into()), + ); + if let Some(max) = ty.maximum { + descriptor.set_property( + &context, + "maximum".to_string(), + JSValue::number(&context, max.0.into()), + ); + } + descriptor.set_property( + &context, + "shared".to_string(), + JSValue::boolean(&context, ty.shared), + ); + + engine + .as_jsc() + .wasm_memory_type() + .construct(&context, &[descriptor.to_jsvalue()]) + .map_err(|e| MemoryError::Generic(format!("{:?}", e))) + } + + pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { + Self { + handle: memory.clone(), + } + } + + pub(crate) fn to_vm_extern(&self) -> VMExtern { + VMExtern::Jsc(crate::rt::jsc::vm::VMExtern::Memory(self.handle.clone())) + } + + pub fn ty(&self, _store: &impl AsStoreRef) -> MemoryType { + self.handle.ty + } + + pub fn view<'a>(&self, store: &'a impl AsStoreRef) -> MemoryView<'a> { + MemoryView::new(self, store) + } + + pub fn grow( + &self, + store: &mut impl AsStoreMut, + delta: IntoPages, + ) -> Result + where + IntoPages: Into, + { + let pages = delta.into(); + + let store_mut = store.as_store_mut(); + let engine = store_mut.engine(); + let context = engine.as_jsc().context(); + let func = self + .handle + .memory + .get_property(&context, "grow".to_string()) + .to_object(&context) + .unwrap(); + match func.call( + &context, + Some(&self.handle.memory), + &[JSValue::number(&context, pages.0 as _)], + ) { + Ok(val) => Ok(Pages(val.to_number(&context).unwrap() as _)), + Err(e) => { + let old_pages = pages; + Err(MemoryError::CouldNotGrow { + current: old_pages, + attempted_delta: pages, + }) + } + } + } + + pub fn grow_at_least( + &self, + store: &mut impl AsStoreMut, + min_size: u64, + ) -> Result<(), MemoryError> { + let cur_size = self.view(store).data_size(); + if min_size > cur_size { + let delta = min_size - cur_size; + let pages = ((delta - 1) / wasmer_types::WASM_PAGE_SIZE as u64) + 1; + + self.grow(store, Pages(pages as u32))?; + } + Ok(()) + } + + pub fn reset(&self, _store: &mut impl AsStoreMut) -> Result<(), MemoryError> { + Ok(()) + } + + pub fn copy_to_store( + &self, + store: &impl AsStoreRef, + new_store: &mut impl AsStoreMut, + ) -> Result { + let view = self.view(store); + let ty = self.ty(store); + let amount = view.data_size() as usize; + + let new_memory = Self::new(new_store, ty)?; + let mut new_view = new_memory.view(&new_store); + let new_view_size = new_view.data_size() as usize; + if amount > new_view_size { + let delta = amount - new_view_size; + let pages = ((delta - 1) / wasmer_types::WASM_PAGE_SIZE) + 1; + new_memory.grow(new_store, Pages(pages as u32))?; + new_view = new_memory.view(&new_store); + } + + // Copy the bytes + view.copy_to_memory(amount as u64, &new_view) + .map_err(|err| MemoryError::Generic(err.to_string()))?; + // // Return the new memory + Ok(new_memory) + } + + pub(crate) fn from_vm_extern( + _store: &mut impl AsStoreMut, + internal: crate::vm::VMExternMemory, + ) -> Self { + Self { + handle: internal.into_jsc(), + } + } + + /// Cloning memory will create another reference to the same memory that + /// can be put into a new store + pub fn try_clone(&self, _store: &impl AsStoreRef) -> Result { + self.handle.try_clone() + } + + /// Copying the memory will actually copy all the bytes in the memory to + /// a identical byte copy of the original that can be put into a new store + pub fn try_copy(&self, store: &impl AsStoreRef) -> Result { + let mut cloned = self.try_clone(store)?; + cloned.copy(store) + } + + pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } + + #[allow(unused)] + pub fn duplicate(&mut self, store: &impl AsStoreRef) -> Result { + self.handle.copy(store) + } + + pub fn as_shared(&self, _store: &impl AsStoreRef) -> Option { + // Not supported. + None + } +} + +impl std::cmp::PartialEq for Memory { + fn eq(&self, other: &Self) -> bool { + self.handle == other.handle + } +} + +impl std::cmp::Eq for Memory {} + +impl crate::Memory { + /// Consume [`self`] into a [`crate::rt::jsc::mem::Memory`]. + pub fn into_jsc(self) -> crate::rt::jsc::memory::Memory { + match self.0 { + RuntimeMemory::Jsc(s) => s, + _ => panic!("Not a `jsc` memory!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::jsc::mem::Memory`]. + pub fn as_jsc(&self) -> &crate::rt::jsc::memory::Memory { + match self.0 { + RuntimeMemory::Jsc(ref s) => s, + _ => panic!("Not a `jsc` memory!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference to [`crate::rt::jsc::mem::Memory`]. + pub fn as_jsc_mut(&mut self) -> &mut crate::rt::jsc::memory::Memory { + match self.0 { + RuntimeMemory::Jsc(ref mut s) => s, + _ => panic!("Not a `jsc` memory!"), + } + } +} diff --git a/lib/api/src/jsc/externals/memory_view.rs b/lib/api/src/rt/jsc/entities/memory/view.rs similarity index 94% rename from lib/api/src/jsc/externals/memory_view.rs rename to lib/api/src/rt/jsc/entities/memory/view.rs index a8b0099e781..cfdae79c29c 100644 --- a/lib/api/src/jsc/externals/memory_view.rs +++ b/lib/api/src/rt/jsc/entities/memory/view.rs @@ -1,15 +1,11 @@ -use crate::store::AsStoreRef; -use crate::MemoryAccessError; +use std::{marker::PhantomData, mem::MaybeUninit, ops::Range}; + +use super::{Memory, MemoryBuffer}; +use crate::{AsStoreRef, MemoryAccessError}; + use rusty_jsc::JSObject; -use std::convert::TryFrom; -use std::marker::PhantomData; -use std::mem::MaybeUninit; -use std::slice; -use std::{convert::TryInto, ops::Range}; use wasmer_types::{Bytes, Pages}; -use super::memory::{Memory, MemoryBuffer}; - /// A WebAssembly `memory` view. /// /// A memory view is used to read and write to the linear memory. @@ -19,7 +15,6 @@ use super::memory::{Memory, MemoryBuffer}; #[derive(Debug)] pub struct MemoryView<'a> { pub(crate) buffer: MemoryBuffer<'a>, - // pub(crate) size: Pages, } impl<'a> MemoryView<'a> { @@ -30,7 +25,7 @@ impl<'a> MemoryView<'a> { pub(crate) fn new_raw(memory: &JSObject, store: &'a (impl AsStoreRef + ?Sized)) -> Self { let store_ref = store.as_store_ref(); let engine = store_ref.engine(); - let context = engine.0.context(); + let context = engine.as_jsc().context(); let buffer = memory .get_property(&context, "buffer".to_string()) @@ -91,7 +86,7 @@ impl<'a> MemoryView<'a> { #[allow(clippy::mut_from_ref)] #[doc(hidden)] pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] { - slice::from_raw_parts_mut(self.buffer.base, self.buffer.len) + std::slice::from_raw_parts_mut(self.buffer.base, self.buffer.len) } /// Returns the size (in [`Pages`]) of the `Memory`. diff --git a/lib/api/src/rt/jsc/entities/mod.rs b/lib/api/src/rt/jsc/entities/mod.rs new file mode 100644 index 00000000000..6a280e5f757 --- /dev/null +++ b/lib/api/src/rt/jsc/entities/mod.rs @@ -0,0 +1,9 @@ +pub(crate) mod engine; +pub(crate) mod external; +pub(crate) mod function; +pub(crate) mod global; +pub(crate) mod instance; +pub(crate) mod memory; +pub(crate) mod module; +pub(crate) mod store; +pub(crate) mod table; diff --git a/lib/api/src/jsc/module.rs b/lib/api/src/rt/jsc/entities/module.rs similarity index 78% rename from lib/api/src/jsc/module.rs rename to lib/api/src/rt/jsc/entities/module.rs index 08146f4ad77..4f6755baad3 100644 --- a/lib/api/src/jsc/module.rs +++ b/lib/api/src/rt/jsc/entities/module.rs @@ -1,23 +1,21 @@ -use crate::errors::InstantiationError; -use crate::errors::RuntimeError; -use crate::imports::Imports; -use crate::jsc::as_js::AsJs; -use crate::jsc::engine::JSC; -use crate::store::AsStoreMut; -use crate::store::AsStoreRef; -use crate::vm::VMInstance; -use crate::Extern; -use crate::IntoBytes; -use crate::{AsEngineRef, ExportType, ImportType}; -use bytes::Bytes; -use rusty_jsc::{JSObject, JSString, JSValue}; use std::path::Path; -use tracing::{debug, warn}; + +use bytes::Bytes; +use rusty_jsc::{JSObject, JSValue}; +use tracing::warn; use wasmer_types::{ - CompileError, DeserializeError, ExportsIterator, ExternType, FunctionType, GlobalType, - ImportsIterator, MemoryType, ModuleInfo, Mutability, Pages, SerializeError, TableType, Type, + CompileError, DeserializeError, ExportType, ExportsIterator, ImportType, ImportsIterator, + ModuleInfo, SerializeError, +}; + +use crate::{ + jsc::{utils::convert::AsJsc, vm::VMInstance}, + AsEngineRef, AsStoreMut, AsStoreRef, Imports, InstantiationError, IntoBytes, RuntimeError, + RuntimeModule, }; +use super::engine::IntoJSC; + #[derive(Clone, PartialEq, Eq)] pub struct Module { module: JSObject, @@ -69,7 +67,7 @@ impl Module { let binary = binary.into_bytes(); // The module is now validated, so we can safely parse it's types - let info = crate::module_info_polyfill::translate_module(&binary[..]) + let info = crate::utils::polyfill::translate_module(&binary[..]) .unwrap() .info; @@ -135,7 +133,7 @@ impl Module { obj_val.set_property( &context, import_type.name().to_string(), - import.as_jsvalue(&store.as_store_ref()), + import.as_jsc_value(&store.as_store_ref()), ); } else { // If the namespace doesn't exist @@ -143,7 +141,7 @@ impl Module { import_namespace.set_property( &context, import_type.name().to_string(), - import.as_jsvalue(&store.as_store_ref()), + import.as_jsc_value(&store.as_store_ref()), ); imports_object .set_property( @@ -218,15 +216,18 @@ impl Module { true } - pub fn imports<'a>(&'a self) -> ImportsIterator + 'a> { + pub fn imports<'a>(&'a self) -> ImportsIterator + 'a>> { self.info().imports() } - pub fn exports<'a>(&'a self) -> ExportsIterator + 'a> { + pub fn exports<'a>(&'a self) -> ExportsIterator + 'a>> { self.info().exports() } - pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator> + 'a { + pub fn custom_sections<'a>( + &'a self, + name: &'a str, + ) -> Box> + 'a> { self.info().custom_sections(name) } @@ -235,26 +236,28 @@ impl Module { } } -// impl From for Module { -// fn from(module: WebAssembly::Module) -> Module { -// Module { -// module, -// name: None, -// type_hints: None, -// #[cfg(feature = "js-serializable-module")] -// raw_bytes: None, -// } -// } -// } - -// impl From<(WebAssembly::Module, T)> for crate::module::Module { -// fn from((module, binary): (WebAssembly::Module, T)) -> crate::module::Module { -// unsafe { crate::module::Module(Module::from_js_module(module, binary.into_bytes())) } -// } -// } - -// impl From for crate::module::Module { -// fn from(module: WebAssembly::Module) -> crate::module::Module { -// crate::module::Module(module.into()) -// } -// } +impl crate::Module { + /// Consume [`self`] into a reference [`crate::rt::jsc::module::Module`]. + pub fn into_jsc(self) -> crate::rt::jsc::module::Module { + match self.0 { + RuntimeModule::Jsc(s) => s, + _ => panic!("Not a `jsc` module!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::jsc::module::Module`]. + pub fn as_jsc(&self) -> &crate::rt::jsc::module::Module { + match self.0 { + RuntimeModule::Jsc(ref s) => s, + _ => panic!("Not a `jsc` module!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::jsc::module::Module`]. + pub fn as_jsc_mut(&mut self) -> &mut crate::rt::jsc::module::Module { + match self.0 { + RuntimeModule::Jsc(ref mut s) => s, + _ => panic!("Not a `jsc` module!"), + } + } +} diff --git a/lib/api/src/rt/jsc/entities/store/handle.rs b/lib/api/src/rt/jsc/entities/store/handle.rs new file mode 100644 index 00000000000..2da556ef0ab --- /dev/null +++ b/lib/api/src/rt/jsc/entities/store/handle.rs @@ -0,0 +1,158 @@ +use std::{marker::PhantomData, num::NonZeroUsize}; + +use wasmer_vm::StoreId; + +use super::{StoreObject, StoreObjects}; + +/// Handle to an object managed by a context. +/// +/// Internally this is just an integer index into a context. A reference to the +/// context must be passed in separately to access the actual object. +pub struct StoreHandle { + id: StoreId, + internal: InternalStoreHandle, +} + +impl core::cmp::PartialEq for StoreHandle { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl std::hash::Hash for StoreHandle { + fn hash(&self, state: &mut H) { + self.id.hash(state); + self.internal.idx.hash(state); + } +} + +impl Clone for StoreHandle { + fn clone(&self) -> Self { + Self { + id: self.id, + internal: self.internal, + } + } +} + +impl std::fmt::Debug for StoreHandle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("StoreHandle") + .field("id", &self.id) + .field("internal", &self.internal.index()) + .finish() + } +} + +impl StoreHandle { + /// Moves the given object into a context and returns a handle to it. + pub fn new(store: &mut StoreObjects, val: T) -> Self { + Self { + id: store.id(), + internal: InternalStoreHandle::new(store, val), + } + } + + /// Returns a reference to the object that this handle points to. + pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T { + assert_eq!(self.id, store.id(), "object used with the wrong context"); + self.internal.get(store) + } + + /// Returns a mutable reference to the object that this handle points to. + pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T { + assert_eq!(self.id, store.id(), "object used with the wrong context"); + self.internal.get_mut(store) + } + + /// Returns the internal handle contains within this handle. + pub fn internal_handle(&self) -> InternalStoreHandle { + self.internal + } + + /// Returns the ID of the context associated with the handle. + #[allow(unused)] + pub fn store_id(&self) -> StoreId { + self.id + } + + /// Overrides the store id with a new ID + #[allow(unused)] + pub fn set_store_id(&mut self, id: StoreId) { + self.id = id; + } + + /// Constructs a `StoreHandle` from a `StoreId` and an `InternalStoreHandle`. + /// + /// # Safety + /// Handling `InternalStoreHandle` values is unsafe because they do not track context ID. + pub unsafe fn from_internal(id: StoreId, internal: InternalStoreHandle) -> Self { + Self { id, internal } + } +} + +/// Internal handle to an object owned by the current context. +/// +/// Unlike `StoreHandle` this does not track the context ID: it is only +/// intended to be used within objects already owned by a context. +#[repr(transparent)] +pub struct InternalStoreHandle { + // Use a NonZero here to reduce the size of Option. + idx: NonZeroUsize, + marker: PhantomData T>, +} + +impl Clone for InternalStoreHandle { + fn clone(&self) -> Self { + *self + } +} +impl Copy for InternalStoreHandle {} + +impl std::fmt::Debug for InternalStoreHandle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("InternalStoreHandle") + .field("idx", &self.idx) + .finish() + } +} +impl PartialEq for InternalStoreHandle { + fn eq(&self, other: &Self) -> bool { + self.idx == other.idx + } +} +impl Eq for InternalStoreHandle {} + +impl InternalStoreHandle { + /// Moves the given object into a context and returns a handle to it. + pub fn new(store: &mut StoreObjects, val: T) -> Self { + let list = T::list_mut(store); + let idx = NonZeroUsize::new(list.len() + 1).unwrap(); + list.push(val); + Self { + idx, + marker: PhantomData, + } + } + + /// Returns a reference to the object that this handle points to. + pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T { + &T::list(store)[self.idx.get() - 1] + } + + /// Returns a mutable reference to the object that this handle points to. + pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T { + &mut T::list_mut(store)[self.idx.get() - 1] + } + + pub(crate) fn index(&self) -> usize { + self.idx.get() + } + + pub(crate) fn from_index(idx: usize) -> Option { + NonZeroUsize::new(idx).map(|idx| Self { + idx, + marker: PhantomData, + }) + } +} diff --git a/lib/api/src/rt/jsc/entities/store/mod.rs b/lib/api/src/rt/jsc/entities/store/mod.rs new file mode 100644 index 00000000000..e38543689e5 --- /dev/null +++ b/lib/api/src/rt/jsc/entities/store/mod.rs @@ -0,0 +1,84 @@ +pub(crate) mod handle; +pub(crate) mod obj; + +pub(crate) use handle::*; +pub(crate) use obj::*; + +use crate::{AsEngineRef, Engine, EngineRef}; + +#[derive(Debug)] +pub(crate) struct Store { + pub(crate) engine: Engine, +} + +impl Store { + pub(crate) fn new(engine: Engine) -> Self { + Self { engine } + } + + pub(crate) fn engine(&self) -> &Engine { + &self.engine + } + + pub(crate) fn engine_mut(&mut self) -> &mut Engine { + &mut self.engine + } +} + +impl AsEngineRef for Store { + fn as_engine_ref(&self) -> EngineRef<'_> { + EngineRef::new(&self.engine) + } +} + +impl crate::RuntimeStore { + /// Consume [`self`] into [`crate::rt::jsc::store::Store`]. + pub fn into_jsc(self) -> crate::rt::jsc::store::Store { + match self { + Self::Jsc(s) => s, + _ => panic!("Not a `jsc` store!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::jsc::store::Store`]. + pub fn as_jsc(&self) -> &crate::rt::jsc::store::Store { + match self { + Self::Jsc(s) => s, + _ => panic!("Not a `jsc` store!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::jsc::store::Store`]. + pub fn as_jsc_mut(&mut self) -> &mut crate::rt::jsc::store::Store { + match self { + Self::Jsc(s) => s, + _ => panic!("Not a `jsc` store!"), + } + } + /// Return true if [`self`] is a store from the `jsc` runtime. + pub fn is_jsc(&self) -> bool { + matches!(self, Self::Jsc(_)) + } +} + +impl crate::Store { + /// Consume [`self`] into [`crate::rt::jsc::store::Store`]. + pub(crate) fn into_jsc(self) -> crate::rt::jsc::store::Store { + self.inner.store.into_jsc() + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::jsc::store::Store`]. + pub(crate) fn as_jsc(&self) -> &crate::rt::jsc::store::Store { + self.inner.store.as_jsc() + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::jsc::store::Store`]. + pub(crate) fn as_jsc_mut(&mut self) -> &mut crate::rt::jsc::store::Store { + self.inner.store.as_jsc_mut() + } + + /// Return true if [`self`] is a store from the `jsc` runtime. + pub fn is_jsc(&self) -> bool { + self.inner.store.is_jsc() + } +} diff --git a/lib/api/src/rt/jsc/entities/store/obj.rs b/lib/api/src/rt/jsc/entities/store/obj.rs new file mode 100644 index 00000000000..75bc42085bd --- /dev/null +++ b/lib/api/src/rt/jsc/entities/store/obj.rs @@ -0,0 +1,134 @@ +use std::{marker::PhantomData, num::NonZeroUsize}; + +use wasmer_vm::StoreId; + +use crate::jsc::vm::{VMFunctionEnvironment, VMGlobal}; + +use super::InternalStoreHandle; + +/// Trait to represent an object managed by a context. This is implemented on +/// the VM types managed by the context. +pub trait StoreObject: Sized { + fn list(store: &StoreObjects) -> &Vec; + fn list_mut(store: &mut StoreObjects) -> &mut Vec; +} + +macro_rules! impl_store_object { + ($($field:ident => $ty:ty,)*) => { + $( + impl StoreObject for $ty { + fn list(store: &StoreObjects) -> &Vec { + &store.$field + } + fn list_mut(store: &mut StoreObjects) -> &mut Vec { + &mut store.$field + } + } + )* + }; +} + +impl_store_object! { + // Note: we store the globals in order to be able to access them later via + // `StoreObjects::iter_globals`. + globals => VMGlobal, + // functions => VMFunction, + // tables => VMTable, + // memories => VMMemory, + // The function environments are the only things attached to a store, + // since the other JS objects (table, globals, memory and functions) + // live in the JS VM Store by default + function_environments => VMFunctionEnvironment, +} + +/// Set of objects managed by a context. +#[derive(Default, Debug)] +pub struct StoreObjects { + id: StoreId, + globals: Vec, + function_environments: Vec, +} + +impl StoreObjects { + /// Returns the ID of this context. + pub fn id(&self) -> StoreId { + self.id + } + + /// Sets the ID of this store + pub fn set_id(&mut self, id: StoreId) { + self.id = id; + } + + /// Returns a pair of mutable references from two handles. + /// + /// Panics if both handles point to the same object. + pub fn get_2_mut( + &mut self, + a: InternalStoreHandle, + b: InternalStoreHandle, + ) -> (&mut T, &mut T) { + assert_ne!(a.index(), b.index()); + let list = T::list_mut(self); + if a.index() < b.index() { + let (low, high) = list.split_at_mut(b.index()); + (&mut low[a.index()], &mut high[0]) + } else { + let (low, high) = list.split_at_mut(a.index()); + (&mut high[0], &mut low[a.index()]) + } + } + + /// Return an immutable iterator over all globals + pub fn iter_globals(&self) -> core::slice::Iter { + self.globals.iter() + } + + /// Return an vector of all globals and converted to u128 + pub fn as_u128_globals(&self) -> Vec { + vec![] + // self.iter_globals() + // .map(|v| v.global.value().as_f64().unwrap() as u128) + // .collect() + } + + /// Set a global, at index idx. Will panic if idx is out of range + /// Safety: the caller should check taht the raw value is compatible + /// with destination VMGlobal type + pub fn set_global_unchecked(&self, idx: usize, new_val: u128) { + assert!(idx < self.globals.len()); + // let g = &self.globals[idx].global; + // let cur_val = g.value().as_f64().unwrap(); + // let new_val = new_val as f64; + // if cur_val != new_val { + // let new_value = JSValue::from(new_val); + // g.set_value(&new_value); + // } + } +} + +impl crate::StoreObjects { + /// Consume [`self`] into [`crate::rt::jsc::store::StoreObjects`]. + pub fn into_jsc(self) -> crate::rt::jsc::store::StoreObjects { + match self { + Self::Jsc(s) => s, + _ => panic!("Not a `jsc` store!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::jsc::store::StoreObjects`]. + pub fn as_jsc(&self) -> &crate::rt::jsc::store::StoreObjects { + match self { + Self::Jsc(s) => s, + _ => panic!("Not a `jsc` store!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::jsc::store::StoreObjects`]. + pub fn as_jsc_mut(&mut self) -> &mut crate::rt::jsc::store::StoreObjects { + match self { + Self::Jsc(s) => s, + _ => panic!("Not a `jsc` store!"), + } + } +} diff --git a/lib/api/src/jsc/externals/table.rs b/lib/api/src/rt/jsc/entities/table.rs similarity index 60% rename from lib/api/src/jsc/externals/table.rs rename to lib/api/src/rt/jsc/entities/table.rs index 24159773b13..d4753a2be6e 100644 --- a/lib/api/src/jsc/externals/table.rs +++ b/lib/api/src/rt/jsc/entities/table.rs @@ -1,12 +1,13 @@ -use crate::errors::RuntimeError; -use crate::store::{AsStoreMut, AsStoreRef}; -use crate::value::Value; -use crate::vm::VMExternTable; -use crate::vm::{VMExtern, VMFunction, VMTable}; -use crate::{FunctionType, TableType}; use rusty_jsc::{JSObject, JSValue}; +use wasmer_types::TableType; -#[derive(Debug, Clone, PartialEq)] +use crate::{ + jsc::vm::{VMExternTable, VMTable}, + vm::VMExtern, + AsStoreMut, AsStoreRef, RuntimeError, RuntimeTable, Value, +}; + +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Table { pub(crate) handle: VMTable, } @@ -22,14 +23,6 @@ fn set_table_item(table: &VMTable, item_index: u32, item: &JSObject) -> Result<( fn get_function(store: &mut impl AsStoreMut, val: Value) -> Result { unimplemented!(); - // if !val.is_from_store(store) { - // return Err(RuntimeError::new("cannot pass Value across contexts")); - // } - // match val { - // Value::FuncRef(Some(ref func)) => Ok(func.0.handle.function.clone().into()), - // // Only funcrefs is supported by the spec atm - // _ => unimplemented!("The {val:?} is not yet supported"), - // } } impl Table { @@ -40,7 +33,7 @@ impl Table { ) -> Result { let store_mut = store.as_store_mut(); let engine = store_mut.engine(); - let context = engine.0.context(); + let context = engine.as_jsc().context(); let mut descriptor = JSObject::new(&context); descriptor.set_property( @@ -62,16 +55,16 @@ impl Table { ); let js_table = engine - .0 + .as_jsc() .wasm_table_type() .construct(&context, &[descriptor.to_jsvalue()]) .map_err(|e| >::into(e))?; let vm_table = VMTable::new(js_table, ty); - Ok(Self::from_vm_extern(store, vm_table)) + Ok(Self { handle: vm_table }) } pub fn to_vm_extern(&self) -> VMExtern { - VMExtern::Table(self.handle.clone()) + VMExtern::Jsc(crate::rt::jsc::vm::VMExtern::Table(self.handle.clone())) } pub fn ty(&self, _store: &impl AsStoreRef) -> TableType { @@ -80,14 +73,6 @@ impl Table { pub fn get(&self, store: &mut impl AsStoreMut, index: u32) -> Option { unimplemented!(); - // if let Some(func) = self.handle.table.get(index).ok() { - // let ty = FunctionType::new(vec![], vec![]); - // let vm_function = VMFunction::new(func, ty); - // let function = crate::Function::from_vm_extern(store, vm_function); - // Some(Value::FuncRef(Some(function))) - // } else { - // None - // } } pub fn set( @@ -104,7 +89,7 @@ impl Table { pub fn size(&self, store: &impl AsStoreRef) -> u32 { let store_mut = store.as_store_ref(); let engine = store_mut.engine(); - let context = engine.0.context(); + let context = engine.as_jsc().context(); self.handle .table .get_property(&context, "length".to_string()) @@ -120,7 +105,7 @@ impl Table { ) -> Result { let store_mut = store.as_store_mut(); let engine = store_mut.engine(); - let context = engine.0.context(); + let context = engine.as_jsc().context(); let func = self .handle .table @@ -148,11 +133,68 @@ impl Table { unimplemented!("Table.copy is not natively supported in Javascript"); } - pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, vm_extern: VMExternTable) -> Self { - Self { handle: vm_extern } + pub(crate) fn from_vm_extern( + _store: &mut impl AsStoreMut, + vm_extern: crate::vm::VMExternTable, + ) -> Self { + Self { + handle: vm_extern.into_jsc(), + } } pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { true } } + +impl crate::Table { + /// Consume [`self`] into [`crate::rt::jsc::table::Table`]. + pub fn into_jsc(self) -> crate::rt::jsc::table::Table { + match self.0 { + RuntimeTable::Jsc(s) => s, + _ => panic!("Not a `jsc` table!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::jsc::table::Table`]. + pub fn as_jsc(&self) -> &crate::rt::jsc::table::Table { + match self.0 { + RuntimeTable::Jsc(ref s) => s, + _ => panic!("Not a `jsc` table!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::jsc::table::Table`]. + pub fn as_jsc_mut(&mut self) -> &mut crate::rt::jsc::table::Table { + match self.0 { + RuntimeTable::Jsc(ref mut s) => s, + _ => panic!("Not a `jsc` table!"), + } + } +} + +impl crate::RuntimeTable { + /// Consume [`self`] into [`crate::rt::jsc::table::Table`]. + pub fn into_jsc(self) -> crate::rt::jsc::table::Table { + match self { + Self::Jsc(s) => s, + _ => panic!("Not a `jsc` table!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::jsc::table::Table`]. + pub fn as_jsc(&self) -> &crate::rt::jsc::table::Table { + match self { + Self::Jsc(ref s) => s, + _ => panic!("Not a `jsc` table!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::jsc::table::Table`]. + pub fn as_jsc_mut(&mut self) -> &mut crate::rt::jsc::table::Table { + match self { + Self::Jsc(ref mut s) => s, + _ => panic!("Not a `jsc` table!"), + } + } +} diff --git a/lib/api/src/jsc/trap.rs b/lib/api/src/rt/jsc/error.rs similarity index 89% rename from lib/api/src/jsc/trap.rs rename to lib/api/src/rt/jsc/error.rs index fa606fc1378..13b27822200 100644 --- a/lib/api/src/jsc/trap.rs +++ b/lib/api/src/rt/jsc/error.rs @@ -52,7 +52,7 @@ impl Trap { } } - pub(crate) fn into_jsvalue(self, ctx: &JSContext) -> JSValue { + pub(crate) fn into_jsc_value(self, ctx: &JSContext) -> JSValue { match self.inner { InnerTrap::User(err) => { let obj = JSObject::new(ctx); @@ -66,7 +66,7 @@ impl Trap { } } - pub(crate) fn from_jsvalue(ctx: &JSContext, val: JSValue) -> Self { + pub(crate) fn from_jsc_value(ctx: &JSContext, val: JSValue) -> Self { let obj_val = val.to_object(ctx).unwrap(); let wasmer_error_ptr = obj_val.get_property(&ctx, "wasmer_error_ptr".to_string()); if wasmer_error_ptr.is_number(ctx) { @@ -115,3 +115,13 @@ impl From for RuntimeError { // trap.into() } } + +impl From for RuntimeError { + fn from(trap: Trap) -> Self { + if trap.is::() { + return trap.downcast::().unwrap(); + } + + RuntimeError::new_from_source(crate::RuntimeTrap::Jsc(trap), vec![], None) + } +} diff --git a/lib/api/src/rt/jsc/mod.rs b/lib/api/src/rt/jsc/mod.rs new file mode 100644 index 00000000000..4ff2a3aed73 --- /dev/null +++ b/lib/api/src/rt/jsc/mod.rs @@ -0,0 +1,8 @@ +//! Data types, functions and traits for the `sys` runtime. + +pub(crate) mod entities; +pub(crate) mod error; +pub(crate) mod utils; +pub(crate) mod vm; + +pub use entities::*; diff --git a/lib/api/src/jsc/as_js.rs b/lib/api/src/rt/jsc/utils/convert.rs similarity index 65% rename from lib/api/src/jsc/as_js.rs rename to lib/api/src/rt/jsc/utils/convert.rs index 77175fc121d..610a3c66e21 100644 --- a/lib/api/src/jsc/as_js.rs +++ b/lib/api/src/rt/jsc/utils/convert.rs @@ -1,25 +1,16 @@ -use crate::imports::Imports; -use crate::instance::Instance; -use crate::jsc::engine::JSC; -use crate::jsc::instance::Instance as JsInstance; -use crate::jsc::vm::{VMFunction, VMGlobal, VMMemory, VMTable}; -use crate::store::{AsStoreMut, AsStoreRef}; -use crate::value::Value; -use crate::Type; -use crate::{Extern, Function, Global, Memory, Table}; -use rusty_jsc::{JSContext, JSObject, JSValue}; -use std::collections::HashMap; -use std::convert::TryInto; -use wasmer_types::ExternType; +use rusty_jsc::{JSContext, JSValue}; +use wasmer_types::{ExternType, Type}; + +use crate::{jsc::engine::IntoJSC, AsStoreMut, AsStoreRef, Extern, Function, Value}; /// Convert the given type to a [`JsValue`]. -pub trait AsJs: Sized { +pub trait AsJsc: Sized { /// The inner definition type from this Javascript object type DefinitionType; /// Convert the given type to a [`JsValue`]. - fn as_jsvalue(&self, store: &impl AsStoreRef) -> JSValue; + fn as_jsc_value(&self, store: &impl AsStoreRef) -> JSValue; /// Convert the given type to a [`JsValue`]. - fn from_jsvalue( + fn from_jsc_value( store: &mut impl AsStoreMut, type_: &Self::DefinitionType, value: &JSValue, @@ -27,7 +18,7 @@ pub trait AsJs: Sized { } #[inline] -pub fn param_from_js(context: &JSContext, ty: &Type, js_val: &JSValue) -> Value { +pub fn jsc_value_to_wasmer(context: &JSContext, ty: &Type, js_val: &JSValue) -> Value { match ty { Type::I32 => Value::I32(js_val.to_number(&context).unwrap() as _), Type::I64 => { @@ -65,10 +56,10 @@ pub fn param_from_js(context: &JSContext, ty: &Type, js_val: &JSValue) -> Value } } -impl AsJs for Value { +impl AsJsc for Value { type DefinitionType = Type; - fn as_jsvalue(&self, store: &impl AsStoreRef) -> JSValue { + fn as_jsc_value(&self, store: &impl AsStoreRef) -> JSValue { let engine = store.as_store_ref(); let context = engine.jsc().context(); match self { @@ -81,36 +72,36 @@ impl AsJs for Value { Self::F32(f) => JSValue::number(&context, *f as _), Self::F64(f) => JSValue::number(&context, *f), Self::V128(v) => JSValue::number(&context, *v as _), - Self::FuncRef(Some(func)) => func.0.handle.function.clone().to_jsvalue(), + Self::FuncRef(Some(func)) => func.as_jsc().handle.function.clone().to_jsvalue(), Self::FuncRef(None) => JSValue::null(&context), Self::ExternRef(_) => unimplemented!(), } } - fn from_jsvalue( + fn from_jsc_value( store: &mut impl AsStoreMut, type_: &Self::DefinitionType, value: &JSValue, ) -> Result { let engine = store.as_store_ref(); let context = engine.jsc().context(); - Ok(param_from_js(context, type_, value)) + Ok(jsc_value_to_wasmer(context, type_, value)) } } -impl AsJs for Extern { +impl AsJsc for Extern { type DefinitionType = ExternType; - fn as_jsvalue(&self, _store: &impl AsStoreRef) -> JSValue { + fn as_jsc_value(&self, _store: &impl AsStoreRef) -> JSValue { match self { - Self::Memory(memory) => memory.0.handle.memory.clone().to_jsvalue(), - Self::Function(function) => function.0.handle.function.clone().to_jsvalue(), - Self::Table(table) => table.0.handle.table.clone().to_jsvalue(), - Self::Global(global) => global.0.handle.global.clone().to_jsvalue(), + Self::Memory(memory) => memory.as_jsc().handle.memory.clone().to_jsvalue(), + Self::Function(function) => function.as_jsc().handle.function.clone().to_jsvalue(), + Self::Table(table) => table.as_jsc().handle.table.clone().to_jsvalue(), + Self::Global(global) => global.as_jsc().handle.global.clone().to_jsvalue(), } } - fn from_jsvalue( + fn from_jsc_value( store: &mut impl AsStoreMut, extern_type: &Self::DefinitionType, val: &JSValue, @@ -125,28 +116,40 @@ impl AsJs for Extern { let obj_val = val.to_object(&context).unwrap(); Ok(Self::Function(Function::from_vm_extern( store, - VMFunction::new(obj_val, function_type.clone()), + crate::vm::VMExternFunction::Jsc(crate::rt::jsc::vm::VMExternFunction::new( + obj_val, + function_type.clone(), + )), ))) } ExternType::Global(global_type) => { let obj_val = val.to_object(&context).unwrap(); - Ok(Self::Global(Global::from_vm_extern( + Ok(Self::Global(crate::Global::from_vm_extern( store, - VMGlobal::new(obj_val, global_type.clone()), + crate::vm::VMExternGlobal::Jsc(crate::rt::jsc::vm::VMExternGlobal::new( + obj_val, + global_type.clone(), + )), ))) } ExternType::Memory(memory_type) => { let obj_val = val.to_object(&context).unwrap(); - Ok(Self::Memory(Memory::from_vm_extern( + Ok(Self::Memory(crate::Memory::from_vm_extern( store, - VMMemory::new(obj_val, memory_type.clone()), + crate::vm::VMExternMemory::Jsc(crate::rt::jsc::vm::VMExternMemory::new( + obj_val, + memory_type.clone(), + )), ))) } ExternType::Table(table_type) => { let obj_val = val.to_object(&context).unwrap(); - Ok(Self::Table(Table::from_vm_extern( + Ok(Self::Table(crate::Table::from_vm_extern( store, - VMTable::new(obj_val, table_type.clone()), + crate::vm::VMExternTable::Jsc(crate::rt::jsc::vm::VMExternTable::new( + obj_val, + table_type.clone(), + )), ))) } } diff --git a/lib/api/src/rt/jsc/utils/mod.rs b/lib/api/src/rt/jsc/utils/mod.rs new file mode 100644 index 00000000000..02d63d4e141 --- /dev/null +++ b/lib/api/src/rt/jsc/utils/mod.rs @@ -0,0 +1 @@ +pub(crate) mod convert; diff --git a/lib/api/src/rt/jsc/vm/external.rs b/lib/api/src/rt/jsc/vm/external.rs new file mode 100644 index 00000000000..3610cdfac7c --- /dev/null +++ b/lib/api/src/rt/jsc/vm/external.rs @@ -0,0 +1,61 @@ +use wasmer_types::RawValue; + +use crate::{AsStoreMut, Extern, VMExternToExtern}; + +use super::{function::VMFunction, global::VMGlobal, memory::VMMemory, table::VMTable}; + +/// The value of an export passed from one instance to another in the `jsc` VM. +pub enum VMExtern { + /// A function export value. + Function(VMFunction), + + /// A table export value. + Table(VMTable), + + /// A memory export value. + Memory(VMMemory), + + /// A global export value. + Global(VMGlobal), +} + +impl VMExternToExtern for VMExtern { + fn to_extern(self, store: &mut impl AsStoreMut) -> Extern { + match self { + Self::Function(f) => Extern::Function(crate::Function::from_vm_extern( + store, + crate::vm::VMExternFunction::Jsc(f), + )), + Self::Memory(m) => Extern::Memory(crate::Memory::from_vm_extern( + store, + crate::vm::VMExternMemory::Jsc(m), + )), + Self::Global(g) => Extern::Global(crate::Global::from_vm_extern( + store, + crate::vm::VMExternGlobal::Jsc(g), + )), + Self::Table(t) => Extern::Table(crate::Table::from_vm_extern( + store, + crate::vm::VMExternTable::Jsc(t), + )), + } + } +} + +/// A reference to an external value in the `js` VM. +pub struct VMExternRef; + +impl VMExternRef { + /// Converts the `VMExternRef` into a `RawValue`. + pub fn into_raw(self) -> RawValue { + unimplemented!(); + } + + /// Extracts a `VMExternRef` from a `RawValue`. + /// + /// # Safety + /// `raw` must be a valid `VMExternRef` instance. + pub unsafe fn from_raw(_raw: RawValue) -> Option { + unimplemented!(); + } +} diff --git a/lib/api/src/rt/jsc/vm/function.rs b/lib/api/src/rt/jsc/vm/function.rs new file mode 100644 index 00000000000..3fe4e978655 --- /dev/null +++ b/lib/api/src/rt/jsc/vm/function.rs @@ -0,0 +1,86 @@ +use std::any::Any; + +use rusty_jsc::{JSObject, JSObjectCallAsFunctionCallback}; +use wasmer_types::{FunctionType, RawValue}; + +/// The VM Function type +#[derive(Clone, Eq)] +pub struct VMFunction { + pub(crate) function: JSObject, + pub(crate) ty: FunctionType, +} + +unsafe impl Send for VMFunction {} +unsafe impl Sync for VMFunction {} + +impl VMFunction { + pub(crate) fn new(function: JSObject, ty: FunctionType) -> Self { + Self { function, ty } + } +} + +impl PartialEq for VMFunction { + fn eq(&self, other: &Self) -> bool { + self.function == other.function + } +} + +impl std::fmt::Debug for VMFunction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("VMFunction") + .field("function", &self.function) + .finish() + } +} + +/// Underlying FunctionEnvironment used by a `VMFunction`. +#[derive(Debug)] +pub struct VMFunctionEnvironment { + pub(crate) contents: Box, +} + +impl VMFunctionEnvironment { + /// Wraps the given value to expose it to Wasm code as a function context. + pub fn new(val: impl Any + Send + 'static) -> Self { + Self { + contents: Box::new(val), + } + } + + #[allow(clippy::should_implement_trait)] + /// Returns a reference to the underlying value. + pub fn as_ref(&self) -> &(dyn Any + Send + 'static) { + &*self.contents + } + + #[allow(clippy::should_implement_trait)] + /// Returns a mutable reference to the underlying value. + pub fn as_mut(&mut self) -> &mut (dyn Any + Send + 'static) { + &mut *self.contents + } +} + +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub(crate) struct VMFuncRef; + +impl VMFuncRef { + /// Converts the `VMFuncRef` into a `RawValue`. + pub fn into_raw(self) -> RawValue { + unimplemented!(); + } + + /// Extracts a `VMFuncRef` from a `RawValue`. + /// + /// # Safety + /// `raw.funcref` must be a valid pointer. + pub unsafe fn from_raw(_raw: RawValue) -> Option { + unimplemented!(); + } +} + +/// The type of function callbackes in the `jsc` runtime. +pub type VMFunctionCallback = JSObjectCallAsFunctionCallback; + +/// Necessary for the top-level macros to work as intended. +pub(crate) type VMFunctionBody = (); diff --git a/lib/api/src/rt/jsc/vm/global.rs b/lib/api/src/rt/jsc/vm/global.rs new file mode 100644 index 00000000000..31542e78419 --- /dev/null +++ b/lib/api/src/rt/jsc/vm/global.rs @@ -0,0 +1,18 @@ +use rusty_jsc::JSObject; +use wasmer_types::GlobalType; + +/// The VM Global type +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct VMGlobal { + pub(crate) global: JSObject, + pub(crate) ty: GlobalType, +} + +impl VMGlobal { + pub(crate) fn new(global: JSObject, ty: GlobalType) -> Self { + Self { global, ty } + } +} + +unsafe impl Send for VMGlobal {} +unsafe impl Sync for VMGlobal {} diff --git a/lib/api/src/rt/jsc/vm/memory.rs b/lib/api/src/rt/jsc/vm/memory.rs new file mode 100644 index 00000000000..d2b55672ff2 --- /dev/null +++ b/lib/api/src/rt/jsc/vm/memory.rs @@ -0,0 +1,67 @@ +use rusty_jsc::JSObject; +use tracing::trace; +use wasmer_types::MemoryType; +use wasmer_vm::MemoryError; + +use crate::AsStoreRef; + +/// Represents linear memory that is managed by the Javascript Core runtime +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct VMMemory { + pub(crate) memory: JSObject, + pub(crate) ty: MemoryType, +} + +unsafe impl Send for VMMemory {} +unsafe impl Sync for VMMemory {} + +impl VMMemory { + /// Creates a new memory directly from a WebAssembly javascript object + pub fn new(memory: JSObject, ty: MemoryType) -> Self { + Self { memory, ty } + } + + /// Returns the size of the memory buffer in pages + pub fn get_runtime_size(&self) -> u32 { + unimplemented!(); + // let dummy: DummyBuffer = match serde_wasm_bindgen::from_value(self.memory.buffer()) { + // Ok(o) => o, + // Err(_) => return 0, + // }; + // if dummy.byte_length == 0 { + // return 0; + // } + // dummy.byte_length / WASM_PAGE_SIZE as u32 + } + + /// Attempts to clone this memory (if its clonable) + pub(crate) fn try_clone(&self) -> Result { + Ok(self.clone()) + } + + /// Copies this memory to a new memory + pub fn copy(&self, store: &impl AsStoreRef) -> Result { + let new_memory = crate::jsc::memory::Memory::js_memory_from_type(&store, &self.ty)?; + + trace!("memory copy started"); + + let src = crate::jsc::memory::view::MemoryView::new_raw(&self.memory, store); + let amount = src.data_size() as usize; + let mut dst = crate::jsc::memory::view::MemoryView::new_raw(&new_memory, store); + let dst_size = dst.data_size() as usize; + + src.copy_to_memory(amount as u64, &dst).map_err(|err| { + wasmer_types::MemoryError::Generic(format!("failed to copy the memory - {}", err)) + })?; + + trace!("memory copy finished (size={})", dst.size().bytes().0); + + Ok(Self { + memory: new_memory, + ty: self.ty.clone(), + }) + } +} + +/// Shared VM memory, in `jsc`, is the "normal" memory. +pub type VMSharedMemory = VMMemory; diff --git a/lib/api/src/rt/jsc/vm/mod.rs b/lib/api/src/rt/jsc/vm/mod.rs new file mode 100644 index 00000000000..bb9fa7407b8 --- /dev/null +++ b/lib/api/src/rt/jsc/vm/mod.rs @@ -0,0 +1,26 @@ +pub(crate) mod external; +pub(crate) mod function; +pub(crate) mod global; +pub(crate) mod memory; +pub(crate) mod table; +pub use super::error::Trap; + +pub use external::*; +pub use function::*; +pub use global::*; +pub use memory::*; +pub use table::*; + +/// The type of instances in the `js` VM. +pub type VMInstance = rusty_jsc::JSObject; + +pub struct VMTrampoline; + +/// The type of extern tables in the `js` VM. +pub(crate) type VMExternTable = VMTable; +/// The type of extern memories in the `js` VM. +pub(crate) type VMExternMemory = VMMemory; +/// The type of extern globals in the `js` VM. +pub(crate) type VMExternGlobal = VMGlobal; +/// The type of extern functions in the `js` VM. +pub(crate) type VMExternFunction = VMFunction; diff --git a/lib/api/src/rt/jsc/vm/table.rs b/lib/api/src/rt/jsc/vm/table.rs new file mode 100644 index 00000000000..2fdbedce942 --- /dev/null +++ b/lib/api/src/rt/jsc/vm/table.rs @@ -0,0 +1,24 @@ +use rusty_jsc::JSObject; +use wasmer_types::TableType; + +/// The VM Table type +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct VMTable { + pub(crate) table: JSObject, + pub(crate) ty: TableType, +} + +unsafe impl Send for VMTable {} +unsafe impl Sync for VMTable {} + +impl VMTable { + pub(crate) fn new(table: JSObject, ty: TableType) -> Self { + Self { table, ty } + } + + /// Get the table size at runtime + pub fn get_runtime_size(&self) -> u32 { + unimplemented!(); + // self.table.length() + } +} diff --git a/lib/api/src/rt/mod.rs b/lib/api/src/rt/mod.rs new file mode 100644 index 00000000000..5fdd396cf53 --- /dev/null +++ b/lib/api/src/rt/mod.rs @@ -0,0 +1,48 @@ +//! This submodule has the concrete definitions for all the available implenters of the WebAssembly +//! types needed to create a runtime. + +#[cfg(feature = "sys")] +pub mod sys; + +#[cfg(feature = "wamr")] +pub mod wamr; + +#[cfg(feature = "wasmi")] +pub mod wasmi; + +#[cfg(feature = "v8")] +pub mod v8; + +#[cfg(feature = "js")] +pub mod js; + +#[cfg(feature = "jsc")] +pub mod jsc; + +#[derive(Debug, Clone, Copy)] +/// An enumeration over all the supported runtimes. +pub enum Runtime { + #[cfg(feature = "sys")] + /// The `sys` runtime. + Sys, + + #[cfg(feature = "wamr")] + /// The `wamr` runtime. + Wamr, + + #[cfg(feature = "wasmi")] + /// The `wasmi` runtime. + Wasmi, + + #[cfg(feature = "v8")] + /// The `v8` runtime. + V8, + + #[cfg(feature = "js")] + /// The `js` runtime. + Js, + + #[cfg(feature = "jsc")] + /// The `jsc` runtime. + Jsc, +} diff --git a/lib/api/src/sys/engine.rs b/lib/api/src/rt/sys/entities/engine.rs similarity index 56% rename from lib/api/src/sys/engine.rs rename to lib/api/src/rt/sys/entities/engine.rs index de4c5e11eff..fa5c44caf5a 100644 --- a/lib/api/src/sys/engine.rs +++ b/lib/api/src/rt/sys/entities/engine.rs @@ -1,3 +1,5 @@ +//! Data types, functions and traits for `sys` runtime's `Engine` implementation. + use std::{path::Path, sync::Arc}; use shared_buffer::OwnedBuffer; @@ -8,6 +10,8 @@ pub use wasmer_compiler::{ use wasmer_types::Features; use wasmer_types::{DeserializeError, HashAlgorithm}; +use crate::{RuntimeEngine, RuntimeModule}; + /// Get the default config for the sys Engine #[allow(unreachable_code)] pub fn get_default_compiler_config() -> Option> { @@ -108,23 +112,38 @@ pub trait NativeEngineExt { impl NativeEngineExt for crate::engine::Engine { #[cfg(feature = "compiler")] fn new(compiler_config: Box, target: Target, features: Features) -> Self { - Self(Engine::new(compiler_config, target, features)) + Self { + rt: RuntimeEngine::Sys(Engine::new(compiler_config, target, features)), + id: Self::atomic_next_engine_id(), + } } fn headless() -> Self { - Self(Engine::headless()) + Self { + rt: RuntimeEngine::Sys(Engine::headless()), + id: Self::atomic_next_engine_id(), + } } fn target(&self) -> &Target { - self.0.target() + match self.rt { + RuntimeEngine::Sys(ref s) => s.target(), + _ => panic!("Not a `sys` engine!"), + } } fn set_tunables(&mut self, tunables: impl Tunables + Send + Sync + 'static) { - self.0.set_tunables(tunables) + match self.rt { + RuntimeEngine::Sys(ref mut s) => s.set_tunables(tunables), + _ => panic!("Not a `sys` engine!"), + } } fn tunables(&self) -> &dyn Tunables { - self.0.tunables() + match self.rt { + RuntimeEngine::Sys(ref s) => s.tunables(), + _ => panic!("Not a `sys` engine!"), + } } unsafe fn deserialize_from_mmapped_file_unchecked( @@ -133,12 +152,12 @@ impl NativeEngineExt for crate::engine::Engine { ) -> Result { let file = std::fs::File::open(file_ref)?; let artifact = Arc::new(Artifact::deserialize_unchecked( - &self.0, + self.as_sys(), OwnedBuffer::from_file(&file) .map_err(|e| DeserializeError::Generic(format!("{e:?}")))?, )?); - Ok(crate::Module(super::module::Module::from_artifact( - artifact, + Ok(crate::Module(RuntimeModule::Sys( + super::module::Module::from_artifact(artifact), ))) } @@ -148,16 +167,107 @@ impl NativeEngineExt for crate::engine::Engine { ) -> Result { let file = std::fs::File::open(file_ref)?; let artifact = Arc::new(Artifact::deserialize( - &self.0, + self.as_sys(), OwnedBuffer::from_file(&file) .map_err(|e| DeserializeError::Generic(format!("{e:?}")))?, )?); - Ok(crate::Module(super::module::Module::from_artifact( - artifact, + Ok(crate::Module(RuntimeModule::Sys( + super::module::Module::from_artifact(artifact), ))) } fn set_hash_algorithm(&mut self, hash_algorithm: Option) { - self.0.set_hash_algorithm(hash_algorithm) + match self.rt { + RuntimeEngine::Sys(ref mut s) => s.set_hash_algorithm(hash_algorithm), + _ => panic!("Not a `sys` engine!"), + } + } +} + +impl crate::Engine { + /// Consume [`self`] into a [`crate::rt::sys::engine::Engine`]. + pub fn into_sys(self) -> crate::rt::sys::engine::Engine { + match self.rt { + RuntimeEngine::Sys(s) => s, + _ => panic!("Not a `sys` engine!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::sys::engine::Engine`]. + pub fn as_sys(&self) -> &crate::rt::sys::engine::Engine { + match self.rt { + RuntimeEngine::Sys(ref s) => s, + _ => panic!("Not a `sys` engine!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::sys::engine::Engine`]. + pub fn as_sys_mut(&mut self) -> &mut crate::rt::sys::engine::Engine { + match self.rt { + RuntimeEngine::Sys(ref mut s) => s, + _ => panic!("Not a `sys` engine!"), + } + } + + /// Return true if [`self`] is an engine from the `sys` runtime. + pub fn is_sys(&self) -> bool { + matches!(self.rt, RuntimeEngine::Sys(_)) + } +} + +impl From for crate::Engine { + fn from(value: Engine) -> Self { + Self { + rt: RuntimeEngine::Sys(value), + id: Self::atomic_next_engine_id(), + } + } +} + +impl From<&Engine> for crate::Engine { + fn from(value: &Engine) -> Self { + Self { + rt: RuntimeEngine::Sys(value.cloned()), + id: Self::atomic_next_engine_id(), + } + } +} + +impl From for crate::Engine { + fn from(value: EngineBuilder) -> Self { + Self { + rt: RuntimeEngine::Sys(value.engine()), + id: Self::atomic_next_engine_id(), + } + } +} + +#[cfg(feature = "cranelift")] +impl From for crate::Engine { + fn from(value: wasmer_compiler_cranelift::Cranelift) -> Self { + Self { + rt: RuntimeEngine::Sys(value.into()), + id: Self::atomic_next_engine_id(), + } + } +} + +#[cfg(feature = "singlepass")] +impl From for crate::Engine { + fn from(value: wasmer_compiler_singlepass::Singlepass) -> Self { + Self { + rt: RuntimeEngine::Sys(value.into()), + id: Self::atomic_next_engine_id(), + } + } +} + +#[cfg(feature = "llvm")] +impl From for crate::Engine { + fn from(value: wasmer_compiler_llvm::LLVM) -> Self { + Self { + rt: RuntimeEngine::Sys(value.into()), + id: Self::atomic_next_engine_id(), + } } } diff --git a/lib/api/src/rt/sys/entities/external.rs b/lib/api/src/rt/sys/entities/external.rs new file mode 100644 index 00000000000..63342f0f01b --- /dev/null +++ b/lib/api/src/rt/sys/entities/external.rs @@ -0,0 +1,65 @@ +//! Data types, functions and traits for `sys` runtime's `ExternRef` implementation. + +use std::any::Any; +use wasmer_vm::{StoreHandle, VMExternRef}; + +use crate::store::{AsStoreMut, AsStoreRef}; + +#[derive(Debug, Clone)] +#[repr(transparent)] +/// A WebAssembly `extern ref` in the `sys` runtime. +pub(crate) struct ExternRef { + handle: StoreHandle, +} + +impl ExternRef { + /// Make a new extern reference + pub fn new(store: &mut impl AsStoreMut, value: T) -> Self + where + T: Any + Send + Sync + 'static + Sized, + { + Self { + handle: crate::rt::sys::store::StoreHandle::new( + store.objects_mut().as_sys_mut(), + wasmer_vm::VMExternObj::new(value), + ), + } + } + + /// Try to downcast to the given value. + pub fn downcast<'a, T>(&self, store: &'a impl AsStoreRef) -> Option<&'a T> + where + T: Any + Send + Sync + 'static + Sized, + { + self.handle + .get(store.as_store_ref().objects().as_sys()) + .as_ref() + .downcast_ref::() + } + + /// Create a [`VMExternRef`] from [`Self`]. + pub(crate) fn vm_externref(&self) -> VMExternRef { + wasmer_vm::VMExternRef(self.handle.internal_handle()) + } + + /// Create an instance of [`Self`] from a [`VMExternRef`]. + pub(crate) unsafe fn from_vm_externref( + store: &mut impl AsStoreMut, + vm_externref: VMExternRef, + ) -> Self { + Self { + handle: StoreHandle::from_internal(store.objects_mut().id(), vm_externref.0), + } + } + + /// Checks whether this `ExternRef` can be used with the given context. + /// + /// Primitive (`i32`, `i64`, etc) and null funcref/externref values are not + /// tied to a context and can be freely shared between contexts. + /// + /// Externref and funcref values are tied to a context and can only be used + /// with that context. + pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + self.handle.store_id() == store.as_store_ref().objects().id() + } +} diff --git a/lib/api/src/function_env.rs b/lib/api/src/rt/sys/entities/function/env.rs similarity index 71% rename from lib/api/src/function_env.rs rename to lib/api/src/rt/sys/entities/function/env.rs index 89c1de7e0d4..910ed0ddb2f 100644 --- a/lib/api/src/function_env.rs +++ b/lib/api/src/rt/sys/entities/function/env.rs @@ -1,8 +1,11 @@ use std::{any::Any, fmt::Debug, marker::PhantomData}; -use crate::vm::VMFunctionEnvironment; +use crate::{ + store::{AsStoreMut, AsStoreRef, StoreRef}, + StoreMut, +}; -use crate::store::{AsStoreMut, AsStoreRef, StoreHandle, StoreMut, StoreObjects, StoreRef}; +use wasmer_vm::{StoreHandle, StoreObject, StoreObjects, VMFunctionEnvironment}; #[derive(Debug)] #[repr(transparent)] @@ -21,7 +24,7 @@ impl FunctionEnv { { Self { handle: StoreHandle::new( - store.as_store_mut().objects_mut(), + store.as_store_mut().objects_mut().as_sys_mut(), VMFunctionEnvironment::new(value), ), marker: PhantomData, @@ -34,7 +37,7 @@ impl FunctionEnv { T: Any + Send + 'static + Sized, { self.handle - .get(store.as_store_ref().objects()) + .get(store.as_store_ref().objects().as_sys()) .as_ref() .downcast_ref::() .unwrap() @@ -54,7 +57,7 @@ impl FunctionEnv { T: Any + Send + 'static + Sized, { self.handle - .get_mut(store.objects_mut()) + .get_mut(store.objects_mut().as_sys_mut()) .as_mut() .downcast_mut::() .unwrap() @@ -72,6 +75,32 @@ impl FunctionEnv { } } +impl crate::FunctionEnv { + /// Consume self into [`crate::rt::sys::function::FunctionEnv`]. + pub fn into_sys(self) -> FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::Sys(s) => s, + _ => panic!("Not a `sys` function env!"), + } + } + + /// Convert a reference to self into a reference to [`crate::rt::sys::function::FunctionEnv`]. + pub fn as_sys(&self) -> &FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::Sys(ref s) => s, + _ => panic!("Not a `sys` function env!"), + } + } + + /// Convert a mutable reference to self into a mutable reference [`crate::rt::sys::function::FunctionEnv`]. + pub fn as_sys_mut(&mut self) -> &mut FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::Sys(ref mut s) => s, + _ => panic!("Not a `sys` function env!"), + } + } +} + impl PartialEq for FunctionEnv { fn eq(&self, other: &Self) -> bool { self.handle == other.handle @@ -161,8 +190,20 @@ impl AsStoreMut for FunctionEnvMut<'_, T> { inner: self.store_mut.inner, } } - #[inline] - fn objects_mut(&mut self) -> &mut StoreObjects { - &mut self.store_mut.inner.objects + + fn objects_mut(&mut self) -> &mut crate::StoreObjects { + self.store_mut.objects_mut() + } +} + +impl<'a, T> From> for crate::FunctionEnvMut<'a, T> { + fn from(value: FunctionEnvMut<'a, T>) -> Self { + crate::FunctionEnvMut(crate::RuntimeFunctionEnvMut::Sys(value)) + } +} + +impl From> for crate::FunctionEnv { + fn from(value: FunctionEnv) -> Self { + Self(crate::RuntimeFunctionEnv::Sys(value)) } } diff --git a/lib/api/src/sys/externals/function.rs b/lib/api/src/rt/sys/entities/function/mod.rs similarity index 66% rename from lib/api/src/sys/externals/function.rs rename to lib/api/src/rt/sys/entities/function/mod.rs index 5ac97294c17..17624c828a2 100644 --- a/lib/api/src/sys/externals/function.rs +++ b/lib/api/src/rt/sys/entities/function/mod.rs @@ -1,20 +1,28 @@ -use crate::externals::function::{HostFunction, WithEnv, WithoutEnv}; -use crate::native_type::{FromToNativeWasmType, IntoResult, NativeWasmTypeInto, WasmTypeList}; -use crate::store::{AsStoreMut, AsStoreRef, StoreInner, StoreMut}; -use crate::sys::engine::NativeEngineExt; -use crate::vm::{VMExternFunction, VMFunctionCallback}; -use crate::{FunctionEnv, FunctionEnvMut, FunctionType, RuntimeError, Value}; +//! Data types, functions and traits for `sys` runtime's `Function` implementation. + +pub(crate) mod env; +pub(crate) mod typed; + +use crate::{ + entities::store::{AsStoreMut, AsStoreRef, StoreMut}, + rt::sys::{engine::NativeEngineExt, vm::VMFunctionCallback}, + utils::{FromToNativeWasmType, IntoResult, NativeWasmTypeInto, WasmTypeList}, + vm::{VMExtern, VMExternFunction}, + FunctionEnv, FunctionEnvMut, FunctionType, HostFunction, RuntimeError, RuntimeFunction, + StoreInner, Value, WithEnv, WithoutEnv, +}; use std::panic::{self, AssertUnwindSafe}; use std::{cell::UnsafeCell, cmp::max, ffi::c_void}; use wasmer_types::{NativeWasmType, RawValue}; use wasmer_vm::{ on_host_stack, raise_user_trap, resume_panic, wasmer_call_trampoline, MaybeInstanceOwned, - StoreHandle, VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMExtern, VMFuncRef, - VMFunction, VMFunctionContext, VMFunctionKind, VMTrampoline, + StoreHandle, VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMFuncRef, + VMFunction, VMFunctionBody, VMFunctionContext, VMFunctionKind, VMTrampoline, }; #[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))] #[derive(Debug, Clone, PartialEq, Eq)] +/// A WebAssembly `function` instance, in the `sys` runtime. pub struct Function { pub(crate) handle: StoreHandle, } @@ -26,7 +34,7 @@ impl From> for Function { } impl Function { - pub fn new_with_env( + pub(crate) fn new_with_env( store: &mut impl AsStoreMut, env: &FunctionEnv, ty: FT, @@ -41,7 +49,7 @@ impl Function { { let function_type = ty.into(); let func_ty = function_type.clone(); - let func_env = env.clone(); + let func_env = env.clone().into_sys(); let raw_store = store.as_store_mut().as_raw() as *mut u8; let wrapper = move |values_vec: *mut RawValue| -> Result<(), RuntimeError> { unsafe { @@ -56,10 +64,11 @@ impl Function { )); } let store_mut = StoreMut::from_raw(raw_store as *mut StoreInner); - let env = FunctionEnvMut { + let env = env::FunctionEnvMut { store_mut, func_env: func_env.clone(), - }; + } + .into(); let returns = func(env, &args)?; // We need to dynamically check that the returns @@ -91,7 +100,7 @@ impl Function { let type_index = store .as_store_mut() .engine() - .0 + .as_sys() .register_signature(&function_type); let vmctx = VMFunctionContext { host_env: host_data.as_ref() as *const _ as *mut c_void, @@ -111,19 +120,19 @@ impl Function { host_data, }; Self { - handle: StoreHandle::new(store.as_store_mut().objects_mut(), vm_function), + handle: StoreHandle::new(store.as_store_mut().objects_mut().as_sys_mut(), vm_function), } } /// Creates a new host `Function` from a native function. - pub fn new_typed(store: &mut impl AsStoreMut, func: F) -> Self + pub(crate) fn new_typed(store: &mut impl AsStoreMut, func: F) -> Self where F: HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync, Args: WasmTypeList, Rets: WasmTypeList, { let env = FunctionEnv::new(store, ()); - let func_ptr = func.function_callback(); + let func_ptr = func.function_callback(crate::Runtime::Sys).into_sys(); let host_data = Box::new(StaticFunction { raw_store: store.as_store_mut().as_raw() as *mut u8, env, @@ -134,13 +143,16 @@ impl Function { let type_index = store .as_store_mut() .engine() - .0 + .as_sys() .register_signature(&function_type); let vmctx = VMFunctionContext { host_env: host_data.as_ref() as *const _ as *mut c_void, }; let call_trampoline = - >::call_trampoline_address(); + >::call_trampoline_address( + crate::Runtime::Sys, + ) + .into_sys(); let anyfunc = VMCallerCheckedAnyfunc { func_ptr, type_index, @@ -155,11 +167,11 @@ impl Function { host_data, }; Self { - handle: StoreHandle::new(store.as_store_mut().objects_mut(), vm_function), + handle: StoreHandle::new(store.as_store_mut().objects_mut().as_sys_mut(), vm_function), } } - pub fn new_typed_with_env( + pub(crate) fn new_typed_with_env( store: &mut impl AsStoreMut, env: &FunctionEnv, func: F, @@ -169,10 +181,10 @@ impl Function { Args: WasmTypeList, Rets: WasmTypeList, { - let func_ptr = func.function_callback(); + let func_ptr = func.function_callback(crate::Runtime::Sys).into_sys(); let host_data = Box::new(StaticFunction { raw_store: store.as_store_mut().as_raw() as *mut u8, - env: env.clone(), + env: env.as_sys().clone().into(), func, }); let function_type = FunctionType::new(Args::wasm_types(), Rets::wasm_types()); @@ -180,13 +192,15 @@ impl Function { let type_index = store .as_store_mut() .engine() - .0 + .as_sys() .register_signature(&function_type); let vmctx = VMFunctionContext { host_env: host_data.as_ref() as *const _ as *mut c_void, }; - let call_trampoline = - >::call_trampoline_address(); + let call_trampoline = >::call_trampoline_address( + crate::Runtime::Sys, + ) + .into_sys(); let anyfunc = VMCallerCheckedAnyfunc { func_ptr, type_index, @@ -201,13 +215,13 @@ impl Function { host_data, }; Self { - handle: StoreHandle::new(store.as_store_mut().objects_mut(), vm_function), + handle: StoreHandle::new(store.as_store_mut().objects_mut().as_sys_mut(), vm_function), } } - pub fn ty(&self, store: &impl AsStoreRef) -> FunctionType { + pub(crate) fn ty(&self, store: &impl AsStoreRef) -> FunctionType { self.handle - .get(store.as_store_ref().objects()) + .get(store.as_store_ref().objects().as_sys()) .signature .clone() } @@ -279,7 +293,7 @@ impl Function { // TODO: This loop is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 loop { let storeref = store.as_store_ref(); - let vm_function = self.handle.get(storeref.objects()); + let vm_function = self.handle.get(storeref.objects().as_sys()); let config = storeref.engine().tunables().vmconfig(); r = unsafe { wasmer_call_trampoline( @@ -325,18 +339,18 @@ impl Function { Ok(()) } - pub fn result_arity(&self, store: &impl AsStoreRef) -> usize { + pub(crate) fn result_arity(&self, store: &impl AsStoreRef) -> usize { self.ty(store).results().len() } - pub fn call( + pub(crate) fn call( &self, store: &mut impl AsStoreMut, params: &[Value], ) -> Result, RuntimeError> { let trampoline = unsafe { self.handle - .get(store.as_store_ref().objects()) + .get(store.as_store_ref().objects().as_sys()) .anyfunc .as_ptr() .as_ref() @@ -349,14 +363,14 @@ impl Function { #[doc(hidden)] #[allow(missing_docs)] - pub fn call_raw( + pub(crate) fn call_raw( &self, store: &mut impl AsStoreMut, params: Vec, ) -> Result, RuntimeError> { let trampoline = unsafe { self.handle - .get(store.as_store_ref().objects()) + .get(store.as_store_ref().objects().as_sys()) .anyfunc .as_ptr() .as_ref() @@ -368,7 +382,7 @@ impl Function { } pub(crate) fn vm_funcref(&self, store: &impl AsStoreRef) -> VMFuncRef { - let vm_function = self.handle.get(store.as_store_ref().objects()); + let vm_function = self.handle.get(store.as_store_ref().objects().as_sys()); if vm_function.kind == VMFunctionKind::Dynamic { panic!("dynamic functions cannot be used in tables or as funcrefs"); } @@ -379,7 +393,7 @@ impl Function { let signature = store .as_store_ref() .engine() - .0 + .as_sys() .lookup_signature(funcref.0.as_ref().type_index) .expect("Signature not found in store"); let vm_function = VMFunction { @@ -391,25 +405,28 @@ impl Function { host_data: Box::new(()), }; Self { - handle: StoreHandle::new(store.as_store_mut().objects_mut(), vm_function), + handle: StoreHandle::new(store.objects_mut().as_sys_mut(), vm_function), } } pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternFunction) -> Self { Self { handle: unsafe { - StoreHandle::from_internal(store.as_store_ref().objects().id(), vm_extern) + StoreHandle::from_internal( + store.as_store_ref().objects().id(), + vm_extern.into_sys(), + ) }, } } /// Checks whether this `Function` can be used with the given store. - pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + pub(crate) fn is_from_store(&self, store: &impl AsStoreRef) -> bool { self.handle.store_id() == store.as_store_ref().objects().id() } pub(crate) fn to_vm_extern(&self) -> VMExtern { - VMExtern::Function(self.handle.internal_handle()) + VMExtern::Sys(wasmer_vm::VMExtern::Function(self.handle.internal_handle())) } } @@ -467,174 +484,163 @@ pub(crate) struct StaticFunction { pub(crate) func: F, } +impl crate::Function { + /// Consume [`self`] into [`crate::rt::sys::function::Function`]. + pub fn into_sys(self) -> crate::rt::sys::function::Function { + match self.0 { + RuntimeFunction::Sys(s) => s, + _ => panic!("Not a `sys` function!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::sys::function::Function`]. + pub fn as_sys(&self) -> &crate::rt::sys::function::Function { + match self.0 { + RuntimeFunction::Sys(ref s) => s, + _ => panic!("Not a `sys` function!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::sys::function::Function`]. + pub fn as_sys_mut(&mut self) -> &mut crate::rt::sys::function::Function { + match self.0 { + RuntimeFunction::Sys(ref mut s) => s, + _ => panic!("Not a `sys` function!"), + } + } +} + macro_rules! impl_host_function { - ( [$c_struct_representation:ident] - $c_struct_name:ident, - $( $x:ident ),* ) => { - - // Implement `HostFunction` for a function with a [`FunctionEnvMut`] that has the same - // arity than the tuple. - #[allow(unused_parens)] - impl< $( $x, )* Rets, RetsAsResult, T: Send + 'static, Func > - HostFunction - for - Func + ([$c_struct_representation:ident] $c_struct_name:ident, $( $x:ident ),* ) => { + paste::paste! { + #[allow(non_snake_case)] + pub(crate) fn [] + <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, Func: Fn($( $x , )*) -> RetsAsResult + 'static> + (this: &Func) -> crate::rt::sys::vm::VMFunctionCallback { + /// This is a function that wraps the real host + /// function. Its address will be used inside the + /// runtime. + unsafe extern "C" fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func>( env: &StaticFunction, $( $x: <$x::Native as NativeWasmType>::Abi, )* ) -> Rets::CStruct where $( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, - Func: Fn(FunctionEnvMut, $( $x , )*) -> RetsAsResult + 'static, + Func: Fn($( $x , )*) -> RetsAsResult + 'static, { - #[allow(non_snake_case)] - fn function_callback(&self) -> VMFunctionCallback { - /// This is a function that wraps the real host - /// function. Its address will be used inside the - /// runtime. - unsafe extern "C" fn func_wrapper( env: &StaticFunction, $( $x: <$x::Native as NativeWasmType>::Abi, )* ) -> Rets::CStruct - where - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - RetsAsResult: IntoResult, - Func: Fn(FunctionEnvMut, $( $x , )*) -> RetsAsResult + 'static, - { - // println!("func wrapper"); - let mut store = StoreMut::from_raw(env.raw_store as *mut _); - let result = on_host_stack(|| { - // println!("func wrapper1"); - panic::catch_unwind(AssertUnwindSafe(|| { - $( - let $x = FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut store, $x)); - )* - // println!("func wrapper2 {:p}", *env.raw_env); - let store_mut = StoreMut::from_raw(env.raw_store as *mut _); - let f_env = FunctionEnvMut { - store_mut, - func_env: env.env.clone(), - }; - // println!("func wrapper3"); - (env.func)(f_env, $($x),* ).into_result() - })) - }); - - match result { - Ok(Ok(result)) => return result.into_c_struct(&mut store), - Ok(Err(trap)) => raise_user_trap(Box::new(trap)), - Err(panic) => resume_panic(panic) , - } - } - - func_wrapper::< T, $( $x, )* Rets, RetsAsResult, Self > as VMFunctionCallback + // println!("func wrapper"); + let mut store = StoreMut::from_raw(env.raw_store as *mut _); + let result = on_host_stack(|| { + // println!("func wrapper1"); + panic::catch_unwind(AssertUnwindSafe(|| { + $( + let $x = FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut store, $x)); + )* + (env.func)($($x),* ).into_result() + })) + }); + + match result { + Ok(Ok(result)) => return result.into_c_struct(&mut store), + Ok(Err(trap)) => raise_user_trap(Box::new(trap)), + Err(panic) => resume_panic(panic) , } + } - #[allow(non_snake_case)] - fn call_trampoline_address() -> VMTrampoline { - unsafe extern "C" fn call_trampoline< - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - >( - vmctx: *mut VMContext, - body: VMFunctionCallback, - args: *mut RawValue, - ) { - let body: unsafe extern "C" fn( - vmctx: *mut VMContext, - $( $x: <$x::Native as NativeWasmType>::Abi, )* - ) -> Rets::CStruct - = std::mem::transmute(body); - - let mut _n = 0; - $( - let $x = *args.add(_n).cast(); - _n += 1; - )* - - let results = body(vmctx, $( $x ),*); - Rets::write_c_struct_to_ptr(results, args); - } + func_wrapper::< $( $x, )* Rets, RetsAsResult, Func > as _ - call_trampoline::<$( $x, )* Rets> - } + } + #[allow(non_snake_case)] + pub(crate) fn [] + <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList> + () -> crate::rt::sys::vm::VMTrampoline { + + unsafe extern "C" fn call_trampoline<$( $x: FromToNativeWasmType, )* Rets: WasmTypeList> + ( + vmctx: *mut crate::rt::sys::vm::VMContext, + body: crate::rt::sys::vm::VMFunctionCallback, + args: *mut RawValue, + ) { + let body: unsafe extern "C" fn(vmctx: *mut crate::rt::sys::vm::VMContext, $( $x: <$x::Native as NativeWasmType>::Abi, )*) -> Rets::CStruct = std::mem::transmute(body); + let mut _n = 0; + $( + let $x = *args.add(_n).cast(); + _n += 1; + )* + + let results = body(vmctx, $( $x ),*); + Rets::write_c_struct_to_ptr(results, args); } - // Implement `HostFunction` for a function that has the same arity than the tuple. - #[allow(unused_parens)] - impl< $( $x, )* Rets, RetsAsResult, Func > - HostFunction<(), ( $( $x ),* ), Rets, WithoutEnv> - for - Func - where + call_trampoline::<$( $x, )* Rets> as _ + + } + + #[allow(non_snake_case)] + pub(crate) fn [] + <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, T: Send + 'static, Func: Fn(FunctionEnvMut, $( $x , )*) -> RetsAsResult + 'static> + (this: &Func) -> crate::rt::sys::vm::VMFunctionCallback { + /// This is a function that wraps the real host + /// function. Its address will be used inside the + /// runtime. + unsafe extern "C" fn func_wrapper( env: &StaticFunction, $( $x: <$x::Native as NativeWasmType>::Abi, )* ) -> Rets::CStruct + where $( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, - Func: Fn($( $x , )*) -> RetsAsResult + 'static, + Func: Fn(FunctionEnvMut, $( $x , )*) -> RetsAsResult + 'static, { - #[allow(non_snake_case)] - fn function_callback(&self) -> VMFunctionCallback { - /// This is a function that wraps the real host - /// function. Its address will be used inside the - /// runtime. - unsafe extern "C" fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func>( env: &StaticFunction, $( $x: <$x::Native as NativeWasmType>::Abi, )* ) -> Rets::CStruct - where - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - RetsAsResult: IntoResult, - Func: Fn($( $x , )*) -> RetsAsResult + 'static, - { - // println!("func wrapper"); - let mut store = StoreMut::from_raw(env.raw_store as *mut _); - let result = on_host_stack(|| { - // println!("func wrapper1"); - panic::catch_unwind(AssertUnwindSafe(|| { - $( - let $x = FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut store, $x)); - )* - (env.func)($($x),* ).into_result() - })) - }); - - match result { - Ok(Ok(result)) => return result.into_c_struct(&mut store), - Ok(Err(trap)) => raise_user_trap(Box::new(trap)), - Err(panic) => resume_panic(panic) , - } - } - - func_wrapper::< $( $x, )* Rets, RetsAsResult, Self > as VMFunctionCallback - } - #[allow(non_snake_case)] - fn call_trampoline_address() -> VMTrampoline { - unsafe extern "C" fn call_trampoline< - $( $x: FromToNativeWasmType, )* - Rets: WasmTypeList, - >( - vmctx: *mut VMContext, - body: VMFunctionCallback, - args: *mut RawValue, - ) { - let body: unsafe extern "C" fn( - vmctx: *mut VMContext, - $( $x: <$x::Native as NativeWasmType>::Abi, )* - ) -> Rets::CStruct - = std::mem::transmute(body); - - let mut _n = 0; - $( - let $x = *args.add(_n).cast(); - _n += 1; - )* - - let results = body(vmctx, $( $x ),*); - Rets::write_c_struct_to_ptr(results, args); - } - - call_trampoline::<$( $x, )* Rets> - } + let mut store = StoreMut::from_raw(env.raw_store as *mut _); + let result = wasmer_vm::on_host_stack(|| { + panic::catch_unwind(AssertUnwindSafe(|| { + $( + let $x = FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut store, $x)); + )* + let store_mut = StoreMut::from_raw(env.raw_store as *mut _); + let f_env = crate::rt::sys::function::env::FunctionEnvMut { + store_mut, + func_env: env.env.as_sys().clone(), + }.into(); + (env.func)(f_env, $($x),* ).into_result() + })) + }); + + match result { + Ok(Ok(result)) => return result.into_c_struct(&mut store), + Ok(Err(trap)) => wasmer_vm::raise_user_trap(Box::new(trap)), + Err(panic) => wasmer_vm::resume_panic(panic), + } + } + func_wrapper::< T, $( $x, )* Rets, RetsAsResult, Func > as _ + } + #[allow(non_snake_case)] + pub(crate) fn [] + <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList> + () -> crate::rt::sys::vm::VMTrampoline { + + unsafe extern "C" fn call_trampoline<$( $x: FromToNativeWasmType, )* Rets: WasmTypeList>( + vmctx: *mut crate::rt::sys::vm::VMContext, + body: crate::rt::sys::vm::VMFunctionCallback, + args: *mut RawValue, + ) { + let body: unsafe extern "C" fn(vmctx: *mut crate::rt::sys::vm::VMContext, $( $x: <$x::Native as NativeWasmType>::Abi, )*) -> Rets::CStruct = std::mem::transmute(body); + let mut _n = 0; + $( + let $x = *args.add(_n).cast(); + _n += 1; + )* + + let results = body(vmctx, $( $x ),*); + Rets::write_c_struct_to_ptr(results, args); } - }; - } + + call_trampoline::<$( $x, )* Rets> as _ + } + } + }; +} // Here we go! Let's generate all the C struct, `WasmTypeList` // implementations and `HostFunction` implementations. diff --git a/lib/api/src/sys/typed_function.rs b/lib/api/src/rt/sys/entities/function/typed.rs similarity index 94% rename from lib/api/src/sys/typed_function.rs rename to lib/api/src/rt/sys/entities/function/typed.rs index 1ff5d252bd8..cb2d0f7e203 100644 --- a/lib/api/src/sys/typed_function.rs +++ b/lib/api/src/rt/sys/entities/function/typed.rs @@ -1,9 +1,7 @@ -use crate::{FromToNativeWasmType, RuntimeError, TypedFunction, WasmTypeList}; -use wasmer_types::RawValue; - -use crate::native_type::NativeWasmTypeInto; +use crate::rt::sys::engine::NativeEngineExt; use crate::store::{AsStoreMut, AsStoreRef}; -use crate::sys::engine::NativeEngineExt; +use crate::{FromToNativeWasmType, NativeWasmTypeInto, RuntimeError, TypedFunction, WasmTypeList}; +use wasmer_types::RawValue; macro_rules! impl_native_traits { ( $( $x:ident ),* ) => { @@ -16,11 +14,11 @@ macro_rules! impl_native_traits { /// Call the typed func and return results. #[allow(unused_mut)] #[allow(clippy::too_many_arguments)] - pub fn call(&self, store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result { + pub fn call_sys(&self, store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result { let anyfunc = unsafe { - *self.func.0 + *self.func.as_sys() .handle - .get(store.as_store_ref().objects()) + .get(store.as_store_ref().objects().as_sys()) .anyfunc .as_ptr() .as_ref() @@ -105,11 +103,11 @@ macro_rules! impl_native_traits { #[allow(missing_docs)] #[allow(unused_mut)] #[allow(clippy::too_many_arguments)] - pub fn call_raw(&self, store: &mut impl AsStoreMut, mut params_list: Vec ) -> Result { + pub fn call_raw_sys(&self, store: &mut impl AsStoreMut, mut params_list: Vec ) -> Result { let anyfunc = unsafe { - *self.func.0 + *self.func.as_sys() .handle - .get(store.as_store_ref().objects()) + .get(store.as_store_ref().objects().as_sys()) .anyfunc .as_ptr() .as_ref() diff --git a/lib/api/src/sys/externals/global.rs b/lib/api/src/rt/sys/entities/global.rs similarity index 61% rename from lib/api/src/sys/externals/global.rs rename to lib/api/src/rt/sys/entities/global.rs index 8e159c7c36d..3548bc3a6a5 100644 --- a/lib/api/src/sys/externals/global.rs +++ b/lib/api/src/rt/sys/entities/global.rs @@ -1,13 +1,17 @@ -use crate::errors::RuntimeError; -use crate::store::{AsStoreMut, AsStoreRef}; -use crate::value::Value; -use crate::vm::VMExternGlobal; -use crate::GlobalType; -use crate::Mutability; -use wasmer_vm::{StoreHandle, VMExtern, VMGlobal}; +//! Data types, functions and traits for `sys` runtime's `Global` implementation. + +use crate::{ + error::RuntimeError, + store::{AsStoreMut, AsStoreRef}, + value::Value, + vm::{VMExtern, VMExternGlobal}, +}; +use wasmer_types::{GlobalType, Mutability}; +use wasmer_vm::{StoreHandle, VMGlobal}; #[derive(Debug, Clone)] #[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))] +/// A WebAssembly `global` in the `sys` runtime. pub struct Global { handle: StoreHandle, } @@ -31,28 +35,35 @@ impl Global { } Ok(Self { - handle: StoreHandle::new(store.objects_mut(), global), + handle: StoreHandle::new(store.objects_mut().as_sys_mut(), global), }) } - pub fn ty(&self, store: &impl AsStoreRef) -> GlobalType { - *self.handle.get(store.as_store_ref().objects()).ty() + pub(crate) fn ty(&self, store: &impl AsStoreRef) -> GlobalType { + *self + .handle + .get(store.as_store_ref().objects().as_sys()) + .ty() } - pub fn get(&self, store: &mut impl AsStoreMut) -> Value { + pub(crate) fn get(&self, store: &mut impl AsStoreMut) -> Value { unsafe { let raw = self .handle - .get(store.as_store_ref().objects()) + .get(store.as_store_ref().objects().as_sys()) .vmglobal() .as_ref() .val; - let ty = self.handle.get(store.as_store_ref().objects()).ty().ty; + let ty = self + .handle + .get(store.as_store_ref().objects().as_sys()) + .ty() + .ty; Value::from_raw(store, ty, raw) } } - pub fn set(&self, store: &mut impl AsStoreMut, val: Value) -> Result<(), RuntimeError> { + pub(crate) fn set(&self, store: &mut impl AsStoreMut, val: Value) -> Result<(), RuntimeError> { if !val.is_from_store(store) { return Err(RuntimeError::new("cross-`Store` values are not supported")); } @@ -68,7 +79,7 @@ impl Global { } unsafe { self.handle - .get_mut(store.objects_mut()) + .get_mut(store.as_store_mut().objects_mut().as_sys_mut()) .vmglobal() .as_mut() .val = val.as_raw(store); @@ -79,17 +90,20 @@ impl Global { pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternGlobal) -> Self { Self { handle: unsafe { - StoreHandle::from_internal(store.as_store_ref().objects().id(), vm_extern) + StoreHandle::from_internal( + store.as_store_ref().objects().id(), + vm_extern.into_sys(), + ) }, } } - pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + pub(crate) fn is_from_store(&self, store: &impl AsStoreRef) -> bool { self.handle.store_id() == store.as_store_ref().objects().id() } pub(crate) fn to_vm_extern(&self) -> VMExtern { - VMExtern::Global(self.handle.internal_handle()) + VMExtern::Sys(wasmer_vm::VMExtern::Global(self.handle.internal_handle())) } } diff --git a/lib/api/src/sys/instance.rs b/lib/api/src/rt/sys/entities/instance.rs similarity index 62% rename from lib/api/src/sys/instance.rs rename to lib/api/src/rt/sys/entities/instance.rs index bbeab94b791..5cb36809a55 100644 --- a/lib/api/src/sys/instance.rs +++ b/lib/api/src/rt/sys/entities/instance.rs @@ -1,13 +1,15 @@ -use crate::errors::InstantiationError; -use crate::exports::Exports; -use crate::module::Module; +//! Data types, functions and traits for `sys` runtime's `Instance` implementation. + +use crate::{ + error::InstantiationError, exports::Exports, imports::Imports, module::Module, + store::AsStoreMut, Extern, +}; use wasmer_vm::{StoreHandle, VMInstance}; -use crate::imports::Imports; -use crate::store::AsStoreMut; -use crate::Extern; +use super::store::Store; #[derive(Clone, PartialEq, Eq)] +/// A WebAssembly `instance` in the `sys` runtime. pub struct Instance { _handle: StoreHandle, } @@ -48,11 +50,11 @@ impl Instance { let externs = imports .imports_for_module(module) .map_err(InstantiationError::Link)?; - let mut handle = module.0.instantiate(store, &externs)?; - let exports = Self::get_exports(store, module, &mut handle); + let mut handle = module.as_sys().instantiate(store, &externs)?; + let exports = Self::get_exports(store, module, handle.as_sys_mut()); let instance = Self { - _handle: StoreHandle::new(store.objects_mut(), handle), + _handle: StoreHandle::new(store.objects_mut().as_sys_mut(), handle.into_sys()), }; Ok((instance, exports)) @@ -65,10 +67,13 @@ impl Instance { externs: &[Extern], ) -> Result<(Self, Exports), InstantiationError> { let externs = externs.to_vec(); - let mut handle = module.0.instantiate(store, &externs)?; - let exports = Self::get_exports(store, module, &mut handle); + let mut handle = module.as_sys().instantiate(store, &externs)?; + let exports = Self::get_exports(store, module, handle.as_sys_mut()); let instance = Self { - _handle: StoreHandle::new(store.objects_mut(), handle), + _handle: StoreHandle::new( + store.as_store_mut().objects_mut().as_sys_mut(), + handle.into_sys(), + ), }; Ok((instance, exports)) @@ -84,9 +89,19 @@ impl Instance { .map(|export| { let name = export.name().to_string(); let export = handle.lookup(&name).expect("export"); - let extern_ = Extern::from_vm_extern(store, export); + let extern_ = Extern::from_vm_extern(store, crate::vm::VMExtern::Sys(export)); (name, extern_) }) .collect::() } } + +impl crate::RuntimeInstance { + /// Consume [`self`] into a [`crate::rt::sys::instance::Instance`]. + pub(crate) fn into_sys(self) -> crate::rt::sys::instance::Instance { + match self { + Self::Sys(s) => s, + _ => panic!("Not a `sys` instance"), + } + } +} diff --git a/lib/api/src/sys/externals/memory.rs b/lib/api/src/rt/sys/entities/memory/mod.rs similarity index 70% rename from lib/api/src/sys/externals/memory.rs rename to lib/api/src/rt/sys/entities/memory/mod.rs index a3b4f38953f..b2fa604a705 100644 --- a/lib/api/src/sys/externals/memory.rs +++ b/lib/api/src/rt/sys/entities/memory/mod.rs @@ -1,3 +1,4 @@ +//! Data types, functions and traits for `sys` runtime's `Memory` implementation. use std::{ convert::TryInto, marker::PhantomData, @@ -6,46 +7,53 @@ use std::{ }; use tracing::warn; -use wasmer_types::Pages; -use wasmer_vm::{ - LinearMemory, MemoryError, StoreHandle, ThreadConditionsHandle, VMExtern, VMMemory, -}; +use wasmer_types::{MemoryType, Pages}; +use wasmer_vm::{LinearMemory, MemoryError, StoreHandle, ThreadConditionsHandle, VMMemory}; use crate::{ - store::{AsStoreMut, AsStoreRef}, - sys::{engine::NativeEngineExt, externals::memory_view::MemoryView}, - vm::VMExternMemory, - MemoryAccessError, MemoryType, + entities::store::{AsStoreMut, AsStoreRef}, + location::{MemoryLocation, SharedMemoryOps}, + rt::sys::entities::{engine::NativeEngineExt, memory::MemoryView}, + vm::{VMExtern, VMExternMemory}, + MemoryAccessError, RuntimeMemory, }; +pub(crate) mod view; +pub use view::*; + +use super::store::Store; + #[derive(Debug, Clone)] #[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))] +/// A WebAssembly `memory` in the `sys` runtime. pub struct Memory { pub(crate) handle: StoreHandle, } impl Memory { - pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result { + pub(crate) fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result { let mut store = store.as_store_mut(); let tunables = store.engine().tunables(); let style = tunables.memory_style(&ty); let memory = tunables.create_host_memory(&ty, &style)?; Ok(Self { - handle: StoreHandle::new(store.objects_mut(), memory), + handle: StoreHandle::new(store.as_store_mut().objects_mut().as_sys_mut(), memory), }) } - pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { - let handle = StoreHandle::new(new_store.objects_mut(), memory); - Self::from_vm_extern(new_store, handle.internal_handle()) + pub(crate) fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { + let handle = StoreHandle::new(new_store.objects_mut().as_sys_mut(), memory); + Self::from_vm_extern(new_store, VMExternMemory::Sys(handle.internal_handle())) } - pub fn ty(&self, store: &impl AsStoreRef) -> MemoryType { - self.handle.get(store.as_store_ref().objects()).ty() + pub(crate) fn ty(&self, store: &impl AsStoreRef) -> MemoryType { + self.handle + .get(store.as_store_ref().objects().as_sys()) + .ty() } - pub fn grow( + pub(crate) fn grow( &self, store: &mut impl AsStoreMut, delta: IntoPages, @@ -53,48 +61,55 @@ impl Memory { where IntoPages: Into, { - self.handle.get_mut(store.objects_mut()).grow(delta.into()) + self.handle + .get_mut(store.objects_mut().as_sys_mut()) + .grow(delta.into()) } - pub fn grow_at_least( + pub(crate) fn grow_at_least( &self, store: &mut impl AsStoreMut, min_size: u64, ) -> Result<(), MemoryError> { self.handle - .get_mut(store.objects_mut()) + .get_mut(store.objects_mut().as_sys_mut()) .grow_at_least(min_size) } - pub fn reset(&self, store: &mut impl AsStoreMut) -> Result<(), MemoryError> { - self.handle.get_mut(store.objects_mut()).reset()?; + pub(crate) fn reset(&self, store: &mut impl AsStoreMut) -> Result<(), MemoryError> { + self.handle + .get_mut(store.as_store_mut().objects_mut().as_sys_mut()) + .reset()?; Ok(()) } pub(crate) fn from_vm_extern(store: &impl AsStoreRef, vm_extern: VMExternMemory) -> Self { Self { handle: unsafe { - StoreHandle::from_internal(store.as_store_ref().objects().id(), vm_extern) + StoreHandle::from_internal( + store.as_store_ref().objects().id(), + vm_extern.into_sys(), + ) }, } } /// Checks whether this `Memory` can be used with the given context. - pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + pub(crate) fn is_from_store(&self, store: &impl AsStoreRef) -> bool { self.handle.store_id() == store.as_store_ref().objects().id() } /// Cloning memory will create another reference to the same memory that /// can be put into a new store - pub fn try_clone(&self, store: &impl AsStoreRef) -> Result { - let mem = self.handle.get(store.as_store_ref().objects()); + pub(crate) fn try_clone(&self, store: &impl AsStoreRef) -> Result { + let mem = self.handle.get(store.as_store_ref().objects().as_sys()); let cloned = mem.try_clone()?; Ok(cloned.into()) } /// Copying the memory will actually copy all the bytes in the memory to /// a identical byte copy of the original that can be put into a new store - pub fn try_copy( + pub(crate) fn try_copy( &self, store: &impl AsStoreRef, ) -> Result, MemoryError> { @@ -102,25 +117,27 @@ impl Memory { mem.copy() } - pub fn as_shared(&self, store: &impl AsStoreRef) -> Option { - let mem = self.handle.get(store.as_store_ref().objects()); + pub(crate) fn as_shared( + &self, + store: &impl AsStoreRef, + ) -> Option { + let mem = self.handle.get(store.as_store_ref().objects().as_sys()); let conds = mem.thread_conditions()?.downgrade(); - Some(crate::SharedMemory::new(crate::Memory(self.clone()), conds)) + Some(crate::memory::shared::SharedMemory::new( + crate::Memory(RuntimeMemory::Sys(self.clone())), + conds, + )) } /// To `VMExtern`. pub(crate) fn to_vm_extern(&self) -> VMExtern { - VMExtern::Memory(self.handle.internal_handle()) + VMExtern::Sys(wasmer_vm::VMExtern::Memory(self.handle.internal_handle())) } } -impl crate::externals::memory::SharedMemoryOps for ThreadConditionsHandle { - fn notify( - &self, - dst: crate::externals::memory::MemoryLocation, - count: u32, - ) -> Result { +impl SharedMemoryOps for ThreadConditionsHandle { + fn notify(&self, dst: MemoryLocation, count: u32) -> Result { let count = self .upgrade() .ok_or(crate::AtomicsError::Unimplemented)? @@ -135,7 +152,7 @@ impl crate::externals::memory::SharedMemoryOps for ThreadConditionsHandle { fn wait( &self, - dst: crate::externals::memory::MemoryLocation, + dst: MemoryLocation, timeout: Option, ) -> Result { self.upgrade() @@ -309,3 +326,29 @@ unsafe fn volatile_memcpy_write(mut src: *const u8, mut dst: *mut u8, mut len: u copy_one::(&mut src, &mut dst, &mut len); } } + +impl crate::Memory { + /// Consume [`self`] into a [`crate::rt::sys::memory::Memory`]. + pub fn into_sys(self) -> crate::rt::sys::memory::Memory { + match self.0 { + RuntimeMemory::Sys(s) => s, + _ => panic!("Not a `sys` memory!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::sys::memory::Memory`]. + pub fn as_sys(&self) -> &crate::rt::sys::memory::Memory { + match self.0 { + RuntimeMemory::Sys(ref s) => s, + _ => panic!("Not a `sys` memory!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference to [`crate::rt::sys::memory::Memory`]. + pub fn as_sys_mut(&mut self) -> &mut crate::rt::sys::memory::Memory { + match self.0 { + RuntimeMemory::Sys(ref mut s) => s, + _ => panic!("Not a `sys` memory!"), + } + } +} diff --git a/lib/api/src/sys/externals/memory_view.rs b/lib/api/src/rt/sys/entities/memory/view.rs similarity index 95% rename from lib/api/src/sys/externals/memory_view.rs rename to lib/api/src/rt/sys/entities/memory/view.rs index a1f37635458..9fb4e04c77f 100644 --- a/lib/api/src/sys/externals/memory_view.rs +++ b/lib/api/src/rt/sys/entities/memory/view.rs @@ -7,7 +7,7 @@ use std::{convert::TryInto, ops::Range}; use wasmer_types::Pages; use wasmer_vm::LinearMemory; -use super::memory::{Memory, MemoryBuffer}; +use super::{Memory, MemoryBuffer}; /// A WebAssembly `memory` view. /// @@ -23,9 +23,15 @@ pub struct MemoryView<'a> { impl<'a> MemoryView<'a> { pub(crate) fn new(memory: &Memory, store: &'a (impl AsStoreRef + ?Sized)) -> Self { - let size = memory.handle.get(store.as_store_ref().objects()).size(); - - let definition = memory.handle.get(store.as_store_ref().objects()).vmmemory(); + let size = memory + .handle + .get(store.as_store_ref().objects().as_sys()) + .size(); + + let definition = memory + .handle + .get(store.as_store_ref().objects().as_sys()) + .vmmemory(); let def = unsafe { definition.as_ref() }; Self { diff --git a/lib/api/src/rt/sys/entities/mod.rs b/lib/api/src/rt/sys/entities/mod.rs new file mode 100644 index 00000000000..6dca177481b --- /dev/null +++ b/lib/api/src/rt/sys/entities/mod.rs @@ -0,0 +1,9 @@ +pub mod engine; +pub mod external; +pub mod function; +pub mod global; +pub mod instance; +pub mod memory; +pub mod module; +pub mod store; +pub mod table; diff --git a/lib/api/src/sys/module.rs b/lib/api/src/rt/sys/entities/module.rs similarity index 72% rename from lib/api/src/sys/module.rs rename to lib/api/src/rt/sys/entities/module.rs index c4030c950c2..6060c543981 100644 --- a/lib/api/src/sys/module.rs +++ b/lib/api/src/rt/sys/entities/module.rs @@ -1,20 +1,22 @@ +//! Data types, functions and traits for `sys` runtime's `Module` implementation. use std::path::Path; use std::sync::Arc; use bytes::Bytes; -use wasmer_compiler::{Artifact, ArtifactCreate}; +use wasmer_compiler::{Artifact, ArtifactCreate, Engine}; use wasmer_types::{ - CompileError, DeserializeError, ExportsIterator, ImportsIterator, ModuleInfo, SerializeError, + CompileError, DeserializeError, ExportType, ExportsIterator, ImportType, ImportsIterator, + ModuleInfo, SerializeError, }; -use wasmer_types::{ExportType, ImportType}; use crate::{ - engine::AsEngineRef, sys::engine::NativeEngineExt, vm::VMInstance, AsStoreMut, AsStoreRef, - InstantiationError, IntoBytes, + engine::AsEngineRef, error::InstantiationError, rt::sys::entities::engine::NativeEngineExt, + vm::VMInstance, AsStoreMut, AsStoreRef, IntoBytes, RuntimeModule, }; #[derive(Clone, PartialEq, Eq)] #[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))] +/// A WebAssembly `module` in the `sys` runtime. pub struct Module { // The field ordering here is actually significant because of the drop // order: we want to drop the artifact before dropping the engine. @@ -52,12 +54,12 @@ impl Module { #[tracing::instrument(level = "debug", skip_all)] pub(crate) fn validate(engine: &impl AsEngineRef, binary: &[u8]) -> Result<(), CompileError> { - engine.as_engine_ref().engine().0.validate(binary) + engine.as_engine_ref().engine().as_sys().validate(binary) } #[cfg(feature = "compiler")] fn compile(engine: &impl AsEngineRef, binary: &[u8]) -> Result { - let artifact = engine.as_engine_ref().engine().0.compile(binary)?; + let artifact = engine.as_engine_ref().engine().as_sys().compile(binary)?; Ok(Self::from_artifact(artifact)) } @@ -73,7 +75,7 @@ impl Module { } #[tracing::instrument(level = "debug", skip_all)] - pub unsafe fn deserialize_unchecked( + pub(crate) unsafe fn deserialize_unchecked( engine: &impl AsEngineRef, bytes: impl IntoBytes, ) -> Result { @@ -81,13 +83,13 @@ impl Module { let artifact = engine .as_engine_ref() .engine() - .0 + .as_sys() .deserialize_unchecked(bytes.into())?; Ok(Self::from_artifact(artifact)) } #[tracing::instrument(level = "debug", skip_all)] - pub unsafe fn deserialize( + pub(crate) unsafe fn deserialize( engine: &impl AsEngineRef, bytes: impl IntoBytes, ) -> Result { @@ -95,31 +97,31 @@ impl Module { let artifact = engine .as_engine_ref() .engine() - .0 + .as_sys() .deserialize(bytes.into())?; Ok(Self::from_artifact(artifact)) } - pub unsafe fn deserialize_from_file_unchecked( + pub(crate) unsafe fn deserialize_from_file_unchecked( engine: &impl AsEngineRef, path: impl AsRef, ) -> Result { let artifact = engine .as_engine_ref() .engine() - .0 + .as_sys() .deserialize_from_file_unchecked(path.as_ref())?; Ok(Self::from_artifact(artifact)) } - pub unsafe fn deserialize_from_file( + pub(crate) unsafe fn deserialize_from_file( engine: &impl AsEngineRef, path: impl AsRef, ) -> Result { let artifact = engine .as_engine_ref() .engine() - .0 + .as_sys() .deserialize_from_file(path.as_ref())?; Ok(Self::from_artifact(artifact)) } @@ -154,9 +156,9 @@ impl Module { engine.tunables(), &imports .iter() - .map(crate::Extern::to_vm_extern) + .map(|e| crate::Extern::to_vm_extern(e).into_sys()) .collect::>(), - objects, + objects.as_sys_mut(), )?; // After the instance handle is created, we need to initialize @@ -167,7 +169,7 @@ impl Module { self.artifact .finish_instantiation(config, signal_handler, &mut instance_handle)?; - Ok(instance_handle) + Ok(VMInstance::Sys(instance_handle)) } } @@ -181,18 +183,18 @@ impl Module { }) } - pub(crate) fn imports(&self) -> ImportsIterator + '_> { + pub(crate) fn imports(&self) -> ImportsIterator + '_>> { self.info().imports() } - pub(crate) fn exports(&self) -> ExportsIterator + '_> { + pub(crate) fn exports(&self) -> ExportsIterator + '_>> { self.info().exports() } pub(crate) fn custom_sections<'a>( &'a self, name: &'a str, - ) -> impl Iterator> + 'a { + ) -> Box> + 'a> { self.info().custom_sections(name) } @@ -200,3 +202,29 @@ impl Module { self.artifact.module_info() } } + +impl crate::Module { + /// Consume [`self`] into a reference [`crate::rt::sys::module::Module`]. + pub fn into_sys(self) -> crate::rt::sys::module::Module { + match self.0 { + RuntimeModule::Sys(s) => s, + _ => panic!("Not a `sys` module!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::sys::module::Module`]. + pub fn as_sys(&self) -> &crate::rt::sys::module::Module { + match self.0 { + RuntimeModule::Sys(ref s) => s, + _ => panic!("Not a `sys` module!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::sys::module::Module`]. + pub fn as_sys_mut(&mut self) -> &mut crate::rt::sys::module::Module { + match self.0 { + RuntimeModule::Sys(ref mut s) => s, + _ => panic!("Not a `sys` module!"), + } + } +} diff --git a/lib/api/src/rt/sys/entities/store/mod.rs b/lib/api/src/rt/sys/entities/store/mod.rs new file mode 100644 index 00000000000..66b33f37510 --- /dev/null +++ b/lib/api/src/rt/sys/entities/store/mod.rs @@ -0,0 +1,135 @@ +//! Data types, functions and traits for `sys` runtime's `Store` implementation. +use crate::entities::engine::{AsEngineRef, Engine, EngineRef}; +use crate::RuntimeStore; +use wasmer_vm::init_traps; +use wasmer_vm::TrapHandlerFn; +pub use wasmer_vm::{StoreHandle, StoreObjects}; + +mod obj; +pub use obj::*; + +/// A WebAssembly `store` in the `sys` runtime. +pub struct Store { + pub(crate) engine: Engine, + pub(crate) trap_handler: Option>>, +} + +impl std::fmt::Debug for Store { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Store") + .field("engine", &self.engine) + .finish() + } +} + +impl Store { + pub(crate) fn new(engine: Engine) -> Self { + init_traps(); + + Self { + engine, + trap_handler: None, + } + } + + pub(crate) fn engine(&self) -> &Engine { + &self.engine + } + + pub(crate) fn engine_mut(&mut self) -> &mut Engine { + &mut self.engine + } +} + +impl AsEngineRef for Store { + fn as_engine_ref(&self) -> EngineRef<'_> { + EngineRef::new(&self.engine) + } +} + +/// The custom trait to access to all the `sys` functions in the +/// Store. +pub trait NativeStoreExt { + /// Sets the trap handler + fn set_trap_handler(&mut self, handler: Option>>); + /// The signal handler + fn signal_handler(&self) -> Option<*const TrapHandlerFn<'static>>; +} + +impl NativeStoreExt for Store { + fn set_trap_handler(&mut self, handler: Option>>) { + self.trap_handler = handler; + } + + /// The signal handler + #[inline] + fn signal_handler(&self) -> Option<*const TrapHandlerFn<'static>> { + self.trap_handler + .as_ref() + .map(|handler| handler.as_ref() as *const _) + } +} + +impl NativeStoreExt for crate::Store { + fn set_trap_handler(&mut self, handler: Option>>) { + self.inner.store.as_sys_mut().set_trap_handler(handler) + } + + /// The signal handler + #[inline] + fn signal_handler(&self) -> Option<*const TrapHandlerFn<'static>> { + self.inner.store.as_sys().signal_handler() + } +} + +impl crate::RuntimeStore { + /// Consume [`self`] into [`crate::rt::sys::store::Store`]. + pub fn into_sys(self) -> crate::rt::sys::store::Store { + match self { + Self::Sys(s) => s, + _ => panic!("Not a `sys` store!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::sys::store::Store`]. + pub fn as_sys(&self) -> &crate::rt::sys::store::Store { + match self { + Self::Sys(s) => s, + _ => panic!("Not a `sys` store!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::sys::store::Store`]. + pub fn as_sys_mut(&mut self) -> &mut crate::rt::sys::store::Store { + match self { + Self::Sys(s) => s, + _ => panic!("Not a `sys` store!"), + } + } + /// Return true if [`self`] is a store from the `sys` runtime. + pub fn is_sys(&self) -> bool { + matches!(self, Self::Sys(_)) + } +} + +impl crate::Store { + /// Consume [`self`] into [`crate::rt::sys::store::Store`]. + pub(crate) fn into_sys(self) -> crate::rt::sys::store::Store { + self.inner.store.into_sys() + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::sys::store::Store`]. + pub(crate) fn as_sys(&self) -> &crate::rt::sys::store::Store { + self.inner.store.as_sys() + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::sys::store::Store`]. + pub(crate) fn as_sys_mut(&mut self) -> &mut crate::rt::sys::store::Store { + self.inner.store.as_sys_mut() + } + + /// Return true if [`self`] is a store from the `sys` runtime. + pub fn is_sys(&self) -> bool { + self.inner.store.is_sys() + } +} diff --git a/lib/api/src/rt/sys/entities/store/obj.rs b/lib/api/src/rt/sys/entities/store/obj.rs new file mode 100644 index 00000000000..7e8302bb5a4 --- /dev/null +++ b/lib/api/src/rt/sys/entities/store/obj.rs @@ -0,0 +1,76 @@ +use wasmer_vm::StoreId; + +use crate::AsStoreMut; + +impl crate::StoreObjects { + /// Consume store objects into [`crate::rt::sys::store::StoreObjects`]. + pub fn into_sys(self) -> crate::rt::sys::store::StoreObjects { + match self { + Self::Sys(s) => s, + _ => panic!("Not a `sys` store!"), + } + } + + /// Convert a reference to store objects into a reference [`crate::rt::sys::store::StoreObjects`]. + pub fn as_sys(&self) -> &crate::rt::sys::store::StoreObjects { + match self { + Self::Sys(s) => s, + _ => panic!("Not a `sys` store!"), + } + } + + /// Convert a mutable reference to store objects into a mutable reference [`crate::rt::sys::store::StoreObjects`]. + pub fn as_sys_mut(&mut self) -> &mut crate::rt::sys::store::StoreObjects { + match self { + Self::Sys(s) => s, + _ => panic!("Not a `sys` store!"), + } + } +} + +//pub trait GetStoreObjects { +// /// Return a mutable reference to [`wasmer_vm::StoreObjects`] and a reference to the current +// /// engine. +// fn engine_and_objects_mut( +// &mut self, +// ) -> (&crate::Engine, &mut crate::rt::sys::store::StoreObjects); +// +// /// Return a mutable reference to [`wasmer_vm::StoreObjects`]. +// fn objects_mut(&mut self) -> &mut crate::rt::sys::store::StoreObjects; +//} +// +//impl GetStoreObjects for crate::StoreInner { +// fn objects_mut(&mut self) -> &mut crate::rt::sys::store::StoreObjects { +// self.objects.as_sys_mut() +// } +// +// fn engine_and_objects_mut( +// &mut self, +// ) -> (&crate::Engine, &mut crate::rt::sys::store::StoreObjects) { +// match (&mut self.objects, &self.store) { +// (crate::StoreObjects::Sys(o), crate::RuntimeStore::Sys(s)) => (&s.engine, o), +// _ => panic!("Not a `sys` store!"), +// } +// } +//} +// +//impl GetStoreObjects for T { +// fn objects_mut<'a>(&'a mut self) -> &'a mut crate::rt::sys::store::StoreObjects { +// match self.as_store_mut().inner.objects { +// crate::StoreObjects::Sys(ref mut s) => s, +// _ => panic!("Not a `sys` store!"), +// } +// } +// +// fn engine_and_objects_mut( +// &mut self, +// ) -> (&crate::Engine, &mut crate::rt::sys::store::StoreObjects) { +// let mut store = self.as_store_mut(); +// match (&mut store.inner.objects, &store.inner.store) { +// (crate::StoreObjects::Sys(ref mut o), crate::RuntimeStore::Sys(ref s)) => { +// (&s.engine, o) +// } +// _ => panic!("Not a `sys` store!"), +// } +// } +//} diff --git a/lib/api/src/rt/sys/entities/table.rs b/lib/api/src/rt/sys/entities/table.rs new file mode 100644 index 00000000000..10cacab0a4a --- /dev/null +++ b/lib/api/src/rt/sys/entities/table.rs @@ -0,0 +1,241 @@ +//! Data types, functions and traits for `sys` runtime's `Table` implementation. +use crate::{ + entities::store::{AsStoreMut, AsStoreRef}, + error::RuntimeError, + rt::sys::entities::engine::NativeEngineExt, + vm::{VMExtern, VMExternTable}, + ExternRef, Function, RuntimeTable, Value, +}; +use wasmer_types::TableType; +use wasmer_vm::{StoreHandle, TableElement, Trap, VMTable}; + +#[derive(Debug, Clone)] +#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))] +/// A WebAssembly `table` in the `sys` runtime. +pub struct Table { + handle: StoreHandle, +} + +fn set_table_item( + table: &mut VMTable, + item_index: u32, + item: TableElement, +) -> Result<(), RuntimeError> { + table + .set(item_index, item) + .map_err(Into::::into) +} + +fn value_to_table_element( + store: &mut impl AsStoreMut, + val: Value, +) -> Result { + if !val.is_from_store(store) { + return Err(RuntimeError::new("cannot pass Value across contexts")); + } + Ok(match val { + Value::ExternRef(extern_ref) => { + wasmer_vm::TableElement::ExternRef(extern_ref.map(|e| e.vm_externref().into_sys())) + } + Value::FuncRef(func_ref) => { + wasmer_vm::TableElement::FuncRef(func_ref.map(|f| f.vm_funcref(store).into_sys())) + } + _ => return Err(RuntimeError::new("val is not reference")), + }) +} + +fn value_from_table_element(store: &mut impl AsStoreMut, item: wasmer_vm::TableElement) -> Value { + match item { + wasmer_vm::TableElement::FuncRef(funcref) => Value::FuncRef( + funcref + .map(|f| unsafe { Function::from_vm_funcref(store, crate::vm::VMFuncRef::Sys(f)) }), + ), + wasmer_vm::TableElement::ExternRef(extern_ref) => { + Value::ExternRef(extern_ref.map(|e| unsafe { + ExternRef::from_vm_externref(store, crate::vm::VMExternRef::Sys(e)) + })) + } + } +} + +impl Table { + pub(crate) fn new( + mut store: &mut impl AsStoreMut, + ty: TableType, + init: Value, + ) -> Result { + let item = value_to_table_element(&mut store, init)?; + let mut store = store.as_store_mut(); + let tunables = store.engine().tunables(); + let style = tunables.table_style(&ty); + let mut table = tunables + .create_host_table(&ty, &style) + .map_err(RuntimeError::new)?; + + let num_elements = table.size(); + for i in 0..num_elements { + set_table_item(&mut table, i, item.clone())?; + } + + Ok(Self { + handle: StoreHandle::new(store.objects_mut().as_sys_mut(), table), + }) + } + + pub(crate) fn ty(&self, store: &impl AsStoreRef) -> TableType { + *self + .handle + .get(store.as_store_ref().objects().as_sys()) + .ty() + } + + pub(crate) fn get(&self, store: &mut impl AsStoreMut, index: u32) -> Option { + let item = self + .handle + .get(store.as_store_ref().objects().as_sys()) + .get(index)?; + Some(value_from_table_element(store, item)) + } + + pub(crate) fn set( + &self, + store: &mut impl AsStoreMut, + index: u32, + val: Value, + ) -> Result<(), RuntimeError> { + let item = value_to_table_element(store, val)?; + set_table_item( + self.handle.get_mut(store.objects_mut().as_sys_mut()), + index, + item, + ) + } + + pub(crate) fn size(&self, store: &impl AsStoreRef) -> u32 { + self.handle + .get(store.as_store_ref().objects().as_sys()) + .size() + } + + pub(crate) fn grow( + &self, + store: &mut impl AsStoreMut, + delta: u32, + init: Value, + ) -> Result { + let item = value_to_table_element(store, init)?; + let obj_mut = store.objects_mut().as_sys_mut(); + + self.handle + .get_mut(obj_mut) + .grow(delta, item) + .ok_or_else(|| RuntimeError::new(format!("failed to grow table by `{}`", delta))) + } + + pub(crate) fn copy( + store: &mut impl AsStoreMut, + dst_table: &Self, + dst_index: u32, + src_table: &Self, + src_index: u32, + len: u32, + ) -> Result<(), RuntimeError> { + if dst_table.handle.store_id() != src_table.handle.store_id() { + return Err(RuntimeError::new( + "cross-`Store` table copies are not supported", + )); + } + if dst_table.handle.internal_handle() == src_table.handle.internal_handle() { + let table = dst_table.handle.get_mut(store.objects_mut().as_sys_mut()); + table.copy_within(dst_index, src_index, len) + } else { + let (src_table, dst_table) = store.objects_mut().as_sys_mut().get_2_mut( + src_table.handle.internal_handle(), + dst_table.handle.internal_handle(), + ); + VMTable::copy(dst_table, src_table, dst_index, src_index, len) + } + .map_err(Into::::into)?; + Ok(()) + } + + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternTable) -> Self { + Self { + handle: unsafe { + StoreHandle::from_internal( + store.as_store_ref().objects().id(), + vm_extern.into_sys(), + ) + }, + } + } + + /// Checks whether this `Table` can be used with the given context. + pub(crate) fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + self.handle.store_id() == store.as_store_ref().objects().id() + } + + pub(crate) fn to_vm_extern(&self) -> VMExtern { + VMExtern::Sys(wasmer_vm::VMExtern::Table(self.handle.internal_handle())) + } +} + +impl std::cmp::PartialEq for Table { + fn eq(&self, other: &Self) -> bool { + self.handle == other.handle + } +} + +impl std::cmp::Eq for Table {} + +impl crate::Table { + /// Consume [`self`] into [`crate::rt::sys::table::Table`]. + pub fn into_sys(self) -> crate::rt::sys::table::Table { + match self.0 { + RuntimeTable::Sys(s) => s, + _ => panic!("Not a `sys` table!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::sys::table::Table`]. + pub fn as_sys(&self) -> &crate::rt::sys::table::Table { + match self.0 { + RuntimeTable::Sys(ref s) => s, + _ => panic!("Not a `sys` table!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::sys::table::Table`]. + pub fn as_sys_mut(&mut self) -> &mut crate::rt::sys::table::Table { + match self.0 { + RuntimeTable::Sys(ref mut s) => s, + _ => panic!("Not a `sys` table!"), + } + } +} + +impl crate::RuntimeTable { + /// Consume [`self`] into [`crate::rt::sys::table::Table`]. + pub fn into_sys(self) -> crate::rt::sys::table::Table { + match self { + Self::Sys(s) => s, + _ => panic!("Not a `sys` table!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::sys::table::Table`]. + pub fn as_sys(&self) -> &crate::rt::sys::table::Table { + match self { + Self::Sys(ref s) => s, + _ => panic!("Not a `sys` table!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::sys::table::Table`]. + pub fn as_sys_mut(&mut self) -> &mut crate::rt::sys::table::Table { + match self { + Self::Sys(ref mut s) => s, + _ => panic!("Not a `sys` table!"), + } + } +} diff --git a/lib/api/src/sys/errors.rs b/lib/api/src/rt/sys/error.rs similarity index 82% rename from lib/api/src/sys/errors.rs rename to lib/api/src/rt/sys/error.rs index b5943bef4d3..1455d44813f 100644 --- a/lib/api/src/sys/errors.rs +++ b/lib/api/src/rt/sys/error.rs @@ -1,4 +1,4 @@ -use crate::{LinkError, RuntimeError}; +use crate::LinkError; use wasmer_vm::Trap; impl From for LinkError { @@ -13,12 +13,12 @@ impl From for LinkError { } } -impl From for RuntimeError { +impl From for crate::RuntimeError { fn from(trap: Trap) -> Self { if trap.is::() { return trap.downcast::().unwrap(); } let (wasm_trace, trap_code) = wasmer_compiler::get_trace_and_trapcode(&trap); - Self::new_from_source(trap, wasm_trace, trap_code) + Self::new_from_source(crate::RuntimeTrap::Sys(trap), wasm_trace, trap_code) } } diff --git a/lib/api/src/rt/sys/mod.rs b/lib/api/src/rt/sys/mod.rs new file mode 100644 index 00000000000..e192c2d7450 --- /dev/null +++ b/lib/api/src/rt/sys/mod.rs @@ -0,0 +1,29 @@ +//! Data types, functions and traits for the `sys` runtime. + +pub(crate) mod entities; +pub(crate) mod error; +pub(crate) mod tunables; +pub mod vm; + +pub use engine::NativeEngineExt; +pub use entities::*; +pub use tunables::*; + +#[cfg(feature = "compiler")] +pub use wasmer_compiler::{ + wasmparser, CompilerConfig, FunctionMiddleware, MiddlewareReaderState, ModuleMiddleware, +}; + +pub use wasmer_compiler::{ + types::target::{Architecture, CpuFeature, OperatingSystem, Target, Triple}, + Artifact, EngineBuilder, Features, Tunables, +}; + +pub use wasmer_types::MiddlewareError; + +#[cfg(feature = "cranelift")] +pub use wasmer_compiler_cranelift::{Cranelift, CraneliftOptLevel}; +#[cfg(feature = "llvm")] +pub use wasmer_compiler_llvm::{LLVMOptLevel, LLVM}; +#[cfg(feature = "singlepass")] +pub use wasmer_compiler_singlepass::Singlepass; diff --git a/lib/api/src/sys/tunables.rs b/lib/api/src/rt/sys/tunables.rs similarity index 99% rename from lib/api/src/sys/tunables.rs rename to lib/api/src/rt/sys/tunables.rs index f5ac6151db9..4f81c4bb871 100644 --- a/lib/api/src/sys/tunables.rs +++ b/lib/api/src/rt/sys/tunables.rs @@ -225,7 +225,7 @@ mod tests { } // Will use a minimum stack size of 8kb, not the 1Mb default - fn vmconfig(&self) -> &crate::vm::VMConfig { + fn vmconfig(&self) -> &wasmer_vm::VMConfig { &VMConfig { wasm_stack_size: Some(8 * 1024), } @@ -256,7 +256,9 @@ mod tests { #[test] fn check_custom_tunables() -> Result<(), Box> { - use crate::{imports, wat2wasm, Engine, Instance, Memory, Module, Store}; + #[cfg(feature = "wat")] + use crate::wat2wasm; + use crate::{imports, Engine, Instance, Memory, Module, Store}; let wasm_bytes = wat2wasm( br#"(module diff --git a/lib/api/src/rt/sys/vm.rs b/lib/api/src/rt/sys/vm.rs new file mode 100644 index 00000000000..5d5bb72e04b --- /dev/null +++ b/lib/api/src/rt/sys/vm.rs @@ -0,0 +1,42 @@ +//! The `vm` module re-exports wasmer-vm types. +use crate::entities::{Function, Global, Memory, Table}; +use crate::store::AsStoreMut; +pub use wasmer_vm::*; + +/// The type of extern tables in the `sys` VM. +pub type VMExternTable = InternalStoreHandle; +/// +/// The type of extern memories in the `sys` VM. +pub type VMExternMemory = InternalStoreHandle; + +/// The type of extern globals in the `sys` VM. +pub type VMExternGlobal = InternalStoreHandle; + +/// The type of extern functioons in the `sys` VM. +pub type VMExternFunction = InternalStoreHandle; + +/// The type of function callbacks in the `sys` VM. +pub type VMFunctionCallback = *const VMFunctionBody; + +impl crate::VMExternToExtern for VMExtern { + fn to_extern(self, store: &mut impl AsStoreMut) -> crate::Extern { + match self { + Self::Function(f) => crate::Extern::Function(Function::from_vm_extern( + store, + crate::vm::VMExternFunction::Sys(f), + )), + Self::Memory(m) => crate::Extern::Memory(Memory::from_vm_extern( + store, + crate::vm::VMExternMemory::Sys(m), + )), + Self::Global(g) => crate::Extern::Global(Global::from_vm_extern( + store, + crate::vm::VMExternGlobal::Sys(g), + )), + Self::Table(t) => crate::Extern::Table(Table::from_vm_extern( + store, + crate::vm::VMExternTable::Sys(t), + )), + } + } +} diff --git a/lib/api/src/rt/v8/bindings.rs b/lib/api/src/rt/v8/bindings.rs new file mode 100644 index 00000000000..71f9e088a63 --- /dev/null +++ b/lib/api/src/rt/v8/bindings.rs @@ -0,0 +1,7 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(unused)] + +// This matches bindgen::Builder output +include!(concat!(env!("OUT_DIR"), "/v8_bindings.rs")); diff --git a/lib/api/src/rt/v8/entities/engine.rs b/lib/api/src/rt/v8/entities/engine.rs new file mode 100644 index 00000000000..2e023548ff2 --- /dev/null +++ b/lib/api/src/rt/v8/entities/engine.rs @@ -0,0 +1,103 @@ +//! Data types, functions and traits for `sys` runtime's `Engine` implementation. +use crate::{ + rt::v8::bindings::{wasm_engine_delete, wasm_engine_new, wasm_engine_t}, + RuntimeEngine, +}; +use std::sync::Arc; + +// A handle to an engine, which we want to unsafely mark as Sync. +struct EngineCapsule(*mut wasm_engine_t); + +impl Drop for EngineCapsule { + fn drop(&mut self) { + unsafe { wasm_engine_delete(self.0) } + } +} + +unsafe impl Sync for EngineCapsule {} +unsafe impl Send for EngineCapsule {} + +static ENGINE: std::sync::OnceLock> = std::sync::OnceLock::new(); + +#[derive(Debug)] +pub(crate) struct CApiEngine { + pub(crate) engine: *mut wasm_engine_t, +} + +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 }; + Self { engine } + } +} + +impl Drop for CApiEngine { + fn drop(&mut self) {} +} + +/// The V8 engine. +#[derive(Clone, Debug, Default)] +pub struct Engine { + pub(crate) inner: Arc, +} + +impl Engine { + /// Create a new instance of the `V8` engine. + pub fn new() -> Self { + Self::default() + } + + pub(crate) fn deterministic_id(&self) -> &str { + "v8" + } +} + +unsafe impl Send for Engine {} +unsafe impl Sync for Engine {} + +/// Returns the default engine for the JS engine +pub(crate) fn default_engine() -> Engine { + Engine::default() +} + +impl crate::Engine { + /// Consume [`self`] into a [`crate::rt::v8::engine::Engine`]. + pub fn into_v8(self) -> crate::rt::v8::engine::Engine { + match self.rt { + RuntimeEngine::V8(s) => s, + _ => panic!("Not a `v8` engine!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::v8::engine::Engine`]. + pub fn as_v8(&self) -> &crate::rt::v8::engine::Engine { + match &self.rt { + RuntimeEngine::V8(s) => s, + _ => panic!("Not a `v8` engine!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::v8::engine::Engine`]. + pub fn as_v8_mut(&mut self) -> &mut crate::rt::v8::engine::Engine { + match self.rt { + RuntimeEngine::V8(ref mut s) => s, + _ => panic!("Not a `v8` engine!"), + } + } + + /// Return true if [`self`] is an engine from the `v8` runtime. + pub fn is_v8(&self) -> bool { + matches!(self.rt, RuntimeEngine::V8(_)) + } +} + +impl From for crate::Engine { + fn from(value: Engine) -> Self { + crate::Engine { + rt: RuntimeEngine::V8(value), + id: crate::Engine::atomic_next_engine_id(), + } + } +} diff --git a/lib/api/src/rt/v8/entities/external.rs b/lib/api/src/rt/v8/entities/external.rs new file mode 100644 index 00000000000..f2c01afb6a0 --- /dev/null +++ b/lib/api/src/rt/v8/entities/external.rs @@ -0,0 +1,42 @@ +//! Data types, functions and traits for `v8` runtime's `ExternRef` implementation. +use crate::{ + store::{AsStoreMut, AsStoreRef}, + v8::vm::VMExternRef, +}; +use std::any::Any; + +#[derive(Debug, Clone)] +#[repr(transparent)] +/// A WebAssembly `extern ref` in the `v8` runtime. +pub struct ExternRef; + +impl ExternRef { + pub fn new(_store: &mut impl AsStoreMut, _value: T) -> Self + where + T: Any + Send + Sync + 'static + Sized, + { + unimplemented!("ExternRef is not yet supported with wasm_c_api"); + } + + pub fn downcast<'a, T>(&self, _store: &'a impl AsStoreRef) -> Option<&'a T> + where + T: Any + Send + Sync + 'static + Sized, + { + unimplemented!("ExternRef is not yet supported in wasm_c_api"); + } + + pub(crate) fn vm_externref(&self) -> VMExternRef { + unimplemented!("ExternRef is not yet supported in wasm_c_api"); + } + + pub(crate) unsafe fn from_vm_externref( + _store: &mut impl AsStoreMut, + _vm_externref: VMExternRef, + ) -> Self { + unimplemented!("ExternRef is not yet supported in wasm_c_api"); + } + + pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} diff --git a/lib/api/src/rt/v8/entities/function/env.rs b/lib/api/src/rt/v8/entities/function/env.rs new file mode 100644 index 00000000000..5e44eb6ea99 --- /dev/null +++ b/lib/api/src/rt/v8/entities/function/env.rs @@ -0,0 +1,213 @@ +use std::{any::Any, fmt::Debug, marker::PhantomData}; + +use crate::{ + store::{AsStoreMut, AsStoreRef, StoreRef}, + v8::{store::StoreHandle, vm::VMFunctionEnvironment}, + StoreMut, +}; + +#[derive(Debug)] +#[repr(transparent)] +/// An opaque reference to a function environment. +/// The function environment data is owned by the `Store`. +pub struct FunctionEnv { + pub(crate) handle: StoreHandle, + marker: PhantomData, +} + +impl FunctionEnv { + /// Make a new FunctionEnv + pub fn new(store: &mut impl AsStoreMut, value: T) -> Self + where + T: Any + Send + 'static + Sized, + { + Self { + handle: StoreHandle::new( + store.as_store_mut().objects_mut().as_v8_mut(), + VMFunctionEnvironment::new(value), + ), + marker: PhantomData, + } + } + + /// Get the data as reference + pub fn as_ref<'a>(&self, store: &'a impl AsStoreRef) -> &'a T + where + T: Any + Send + 'static + Sized, + { + self.handle + .get(store.as_store_ref().objects().as_v8()) + .as_ref() + .downcast_ref::() + .unwrap() + } + + pub(crate) fn from_handle(handle: StoreHandle) -> Self { + Self { + handle, + marker: PhantomData, + } + } + + /// Get the data as mutable + pub fn as_mut<'a>(&self, store: &'a mut impl AsStoreMut) -> &'a mut T + where + T: Any + Send + 'static + Sized, + { + self.handle + .get_mut(store.objects_mut().as_v8_mut()) + .as_mut() + .downcast_mut::() + .unwrap() + } + + /// Convert it into a `FunctionEnvMut` + pub fn into_mut(self, store: &mut impl AsStoreMut) -> FunctionEnvMut + where + T: Any + Send + 'static + Sized, + { + FunctionEnvMut { + store_mut: store.as_store_mut(), + func_env: self, + } + } +} + +impl PartialEq for FunctionEnv { + fn eq(&self, other: &Self) -> bool { + self.handle == other.handle + } +} + +impl Eq for FunctionEnv {} + +impl std::hash::Hash for FunctionEnv { + fn hash(&self, state: &mut H) { + self.handle.hash(state); + self.marker.hash(state); + } +} + +impl Clone for FunctionEnv { + fn clone(&self) -> Self { + Self { + handle: self.handle.clone(), + marker: self.marker, + } + } +} + +/// A temporary handle to a [`FunctionEnv`]. +pub struct FunctionEnvMut<'a, T: 'a> { + pub(crate) store_mut: StoreMut<'a>, + pub(crate) func_env: FunctionEnv, +} + +impl<'a, T> Debug for FunctionEnvMut<'a, T> +where + T: Send + Debug + 'static, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.func_env.as_ref(&self.store_mut).fmt(f) + } +} + +impl FunctionEnvMut<'_, T> { + /// Returns a reference to the host state in this function environement. + pub fn data(&self) -> &T { + self.func_env.as_ref(&self.store_mut) + } + + /// Returns a mutable- reference to the host state in this function environement. + pub fn data_mut(&mut self) -> &mut T { + self.func_env.as_mut(&mut self.store_mut) + } + + /// Borrows a new immmutable reference + pub fn as_ref(&self) -> FunctionEnv { + self.func_env.clone() + } + + /// Borrows a new mutable reference + pub fn as_mut(&mut self) -> FunctionEnvMut<'_, T> { + FunctionEnvMut { + store_mut: self.store_mut.as_store_mut(), + func_env: self.func_env.clone(), + } + } + + /// Borrows a new mutable reference of both the attached Store and host state + pub fn data_and_store_mut(&mut self) -> (&mut T, StoreMut) { + let data = self.func_env.as_mut(&mut self.store_mut) as *mut T; + // telling the borrow check to close his eyes here + // this is still relatively safe to do as func_env are + // stored in a specific vec of Store, separate from the other objects + // and not really directly accessible with the StoreMut + let data = unsafe { &mut *data }; + (data, self.store_mut.as_store_mut()) + } +} + +//impl Into> for FunctionEnv { +// fn into(self) -> crate::FunctionEnv { +// crate::FunctionEnv::Wamr(self) +// } +//} + +impl AsStoreRef for FunctionEnvMut<'_, T> { + fn as_store_ref(&self) -> StoreRef<'_> { + StoreRef { + inner: self.store_mut.inner, + } + } +} + +impl AsStoreMut for FunctionEnvMut<'_, T> { + fn as_store_mut(&mut self) -> StoreMut<'_> { + StoreMut { + inner: self.store_mut.inner, + } + } + + fn objects_mut(&mut self) -> &mut crate::StoreObjects { + self.store_mut.objects_mut() + } +} + +impl crate::FunctionEnv { + /// Consume [`self`] into [`crate::rt::v8::function::env::FunctionEnv`]. + pub fn into_v8(self) -> FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::V8(s) => s, + _ => panic!("Not a `v8` function env!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::v8::function::env::FunctionEnv`]. + pub fn as_v8(&self) -> &FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::V8(ref s) => s, + _ => panic!("Not a `v8` function env!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::v8::function::env::FunctionEnv`]. + pub fn as_v8_mut(&mut self) -> &mut FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::V8(ref mut s) => s, + _ => panic!("Not a `v8` function env!"), + } + } +} + +impl<'a, T> From> for crate::FunctionEnvMut<'a, T> { + fn from(value: FunctionEnvMut<'a, T>) -> Self { + crate::FunctionEnvMut(crate::RuntimeFunctionEnvMut::V8(value)) + } +} + +impl From> for crate::FunctionEnv { + fn from(value: FunctionEnv) -> Self { + crate::FunctionEnv(crate::RuntimeFunctionEnv::V8(value)) + } +} diff --git a/lib/api/src/rt/v8/entities/function/mod.rs b/lib/api/src/rt/v8/entities/function/mod.rs new file mode 100644 index 00000000000..35743965da2 --- /dev/null +++ b/lib/api/src/rt/v8/entities/function/mod.rs @@ -0,0 +1,703 @@ +//! Data types, functions and traits for `v8` runtime's `Function` implementation. +#![allow(non_snake_case)] +use std::{ + ffi::c_void, + panic::{self, AssertUnwindSafe}, +}; + +use crate::{ + v8::{ + bindings::*, + utils::convert::{IntoCApiType, IntoCApiValue, IntoWasmerType, IntoWasmerValue}, + vm::{VMFuncRef, VMFunction, VMFunctionCallback, VMFunctionEnvironment}, + }, + vm::{VMExtern, VMExternFunction}, + AsStoreMut, AsStoreRef, FromToNativeWasmType, FunctionEnv, FunctionEnvMut, IntoResult, + NativeWasmType, NativeWasmTypeInto, RuntimeError, RuntimeFunction, RuntimeFunctionEnvMut, + RuntimeTrap, StoreMut, Value, WasmTypeList, WithEnv, WithoutEnv, +}; + +use super::{super::error::Trap, store::StoreHandle}; +use wasmer_types::{FunctionType, RawValue}; + +pub(crate) mod env; +pub(crate) mod typed; + +pub use typed::*; + +type CCallback = + unsafe extern "C" fn(*mut c_void, *const wasm_val_t, *mut wasm_val_t) -> *mut wasm_trap_t; + +#[derive(Clone, PartialEq, Eq)] +/// A WebAssembly `function` in the `v8` runtime. +pub struct Function { + pub(crate) handle: VMFunction, +} + +unsafe impl Send for Function {} +unsafe impl Sync for Function {} + +impl From for Function { + fn from(handle: VMFunction) -> Self { + Self { handle } + } +} + +pub(crate) struct FunctionCallbackEnv<'a, F> { + pub(crate) store: StoreMut<'a>, + pub(crate) func: F, + pub(crate) env_handle: Option>, +} + +impl<'a, F> std::fmt::Debug for FunctionCallbackEnv<'a, F> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FunctionCallbackEnv") + .field("env_is_some", &self.env_handle.is_some()) + .finish() + } +} + +impl Function { + /// To `VMExtern`. + pub fn to_vm_extern(&self) -> VMExtern { + let extern_ = unsafe { wasm_func_as_extern(self.handle) }; + assert!( + !extern_.is_null(), + "Returned null Function extern from wasm-c-api" + ); + VMExtern::V8(extern_) + } + + #[allow(clippy::cast_ptr_alignment)] + pub fn new_with_env( + store: &mut impl AsStoreMut, + env: &FunctionEnv, + ty: FT, + func: F, + ) -> Self + where + FT: Into, + F: Fn(FunctionEnvMut<'_, T>, &[Value]) -> Result, RuntimeError> + + 'static + + Send + + Sync, + { + let fn_ty: FunctionType = ty.into(); + let params = fn_ty.params(); + + let mut param_types = params + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_param_types = unsafe { + let mut vec = Default::default(); + wasm_valtype_vec_new(&mut vec, param_types.len(), param_types.as_ptr()); + vec + }; + + let results = fn_ty.results(); + let mut result_types = results + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_result_types = unsafe { + let mut vec = Default::default(); + wasm_valtype_vec_new(&mut vec, result_types.len(), result_types.as_ptr()); + vec + }; + + let wasm_functype = unsafe { + wasm_functype_new( + &mut wasm_param_types as *mut _, + &mut wasm_result_types as *mut _, + ) + }; + + let mut store = store.as_store_mut(); + let inner = store.inner.store.as_v8().inner; + + let callback: CCallback = make_fn_callback(&func, param_types.len()); + + let mut callback_env: *mut FunctionCallbackEnv<'_, F> = + Box::leak(Box::new(FunctionCallbackEnv { + store, + func, + env_handle: Some(env.as_v8().handle.clone()), + })); + + let wasm_function = unsafe { + wasm_func_new_with_env( + inner, + wasm_functype, + Some(callback), + callback_env as *mut _ as _, + None, + ) + }; + + if wasm_function.is_null() { + panic!("failed when creating new typed function"); + } + + Function { + handle: wasm_function, + } + } + + /// Creates a new host `Function` from a native function. + pub fn new_typed(store: &mut impl AsStoreMut, func: F) -> Self + where + F: crate::HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync, + Args: WasmTypeList, + Rets: WasmTypeList, + { + let mut param_types = Args::wasm_types() + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_param_types = unsafe { + let mut vec = Default::default(); + wasm_valtype_vec_new(&mut vec, param_types.len(), param_types.as_ptr()); + vec + }; + + let mut result_types = Rets::wasm_types() + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_result_types = unsafe { + let mut vec = Default::default(); + wasm_valtype_vec_new(&mut vec, result_types.len(), result_types.as_ptr()); + vec + }; + + let wasm_functype = + unsafe { wasm_functype_new(&mut wasm_param_types, &mut wasm_result_types) }; + + let mut store = store.as_store_mut(); + let inner = store.inner.store.as_v8().inner; + + let callback: CCallback = + unsafe { std::mem::transmute(func.function_callback(crate::Runtime::V8).into_v8()) }; + + let mut callback_env: *mut FunctionCallbackEnv<'_, F> = + Box::into_raw(Box::new(FunctionCallbackEnv { + store, + func, + env_handle: None, + })); + + let wasm_function = unsafe { + wasm_func_new_with_env( + inner, + wasm_functype, + Some(callback), + callback_env as _, + None, + ) + }; + + if wasm_function.is_null() { + panic!("failed when creating new typed function"); + } + + Function { + handle: wasm_function, + } + } + + pub fn new_typed_with_env( + store: &mut impl AsStoreMut, + env: &FunctionEnv, + func: F, + ) -> Self + where + F: crate::HostFunction, + Args: WasmTypeList, + Rets: WasmTypeList, + T: Send + 'static, + { + let mut param_types = Args::wasm_types() + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_param_types = unsafe { + let mut vec = wasm_valtype_vec_t::default(); + wasm_valtype_vec_new(&mut vec, param_types.len(), param_types.as_ptr()); + vec + }; + + let mut result_types = Rets::wasm_types() + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_result_types = unsafe { + let mut vec: wasm_valtype_vec_t = Default::default(); + wasm_valtype_vec_new(&mut vec, result_types.len(), result_types.as_ptr()); + vec + }; + + let wasm_functype = unsafe { + wasm_functype_new( + &mut wasm_param_types as *mut _, + &mut wasm_result_types as *mut _, + ) + }; + + let mut store = store.as_store_mut(); + let inner = store.inner.store.as_v8().inner; + + let callback: CCallback = + unsafe { std::mem::transmute(func.function_callback(crate::Runtime::V8).into_v8()) }; + + let mut callback_env: *mut FunctionCallbackEnv<'_, F> = + Box::into_raw(Box::new(FunctionCallbackEnv { + store, + func, + env_handle: Some(env.as_v8().handle.clone()), + })); + + let wasm_function = unsafe { + wasm_func_new_with_env( + inner, + wasm_functype, + Some(callback), + callback_env as _, + None, + ) + }; + + if wasm_function.is_null() { + panic!("failed when creating new typed function"); + } + + Function { + handle: wasm_function, + } + } + + pub fn ty(&self, _store: &impl AsStoreRef) -> FunctionType { + let type_ = unsafe { wasm_func_type(self.handle) }; + let params: *const wasm_valtype_vec_t = unsafe { wasm_functype_params(type_) }; + let returns: *const wasm_valtype_vec_t = unsafe { wasm_functype_results(type_) }; + + let params: Vec = unsafe { + let mut res = vec![]; + for i in 0..(*params).size { + res.push((*(*params).data.wrapping_add(i)).into_wt()); + } + res + }; + + let returns: Vec = unsafe { + let mut res = vec![]; + for i in 0..(*returns).size { + res.push((*(*returns).data.wrapping_add(i)).into_wt()); + } + res + }; + + FunctionType::new(params, returns) + } + + pub fn call_raw( + &self, + _store: &mut impl AsStoreMut, + _params: Vec, + ) -> Result, RuntimeError> { + // There is no optimal call_raw in JSC, so we just + // simply rely the call + // self.call(store, params) + unimplemented!(); + } + + pub fn call( + &self, + store: &mut impl AsStoreMut, + params: &[Value], + ) -> Result, RuntimeError> { + let store_mut = store.as_store_mut(); + + let mut args = { + let params = params + .into_iter() + .map(|v| v.clone().into_cv()) + .collect::>(); + + let ptr = params.as_ptr(); + + std::mem::forget(params); + + ptr + }; + + let size = unsafe { wasm_func_result_arity(self.handle) }; + + let mut results = { + let mut vec = Vec::with_capacity(size); + let ptr = vec.as_mut_ptr() as *mut wasm_val_t; + + std::mem::forget(vec); + + ptr + }; + + let trap = unsafe { wasm_func_call(self.handle, args as *const _, results as *mut _) }; + + if !trap.is_null() { + return Err(Into::::into(trap).into()); + } + + unsafe { + let results = Vec::from_raw_parts(results, size, size); + + return Ok((results) + .into_iter() + .map(|v| v.into_wv()) + .collect::>() + .into_boxed_slice()); + } + } + + pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, internal: VMExternFunction) -> Self { + Self { + handle: internal.into_v8(), + } + } + + pub(crate) fn vm_funcref(&self, _store: &impl AsStoreRef) -> VMFuncRef { + unimplemented!(); + } + + pub(crate) unsafe fn from_vm_funcref( + _store: &mut impl AsStoreMut, + _funcref: VMFuncRef, + ) -> Self { + unimplemented!(); + } + + /// Checks whether this `Function` can be used with the given context. + pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} + +macro_rules! gen_v8_callback { + ($args:expr) => {{ + unsafe extern "C" fn fn_callback( + env: *mut c_void, + args: *const wasm_val_t, + rets: *mut wasm_val_t, + ) -> *mut wasm_trap_t + where + F: Fn(FunctionEnvMut<'_, T>, &[Value]) -> Result, RuntimeError> + + 'static + + Send + + Sync, + { + let r: *mut (FunctionCallbackEnv<'_, F>) = env as _; + + let mut store = (*r).store.as_store_mut(); + let env_handle = (*r).env_handle.as_ref().unwrap().clone(); + let mut fn_env = crate::rt::v8::function::env::FunctionEnv::from_handle(env_handle) + .into_mut(&mut store); + let func: &F = &(*r).func; + + let mut wasmer_args = vec![]; + + for i in 0..$args { + wasmer_args.push((*(args).wrapping_add(i)).into_wv()); + } + + let result = panic::catch_unwind(AssertUnwindSafe(|| unsafe { + func( + FunctionEnvMut(crate::RuntimeFunctionEnvMut::V8(fn_env)), + wasmer_args.as_slice(), + ) + })); + + match result { + Ok(Ok(native_results)) => { + let mut c_results: Vec = + native_results.into_iter().map(|r| r.into_cv()).collect(); + + unsafe { + for i in 0..c_results.len() { + *((rets).wrapping_add(i)) = c_results[i] + } + } + + unsafe { std::ptr::null_mut() } + } + + Ok(Err(e)) => { + let trap: Trap = Trap::user(Box::new(e)); + unsafe { trap.into_wasm_trap(&mut store) } + } + + Err(e) => { + unimplemented!("host function panicked"); + } + } + } + + fn_callback:: + }}; +} + +fn make_fn_callback(func: &F, args: usize) -> CCallback +where + F: Fn(FunctionEnvMut<'_, T>, &[Value]) -> Result, RuntimeError> + + 'static + + Send + + Sync, +{ + unsafe { + // Hacky: v8 uses an older version of the `wasm_c_api` header in which the signature for + // callbacks is different from that of wamr and wasmi: in v8, args (and returns) are of + // type `*const wasm_val_t` (instead of `wasm_val_vec_t`, which also contains the length of + // the vector), so we can't generate a single callback that at runtime gets the number of + // arguments to perform the call to the underlying function. Therefore, we need to generate + // different concrete callbacks for different signatures. + + seq_macro::seq!(arg_num in 1..=26 { + let func = if args == 0 { + gen_v8_callback!(0) + } + #( + else if args == arg_num { + gen_v8_callback!(arg_num) + } + )* + else { + panic!("v8 callbacks for function with more than 26 parameters are not supported!"); + }; + + }); + return func; + } +} + +impl std::fmt::Debug for Function { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.debug_struct("Function").finish() + } +} + +impl crate::Function { + /// Consume [`self`] into [`crate::rt::v8::function::Function`]. + pub fn into_v8(self) -> crate::rt::v8::function::Function { + match self.0 { + RuntimeFunction::V8(s) => s, + _ => panic!("Not a `v8` function!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::v8::function::Function`]. + pub fn as_v8(&self) -> &crate::rt::v8::function::Function { + match self.0 { + RuntimeFunction::V8(ref s) => s, + _ => panic!("Not a `v8` function!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::v8::function::Function`]. + pub fn as_v8_mut(&mut self) -> &mut crate::rt::v8::function::Function { + match self.0 { + RuntimeFunction::V8(ref mut s) => s, + _ => panic!("Not a `v8` function!"), + } + } +} + +macro_rules! impl_host_function { + ([$c_struct_representation:ident] $c_struct_name:ident, $( $x:ident ),* ) => { + paste::paste! { + #[allow(non_snake_case)] + pub(crate) fn [] + <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, Func: Fn($( $x , )*) -> RetsAsResult + 'static> + (this: &Func) -> crate::rt::v8::vm::VMFunctionCallback { + unsafe extern "C" fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func>(env: *mut c_void, args: *const wasm_val_t, results: *mut wasm_val_t) -> *mut wasm_trap_t + where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + RetsAsResult: IntoResult, + Func: Fn($( $x , )*) -> RetsAsResult + 'static, + { + let mut r: *mut crate::rt::v8::function::FunctionCallbackEnv = unsafe {std::mem::transmute(env)}; + let store = &mut (*r).store.as_store_mut(); + let mut i = 0; + + $( + let c_arg = (*(args).wrapping_add(i)).clone(); + let wasmer_arg = c_arg.into_wv(); + let raw_arg : RawValue = wasmer_arg.as_raw(store); + let $x : $x = FromToNativeWasmType::from_native($x::Native::from_raw(store, raw_arg)); + i += 1; + )* + + let result = panic::catch_unwind(AssertUnwindSafe(|| unsafe { ((*r).func)( $( $x, )* ).into_result() })); + + match result { + Ok(Ok(result)) => { + let types = Rets::wasm_types(); + let size = types.len(); + let mut native_results = result.into_array(store); + let native_results = native_results.as_mut(); + let native_results: Vec = native_results.into_iter().enumerate() + .map(|(i, r)| Value::from_raw(store, types[i], r.clone())) + .collect(); + let mut c_results: Vec = native_results.into_iter().map(|r| r.into_cv()).collect(); + + if c_results.len() != size { + panic!("when calling host function: number of observed results differ from wanted results") + } + + unsafe { + for i in 0..size { + *((results).wrapping_add(i)) = c_results[i] + } + } + unsafe { std::ptr::null_mut() } + }, + Ok(Err(e)) => { + let trap: crate::rt::v8::error::Trap = crate::rt::v8::error::Trap::user(Box::new(e)); + unsafe { trap.into_wasm_trap(store) } + // unimplemented!("host function panicked"); + }, + Err(e) => { + unimplemented!("host function panicked"); + } + } + } + func_wrapper::< $( $x, )* Rets, RetsAsResult, Func> as _ + + } + + + #[allow(non_snake_case)] + pub(crate) fn [] + <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, T: Send + 'static, Func: Fn(FunctionEnvMut, $( $x , )*) -> RetsAsResult + 'static> + (this: &Func) -> crate::rt::v8::vm::VMFunctionCallback { + + unsafe extern "C" fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func, T>(env: *mut c_void, args: *const wasm_val_t, results: *mut wasm_val_t) -> *mut wasm_trap_t + where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + RetsAsResult: IntoResult, + T: Send + 'static, + Func: Fn(FunctionEnvMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static, + { + + let r: *mut (crate::rt::v8::function::FunctionCallbackEnv<'_, Func>) = env as _; + let store = &mut (*r).store.as_store_mut(); + let mut i = 0; + + $( + let c_arg = (*(args).wrapping_add(i)).clone(); + let wasmer_arg = c_arg.into_wv(); + let raw_arg : RawValue = wasmer_arg.as_raw(store); + let $x : $x = FromToNativeWasmType::from_native($x::Native::from_raw(store, raw_arg)); + i += 1; + )* + + let env_handle = (*r).env_handle.as_ref().unwrap().clone(); + let mut fn_env = crate::rt::v8::function::env::FunctionEnv::from_handle(env_handle).into_mut(store); + let func: &Func = &(*r).func; + let result = panic::catch_unwind(AssertUnwindSafe(|| unsafe { + ((*r).func)(RuntimeFunctionEnvMut::V8(fn_env).into(), $( $x, )* ).into_result() + })); + + match result { + Ok(Ok(result)) => { + let types = Rets::wasm_types(); + let size = types.len(); + let mut native_results = result.into_array(store); + let native_results = native_results.as_mut(); + + let native_results: Vec = native_results.into_iter().enumerate() + .map(|(i, r)| Value::from_raw(store, types[i], r.clone())) + .collect(); + + let mut c_results: Vec = native_results.into_iter().map(|r| r.into_cv()).collect(); + + if c_results.len() != size { + panic!("when calling host function: number of observed results differ from wanted results") + } + + unsafe { + for i in 0..size { + *((results).wrapping_add(i)) = c_results[i] + } + } + + unsafe { std::ptr::null_mut() } + }, + Ok(Err(e)) => { + let trap: crate::rt::v8::error::Trap = crate::rt::v8::error::Trap::user(Box::new(e)); + unsafe { trap.into_wasm_trap(store) } + }, + Err(e) => { unimplemented!("host function panicked"); } + } + } + + func_wrapper::< $( $x, )* Rets, RetsAsResult, Func, T> as _ + + } + } + }; +} + +// Here we go! Let's generate all the C struct, `WasmTypeList` +// implementations and `HostFunction` implementations. +impl_host_function!([C] S0,); +impl_host_function!([transparent] S1, A1); +impl_host_function!([C] S2, A1, A2); +impl_host_function!([C] S3, A1, A2, A3); +impl_host_function!([C] S4, A1, A2, A3, A4); +impl_host_function!([C] S5, A1, A2, A3, A4, A5); +impl_host_function!([C] S6, A1, A2, A3, A4, A5, A6); +impl_host_function!([C] S7, A1, A2, A3, A4, A5, A6, A7); +impl_host_function!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8); +impl_host_function!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9); +impl_host_function!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); +impl_host_function!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); +impl_host_function!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); +impl_host_function!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); +impl_host_function!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); +impl_host_function!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); +impl_host_function!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16); +impl_host_function!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17); +impl_host_function!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18); +impl_host_function!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19); +impl_host_function!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20); +impl_host_function!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21); +impl_host_function!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22); +impl_host_function!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23); +impl_host_function!([C] S24, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24); +impl_host_function!([C] S25, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25); +impl_host_function!([C] S26, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26); diff --git a/lib/api/src/rt/v8/entities/function/typed.rs b/lib/api/src/rt/v8/entities/function/typed.rs new file mode 100644 index 00000000000..0164aadac2b --- /dev/null +++ b/lib/api/src/rt/v8/entities/function/typed.rs @@ -0,0 +1,114 @@ +//! Native Functions. +//! +//! This module creates the helper `TypedFunction` that let us call WebAssembly +//! functions with the native ABI, that is: +//! +//! ```ignore +//! let add_one = instance.exports.get_function("function_name")?; +//! let add_one_native: TypedFunction = add_one.typed().unwrap(); +//! ``` + +use std::iter::FromIterator; + +use crate::{ + rt::v8::{bindings::*, error::Trap, function::Function, utils::convert::*}, + AsStoreMut, FromToNativeWasmType, NativeWasmType, NativeWasmTypeInto, RuntimeError, + TypedFunction, Value, WasmTypeList, +}; +use wasmer_types::RawValue; + +macro_rules! impl_native_traits { + ( $( $x:ident ),* ) => { + #[allow(unused_parens, non_snake_case)] + impl<$( $x , )* Rets> TypedFunction<( $( $x ),* ), Rets> + where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + { + /// Call the typed func and return results. + #[allow(clippy::too_many_arguments)] + pub fn call_v8(&self, mut store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result where + $( $x: FromToNativeWasmType, )* + { + + #[allow(unused_unsafe)] + let params_list: Vec<_> = unsafe { + 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() + } ),* ] + }; + + let mut results = unsafe { + let rets_len = Rets::wasm_types().len(); + let mut vec = Vec::::with_capacity(rets_len); + let ptr = vec.as_mut_ptr(); + std::mem::forget(vec); + ptr as *mut _ + }; + + + let func = unsafe { wasm_extern_as_func(self.func.to_vm_extern().into_v8()) }; + + let trap = unsafe { + wasm_func_call(func, params_list.as_ptr() as *const _, results) + }; + + if !trap.is_null() { + unsafe { + let trap: Trap = trap.into(); + return Err(RuntimeError::from(trap)); + } + } + + unsafe { + let rets_len = Rets::wasm_types().len(); + let mut results: *const [crate::rt::v8::bindings::wasm_val_t] = std::ptr::slice_from_raw_parts(results, rets_len); + + unsafe { + let results: Vec<_> = (*results).into_iter().map(|v| v.into_wv().as_raw(&mut store)).collect(); + Ok(unsafe {Rets::from_slice(store, &results).unwrap()}) + } + } + } + + #[doc(hidden)] + #[allow(missing_docs)] + #[allow(unused_mut)] + #[allow(clippy::too_many_arguments)] + pub fn call_raw_v8(&self, store: &mut impl AsStoreMut, mut params_list: Vec ) -> Result { + todo!("Raw calls from v8 are not supported yet!") + } + } + }; + +} + +impl_native_traits!(); +impl_native_traits!(A1); +impl_native_traits!(A1, A2); +impl_native_traits!(A1, A2, A3); +impl_native_traits!(A1, A2, A3, A4); +impl_native_traits!(A1, A2, A3, A4, A5); +impl_native_traits!(A1, A2, A3, A4, A5, A6); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17); +impl_native_traits!( + A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18 +); +impl_native_traits!( + A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19 +); +impl_native_traits!( + A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20 +); diff --git a/lib/api/src/rt/v8/entities/global.rs b/lib/api/src/rt/v8/entities/global.rs new file mode 100644 index 00000000000..f446d77063f --- /dev/null +++ b/lib/api/src/rt/v8/entities/global.rs @@ -0,0 +1,118 @@ +//! Data types, functions and traits for `v8` runtime's `Global` implementation. +use wasmer_types::{GlobalType, Mutability}; + +use crate::{ + v8::{ + bindings::{ + self, wasm_global_as_extern, wasm_global_get, wasm_global_new, wasm_global_set, + wasm_global_type, wasm_globaltype_content, wasm_globaltype_mutability, + wasm_globaltype_new, wasm_mutability_t, wasm_valtype_new, + }, + utils::convert::{IntoCApiType, IntoCApiValue, IntoWasmerType, IntoWasmerValue}, + vm::VMGlobal, + }, + vm::{VMExtern, VMExternGlobal}, + AsStoreMut, AsStoreRef, RuntimeError, Value, +}; + +#[derive(Debug, Clone, PartialEq, Eq)] +/// A WebAssembly `global` in the `v8` runtime. +pub struct Global { + pub(crate) handle: VMGlobal, +} + +unsafe impl Send for Global {} +unsafe impl Sync for Global {} + +// Global can't be Send in js because it dosen't support `structuredClone` +// https://developer.mozilla.org/en-US/docs/Web/API/structuredClone +// unsafe impl Send for Global {} + +impl Global { + pub(crate) fn to_vm_extern(&self) -> VMExtern { + let extern_ = unsafe { wasm_global_as_extern(self.handle) }; + assert!( + !extern_.is_null(), + "Returned null Global extern from wasm-c-api" + ); + VMExtern::V8(extern_) + } + + /// Create a `Global` with the initial value [`Value`] and the provided [`Mutability`]. + pub(crate) fn from_value( + store: &mut impl AsStoreMut, + val: Value, + mutability: Mutability, + ) -> Result { + let store = store.as_store_mut(); + + let v8_type = val.ty().into_ct(); + let v8_value = val.into_cv(); + let v8_mutability = if mutability.is_mutable() { + bindings::wasm_mutability_enum_WASM_VAR + } else { + bindings::wasm_mutability_enum_WASM_CONST + } as wasm_mutability_t; + + let v8_global_type = + unsafe { wasm_globaltype_new(wasm_valtype_new(v8_type), v8_mutability) }; + + Ok(Self { + handle: unsafe { + wasm_global_new(store.inner.store.as_v8().inner, v8_global_type, &v8_value) + }, + }) + } + + pub fn ty(&self, _store: &impl AsStoreRef) -> GlobalType { + let r#type = unsafe { wasm_global_type(self.handle) }; + let mutability = unsafe { wasm_globaltype_mutability(&*r#type) }; + let valtype = unsafe { wasm_globaltype_content(r#type) }; + let wasmer_type = valtype.into_wt(); + + GlobalType::new( + wasmer_type, + if mutability == bindings::wasm_mutability_enum_WASM_VAR as u8 { + Mutability::Var + } else { + Mutability::Const + }, + ) + } + + pub fn get(&self, store: &mut impl AsStoreMut) -> Value { + let mut out = unsafe { std::mem::zeroed() }; + unsafe { wasm_global_get(self.handle, &mut out) }; + out.into_wv() + } + + pub fn set(&self, store: &mut impl AsStoreMut, val: Value) -> Result<(), RuntimeError> { + if val.ty() != self.ty(store).ty { + return Err(RuntimeError::new(format!( + "Incompatible types: {} != {}", + val.ty(), + self.ty(store) + ))); + } + + if self.ty(store).mutability == Mutability::Const { + return Err(RuntimeError::new("The global is immutable".to_owned())); + } + + let value = val.into_cv(); + + unsafe { wasm_global_set(self.handle, &value) }; + + Ok(()) + } + + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_global: VMExternGlobal) -> Self { + Self { + handle: vm_global.into_v8(), + } + } + + pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} diff --git a/lib/api/src/rt/v8/entities/instance.rs b/lib/api/src/rt/v8/entities/instance.rs new file mode 100644 index 00000000000..d25ebb79d5a --- /dev/null +++ b/lib/api/src/rt/v8/entities/instance.rs @@ -0,0 +1,131 @@ +//! Data types, functions and traits for `v8` runtime's `Function` implementation. +use std::sync::Arc; + +use crate::{ + rt::v8::bindings::*, v8::error::Trap, vm::VMExtern, AsStoreMut, AsStoreRef, Exports, Extern, + Imports, InstantiationError, Module, +}; + +#[derive(PartialEq, Eq)] +pub(crate) struct InstanceHandle(pub(crate) *mut wasm_instance_t); + +unsafe impl Send for InstanceHandle {} +unsafe impl Sync for InstanceHandle {} + +impl InstanceHandle { + fn new( + store: *mut wasm_store_t, + module: *mut wasm_module_t, + mut externs: Vec, + ) -> Result { + let mut trap: *mut wasm_trap_t = std::ptr::null_mut() as _; + + let externs: Vec<_> = externs.into_iter().map(|v| v.into_v8()).collect(); + + let instance = unsafe { + let ptr = externs.as_ptr(); + std::mem::forget(externs); + + wasm_instance_new(store, module, ptr as *const *const _, &mut trap) + }; + + if instance.is_null() { + let trap = Trap::from(trap); + return Err(InstantiationError::Start(trap.into())); + } + + Ok(InstanceHandle(instance)) + } + + fn get_exports(&self, mut store: &mut impl AsStoreMut, module: &Module) -> Exports { + let mut exports = unsafe { + let mut vec = Default::default(); + wasm_instance_exports(self.0, &mut vec); + vec + }; + + let wasm_exports: &[*mut wasm_extern_t] = + unsafe { std::slice::from_raw_parts(exports.data, exports.size) }; + + let exports_ty = module.exports().collect::>(); + let exports = exports_ty + .iter() + .zip(wasm_exports.into_iter()) + .map(|(export_type, wasm_export)| { + let name = export_type.name(); + let mut store = store.as_store_mut(); + let extern_type = export_type.ty(); + // Annotation is here to prevent spurious IDE warnings. + + let extern_ = Extern::from_vm_extern(&mut store, VMExtern::V8(*wasm_export)); + (name.to_string(), extern_) + }) + .collect::(); + exports + } +} +impl Drop for InstanceHandle { + fn drop(&mut self) { + unsafe { wasm_instance_delete(self.0) } + } +} + +#[derive(Clone, PartialEq, Eq)] +/// A WebAssembly `instance` in the `v8` runtime. +pub struct Instance { + pub(crate) handle: Arc, +} + +impl Instance { + pub(crate) fn new( + store: &mut impl AsStoreMut, + module: &Module, + imports: &Imports, + ) -> Result<(Self, Exports), InstantiationError> { + let externs = module + .imports() + .map(|import_ty| { + imports + .get_export(import_ty.module(), import_ty.name()) + .expect("Extern not found") + }) + .collect::>(); + + return Self::new_by_index(store, module, &externs); + } + + pub(crate) fn new_by_index( + store: &mut impl AsStoreMut, + module: &Module, + externs: &[Extern], + ) -> Result<(Self, Exports), InstantiationError> { + let store_ref = store.as_store_ref(); + let externs: Vec = externs + .iter() + .map(|extern_| extern_.to_vm_extern()) + .collect::>(); + let instance = InstanceHandle::new( + store_ref.inner.store.as_v8().inner, + module.as_v8().handle.inner, + externs, + )?; + let exports = instance.get_exports(store, module); + + Ok(( + Self { + handle: Arc::new(instance), + }, + exports, + )) + } +} + +impl crate::RuntimeInstance { + /// Consume [`self`] into a [`crate::rt::v8::instance::Instance`]. + pub(crate) fn into_v8(self) -> crate::rt::v8::instance::Instance { + match self { + Self::V8(s) => s, + _ => panic!("Not a `v8` instance"), + } + } +} diff --git a/lib/api/src/c_api/externals/memory.rs b/lib/api/src/rt/v8/entities/memory/mod.rs similarity index 79% rename from lib/api/src/c_api/externals/memory.rs rename to lib/api/src/rt/v8/entities/memory/mod.rs index 3ccefff550f..4643d2b5054 100644 --- a/lib/api/src/c_api/externals/memory.rs +++ b/lib/api/src/rt/v8/entities/memory/mod.rs @@ -1,27 +1,22 @@ -use crate::bindings::{ - wasm_limits_max_default, wasm_limits_t, wasm_memory_copy, wasm_memory_grow, wasm_memory_new, - wasm_memory_size, wasm_memory_t, wasm_memory_type, wasm_memorytype_limits, wasm_memorytype_new, - wasm_memorytype_t, -}; -use crate::c_api::bindings::wasm_memory_as_extern; -use crate::c_api::vm::{VMExtern, VMMemory}; -use crate::mem_access::MemoryAccessError; -use crate::store::{AsStoreMut, AsStoreRef, StoreObjects}; -use crate::MemoryType; -use std::convert::TryInto; -use std::marker::PhantomData; -use std::mem::{self, MaybeUninit}; -use std::slice; +//! Data types, functions and traits for `v8` runtime's `Memory` implementation. +use std::{marker::PhantomData, mem::MaybeUninit}; use tracing::warn; +pub use wasmer_types::MemoryError; +use wasmer_types::{MemoryType, Pages, WASM_PAGE_SIZE}; -use wasmer_types::{Pages, WASM_PAGE_SIZE}; - -use super::memory_view::MemoryView; +use crate::{ + shared::SharedMemory, + v8::{bindings::*, vm::VMMemory}, + vm::{VMExtern, VMExternMemory}, + AsStoreMut, AsStoreRef, MemoryAccessError, RuntimeMemory, +}; -pub use wasmer_types::MemoryError; +pub(crate) mod view; +pub use view::*; #[derive(Debug, Clone)] +/// A WebAssembly `memory` in the `v8` runtime. pub struct Memory { pub(crate) handle: VMMemory, } @@ -42,28 +37,28 @@ impl Memory { let memorytype = unsafe { wasm_memorytype_new(limits) }; let mut store = store.as_store_mut(); - let inner = store.inner.store.inner; - let c_memory = unsafe { wasm_memory_new(store.inner.store.inner, memorytype) }; + let inner = store.inner.store.as_v8().inner; + let c_memory = unsafe { wasm_memory_new(inner, memorytype) }; Ok(Self { handle: c_memory }) } pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { - Self::from_vm_extern(new_store, memory) + Self { handle: memory } } pub(crate) fn to_vm_extern(&self) -> VMExtern { - unsafe { wasm_memory_as_extern(self.handle) } + VMExtern::V8(unsafe { wasm_memory_as_extern(self.handle) }) } pub fn ty(&self, _store: &impl AsStoreRef) -> MemoryType { - let wamr_memory_type: *mut wasm_memorytype_t = unsafe { wasm_memory_type(self.handle) }; - let limits: *const wasm_limits_t = unsafe { wasm_memorytype_limits(wamr_memory_type) }; + let memory_type: *mut wasm_memorytype_t = unsafe { wasm_memory_type(self.handle) }; + let limits: *const wasm_limits_t = unsafe { wasm_memorytype_limits(memory_type) }; MemoryType { // [TODO]: Find a way to extract this from the inner memory type instead // of hardcoding. - shared: if cfg!(feature = "wamr") { true } else { false }, + shared: false, minimum: unsafe { wasmer_types::Pages((*limits).min) }, maximum: unsafe { Some(wasmer_types::Pages((*limits).max)) }, } @@ -82,19 +77,10 @@ impl Memory { where IntoPages: Into, { - #[cfg(feature = "wamr")] - { - unimplemented!( - "calling grow from host is not supported! Use the memory.grow opcode instead." - ); - } - - #[cfg(any(feature = "v8", feature = "wasmi"))] unsafe { let delta: Pages = delta.into(); let current = Pages(wasm_memory_size(self.handle)); - eprintln!("current: {current:?}, delta: {delta:?}"); if !wasm_memory_grow(self.handle, delta.0) { Err(MemoryError::CouldNotGrow { current, @@ -111,9 +97,15 @@ impl Memory { store: &mut impl AsStoreMut, min_size: u64, ) -> Result<(), MemoryError> { - unimplemented!( - "calling grow from host is not supported! Use the memory.grow opcode instead." - ); + unsafe { + let current = wasm_memory_size(self.handle); + let delta = (min_size as u32) - current; + if delta > 0 { + self.grow(store, delta)?; + } + } + + Ok(()) } pub fn reset(&self, _store: &mut impl AsStoreMut) -> Result<(), MemoryError> { @@ -147,8 +139,10 @@ impl Memory { // Ok(new_memory) } - pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, internal: VMMemory) -> Self { - Self { handle: internal } + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, internal: VMExternMemory) -> Self { + Self { + handle: internal.into_v8(), + } } /// Cloning memory will create another reference to the same memory that @@ -178,7 +172,7 @@ impl Memory { // self.handle.duplicate(store) } - pub fn as_shared(&self, _store: &impl AsStoreRef) -> Option { + pub fn as_shared(&self, _store: &impl AsStoreRef) -> Option { // Not supported. None } @@ -248,7 +242,7 @@ impl<'a> MemoryBuffer<'a> { volatile_memcpy_read(self.base.add(offset as usize), buf_ptr, buf.len()); } - Ok(unsafe { slice::from_raw_parts_mut(buf_ptr, buf.len()) }) + Ok(unsafe { std::slice::from_raw_parts_mut(buf_ptr, buf.len()) }) } pub(crate) fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { @@ -286,9 +280,9 @@ unsafe fn volatile_memcpy_read(mut src: *const u8, mut dst: *mut u8, mut len: us struct Unaligned(T); let val = (*src as *const Unaligned).read_volatile(); (*dst as *mut Unaligned).write(val); - *src = src.add(mem::size_of::()); - *dst = dst.add(mem::size_of::()); - *len -= mem::size_of::(); + *src = src.add(std::mem::size_of::()); + *dst = dst.add(std::mem::size_of::()); + *len -= std::mem::size_of::(); } while len >= 8 { @@ -313,9 +307,9 @@ unsafe fn volatile_memcpy_write(mut src: *const u8, mut dst: *mut u8, mut len: u struct Unaligned(T); let val = (*src as *const Unaligned).read(); (*dst as *mut Unaligned).write_volatile(val); - *src = src.add(mem::size_of::()); - *dst = dst.add(mem::size_of::()); - *len -= mem::size_of::(); + *src = src.add(std::mem::size_of::()); + *dst = dst.add(std::mem::size_of::()); + *len -= std::mem::size_of::(); } while len >= 8 { @@ -331,3 +325,29 @@ unsafe fn volatile_memcpy_write(mut src: *const u8, mut dst: *mut u8, mut len: u copy_one::(&mut src, &mut dst, &mut len); } } + +impl crate::Memory { + /// Consume [`self`] into a [`crate::rt::v8::memory::Memory`]. + pub fn into_v8(self) -> crate::rt::v8::memory::Memory { + match self.0 { + RuntimeMemory::V8(s) => s, + _ => panic!("Not a `v8` memory!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::v8::memory::Memory`]. + pub fn as_v8(&self) -> &crate::rt::v8::memory::Memory { + match self.0 { + RuntimeMemory::V8(ref s) => s, + _ => panic!("Not a `v8` memory!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference to [`crate::rt::v8::memory::Memory`]. + pub fn as_v8_mut(&mut self) -> &mut crate::rt::v8::memory::Memory { + match self.0 { + RuntimeMemory::V8(ref mut s) => s, + _ => panic!("Not a `v8` memory!"), + } + } +} diff --git a/lib/api/src/c_api/externals/memory_view.rs b/lib/api/src/rt/v8/entities/memory/view.rs similarity index 93% rename from lib/api/src/c_api/externals/memory_view.rs rename to lib/api/src/rt/v8/entities/memory/view.rs index f18d77b7ab8..c46c358acc0 100644 --- a/lib/api/src/c_api/externals/memory_view.rs +++ b/lib/api/src/rt/v8/entities/memory/view.rs @@ -1,17 +1,13 @@ -use crate::bindings::{ - wasm_memory_data, wasm_memory_data_size, wasm_memory_size, wasm_memory_t, wasm_memory_type, - wasm_memorytype_limits, -}; -use crate::store::AsStoreRef; -use crate::MemoryAccessError; -use std::convert::TryFrom; -use std::marker::PhantomData; -use std::mem::MaybeUninit; -use std::slice; -use std::{convert::TryInto, ops::Range}; -use wasmer_types::{Bytes, Pages}; +use std::{marker::PhantomData, mem::MaybeUninit, ops::Range}; + +use wasmer_types::Pages; -use super::memory::{Memory, MemoryBuffer}; +use super::{Memory, MemoryBuffer}; +use crate::{ + rt::v8::bindings::{wasm_memory_data, wasm_memory_data_size, wasm_memory_size}, + v8::bindings::wasm_memory_t, + AsStoreRef, MemoryAccessError, +}; /// A WebAssembly `memory` view. /// @@ -81,7 +77,7 @@ impl<'a> MemoryView<'a> { #[allow(clippy::mut_from_ref)] #[doc(hidden)] pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] { - slice::from_raw_parts_mut(self.buffer.base, self.buffer.len) + std::slice::from_raw_parts_mut(self.buffer.base, self.buffer.len) } /// Returns the size (in [`Pages`]) of the `Memory`. diff --git a/lib/api/src/rt/v8/entities/mod.rs b/lib/api/src/rt/v8/entities/mod.rs new file mode 100644 index 00000000000..e842f2f6659 --- /dev/null +++ b/lib/api/src/rt/v8/entities/mod.rs @@ -0,0 +1,9 @@ +pub mod engine; +pub(crate) mod external; +pub(crate) mod function; +pub(crate) mod global; +pub(crate) mod instance; +pub(crate) mod memory; +pub(crate) mod module; +pub(crate) mod store; +pub(crate) mod table; diff --git a/lib/api/src/rt/v8/entities/module.rs b/lib/api/src/rt/v8/entities/module.rs new file mode 100644 index 00000000000..a0fc06aeb15 --- /dev/null +++ b/lib/api/src/rt/v8/entities/module.rs @@ -0,0 +1,188 @@ +//! Data types, functions and traits for `v8` runtime's `Module` implementation. +use std::{path::Path, sync::Arc}; + +use crate::{ + rt::v8::bindings::{wasm_byte_vec_t, wasm_module_delete, wasm_module_new, wasm_module_t}, + AsEngineRef, IntoBytes, RuntimeModule, +}; + +use bytes::Bytes; +use wasmer_types::{ + CompileError, DeserializeError, ExportType, ExportsIterator, ExternType, FunctionType, + GlobalType, ImportType, ImportsIterator, MemoryType, ModuleInfo, Mutability, Pages, + SerializeError, TableType, Type, +}; +pub(crate) struct ModuleHandle { + pub(crate) inner: *mut wasm_module_t, + pub(crate) store: std::sync::Mutex, +} + +impl PartialEq for ModuleHandle { + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner + // && self.store.lock() == other.store.lock() + } +} + +impl Eq for ModuleHandle {} + +impl ModuleHandle { + fn new(engine: &impl AsEngineRef, binary: &[u8]) -> Result { + let bytes = wasm_byte_vec_t { + size: binary.len(), + data: binary.as_ptr() as _, + }; + + let store = crate::store::Store::new(engine.as_engine_ref().engine().clone()); + + let inner = unsafe { wasm_module_new(store.inner.store.as_v8().inner, &bytes as *const _) }; + let store = std::sync::Mutex::new(store); + + if inner.is_null() { + return Err(CompileError::Validate(format!("module is null"))); + } + + Ok(ModuleHandle { inner, store }) + } +} +impl Drop for ModuleHandle { + fn drop(&mut self) { + unsafe { wasm_module_delete(self.inner) } + } +} + +#[derive(Clone, PartialEq, Eq)] +/// A WebAssembly `module` in the `v8` runtime. +pub struct Module { + pub(crate) handle: Arc, + name: Option, + raw_bytes: Option, + info: ModuleInfo, +} + +unsafe impl Send for Module {} +unsafe impl Sync for Module {} + +impl Module { + pub(crate) fn from_binary( + _engine: &impl AsEngineRef, + binary: &[u8], + ) -> Result { + unsafe { Self::from_binary_unchecked(_engine, binary) } + } + + pub(crate) unsafe fn from_binary_unchecked( + engine: &impl AsEngineRef, + binary: &[u8], + ) -> Result { + let mut binary = binary.to_vec(); + let binary = binary.into_bytes(); + let module = ModuleHandle::new(engine, &binary)?; + let info = crate::utils::polyfill::translate_module(&binary[..]) + .unwrap() + .info; + + Ok(Self { + handle: Arc::new(module), + name: info.name.clone(), + raw_bytes: Some(binary.into_bytes()), + info, + }) + } + + pub fn validate(engine: &impl AsEngineRef, binary: &[u8]) -> Result<(), CompileError> { + let engine = engine.as_engine_ref(); + unimplemented!(); + } + + pub fn name(&self) -> Option<&str> { + self.name.as_ref().map(|s| s.as_ref()) + } + + pub fn serialize(&self) -> Result { + return self.raw_bytes.clone().ok_or(SerializeError::Generic( + "Not able to serialize module".to_string(), + )); + } + + pub unsafe fn deserialize_unchecked( + engine: &impl AsEngineRef, + bytes: impl IntoBytes, + ) -> Result { + Self::deserialize(engine, bytes) + } + + pub unsafe fn deserialize( + engine: &impl AsEngineRef, + bytes: impl IntoBytes, + ) -> Result { + return Self::from_binary(engine, &bytes.into_bytes()) + .map_err(|e| DeserializeError::Compiler(e)); + } + + pub unsafe fn deserialize_from_file_unchecked( + engine: &impl AsEngineRef, + path: impl AsRef, + ) -> Result { + let bytes = std::fs::read(path.as_ref())?; + Self::deserialize_unchecked(engine, bytes) + } + + pub unsafe fn deserialize_from_file( + engine: &impl AsEngineRef, + path: impl AsRef, + ) -> Result { + let bytes = std::fs::read(path.as_ref())?; + Self::deserialize(engine, bytes) + } + + pub fn set_name(&mut self, name: &str) -> bool { + self.name = Some(name.to_string()); + true + } + + pub fn imports<'a>(&'a self) -> ImportsIterator + 'a>> { + self.info().imports() + } + + pub fn exports<'a>(&'a self) -> ExportsIterator + 'a>> { + self.info().exports() + } + + pub fn custom_sections<'a>( + &'a self, + name: &'a str, + ) -> Box> + 'a> { + self.info().custom_sections(name) + } + + pub(crate) fn info(&self) -> &ModuleInfo { + &self.info + } +} + +impl crate::Module { + /// Consume [`self`] into a reference [`crate::rt::v8::module::Module`]. + pub fn into_v8(self) -> crate::rt::v8::module::Module { + match self.0 { + RuntimeModule::V8(s) => s, + _ => panic!("Not a `v8` module!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::v8::module::Module`]. + pub fn as_v8(&self) -> &crate::rt::v8::module::Module { + match self.0 { + RuntimeModule::V8(ref s) => s, + _ => panic!("Not a `v8` module!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::v8::module::Module`]. + pub fn as_v8_mut(&mut self) -> &mut crate::rt::v8::module::Module { + match self.0 { + RuntimeModule::V8(ref mut s) => s, + _ => panic!("Not a `v8` module!"), + } + } +} diff --git a/lib/api/src/rt/v8/entities/store/mod.rs b/lib/api/src/rt/v8/entities/store/mod.rs new file mode 100644 index 00000000000..90937284282 --- /dev/null +++ b/lib/api/src/rt/v8/entities/store/mod.rs @@ -0,0 +1,103 @@ +//! Data types, functions and traits for `v8` runtime's `Store` implementation. +use crate::{ + engine::{AsEngineRef, Engine, EngineRef}, + rt::v8::bindings::{wasm_store_delete, wasm_store_new, wasm_store_t}, + AsStoreRef, RuntimeStore, StoreRef, +}; + +mod obj; +pub use obj::*; + +/// A WebAssembly `store` in the `v8` runtime. +pub struct Store { + pub(crate) engine: Engine, + pub(crate) inner: *mut wasm_store_t, +} + +impl std::fmt::Debug for Store { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Store") + .field("engine", &self.engine) + .finish() + } +} + +impl Store { + pub(crate) fn new(engine: crate::engine::Engine) -> Self { + let inner: *mut wasm_store_t = unsafe { wasm_store_new(engine.as_v8().inner.engine) }; + Store { inner, engine } + } + + pub(crate) fn engine(&self) -> &Engine { + &self.engine + } + + pub(crate) fn engine_mut(&mut self) -> &mut Engine { + &mut self.engine + } +} + +impl Drop for Store { + fn drop(&mut self) { + unsafe { wasm_store_delete(self.inner) } + } +} + +impl AsEngineRef for Store { + fn as_engine_ref(&self) -> EngineRef<'_> { + EngineRef::new(&self.engine) + } +} + +impl crate::RuntimeStore { + /// Consume [`self`] into [`crate::rt::v8::store::Store`]. + pub fn into_v8(self) -> crate::rt::v8::store::Store { + match self { + Self::V8(s) => s, + _ => panic!("Not a `v8` store!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::v8::store::Store`]. + pub fn as_v8(&self) -> &crate::rt::v8::store::Store { + match self { + Self::V8(s) => s, + _ => panic!("Not a `v8` store!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::v8::store::Store`]. + pub fn as_v8_mut(&mut self) -> &mut crate::rt::v8::store::Store { + match self { + Self::V8(s) => s, + _ => panic!("Not a `v8` store!"), + } + } + + /// Return true if [`self`] is a store from the `v8` runtime. + pub fn is_v8(&self) -> bool { + matches!(self, Self::V8(_)) + } +} + +impl crate::Store { + /// Consume [`self`] into [`crate::rt::v8::store::Store`]. + pub(crate) fn into_v8(self) -> crate::rt::v8::store::Store { + self.inner.store.into_v8() + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::v8::store::Store`]. + pub(crate) fn as_v8(&self) -> &crate::rt::v8::store::Store { + self.inner.store.as_v8() + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::v8::store::Store`]. + pub(crate) fn as_v8_mut(&mut self) -> &mut crate::rt::v8::store::Store { + self.inner.store.as_v8_mut() + } + + /// Return true if [`self`] is a store from the `v8` runtime. + pub fn is_v8(&self) -> bool { + self.inner.store.is_v8() + } +} diff --git a/lib/api/src/rt/v8/entities/store/obj.rs b/lib/api/src/rt/v8/entities/store/obj.rs new file mode 100644 index 00000000000..b99738b37e3 --- /dev/null +++ b/lib/api/src/rt/v8/entities/store/obj.rs @@ -0,0 +1,288 @@ +use std::{fmt, marker::PhantomData, num::NonZeroUsize}; + +use crate::{ + rt::v8::vm::{VMFunctionEnvironment, VMGlobal}, + AsStoreMut, +}; + +pub use wasmer_types::StoreId; + +impl crate::StoreObjects { + /// Consume [`self`] into [`crate::rt::v8::store::StoreObjects`]. + pub fn into_v8(self) -> crate::rt::v8::store::StoreObjects { + match self { + crate::StoreObjects::V8(s) => s, + _ => panic!("Not a `v8` store!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::v8::store::StoreObjects`]. + pub fn as_v8(&self) -> &crate::rt::v8::store::StoreObjects { + match self { + crate::StoreObjects::V8(s) => s, + _ => panic!("Not a `v8` store!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::v8::store::StoreObjects`]. + pub fn as_v8_mut(&mut self) -> &mut crate::rt::v8::store::StoreObjects { + match self { + crate::StoreObjects::V8(s) => s, + _ => panic!("Not a `v8` store!"), + } + } +} + +/// Trait to represent an object managed by a context. This is implemented on +/// the VM types managed by the context. +pub trait StoreObject: Sized { + fn list(store: &StoreObjects) -> &Vec; + fn list_mut(store: &mut StoreObjects) -> &mut Vec; +} + +macro_rules! impl_store_object { + ($($field:ident => $ty:ty,)*) => { + $( + impl StoreObject for $ty { + fn list(store: &StoreObjects) -> &Vec { + &store.$field + } + fn list_mut(store: &mut StoreObjects) -> &mut Vec { + &mut store.$field + } + } + )* + }; +} + +impl_store_object! { + // Note: we store the globals in order to be able to access them later via + // `StoreObjects::iter_globals`. + globals => VMGlobal, + // functions => VMFunction, + // tables => VMTable, + // memories => VMMemory, + // The function environments are the only things attached to a store, + // since the other JS objects (table, globals, memory and functions) + // live in the JS VM Store by default + function_environments => VMFunctionEnvironment, +} + +/// Set of objects managed by a context. +#[derive(Default, Debug)] +pub struct StoreObjects { + id: StoreId, + globals: Vec, + function_environments: Vec, +} + +impl StoreObjects { + /// Returns the ID of this context. + pub fn id(&self) -> StoreId { + self.id + } + + /// Sets the ID of this store + pub fn set_id(&mut self, id: StoreId) { + self.id = id; + } + + /// Returns a pair of mutable references from two handles. + /// + /// Panics if both handles point to the same object. + pub fn get_2_mut( + &mut self, + a: InternalStoreHandle, + b: InternalStoreHandle, + ) -> (&mut T, &mut T) { + assert_ne!(a.index(), b.index()); + let list = T::list_mut(self); + if a.index() < b.index() { + let (low, high) = list.split_at_mut(b.index()); + (&mut low[a.index()], &mut high[0]) + } else { + let (low, high) = list.split_at_mut(a.index()); + (&mut high[0], &mut low[a.index()]) + } + } + + /// Return an immutable iterator over all globals + pub fn iter_globals(&self) -> core::slice::Iter { + self.globals.iter() + } + + /// Return an vector of all globals and converted to u128 + pub fn as_u128_globals(&self) -> Vec { + vec![] + // self.iter_globals() + // .map(|v| v.global.value().as_f64().unwrap() as u128) + // .collect() + } + + /// Set a global, at index idx. Will panic if idx is out of range + /// Safety: the caller should check taht the raw value is compatible + /// with destination VMGlobal type + pub fn set_global_unchecked(&self, idx: usize, new_val: u128) { + assert!(idx < self.globals.len()); + // let g = &self.globals[idx].global; + // let cur_val = g.value().as_f64().unwrap(); + // let new_val = new_val as f64; + // if cur_val != new_val { + // let new_value = JSValue::from(new_val); + // g.set_value(&new_value); + // } + } +} + +/// Handle to an object managed by a context. +/// +/// Internally this is just an integer index into a context. A reference to the +/// context must be passed in separately to access the actual object. +pub struct StoreHandle { + id: StoreId, + internal: InternalStoreHandle, +} + +impl core::cmp::PartialEq for StoreHandle { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl std::hash::Hash for StoreHandle { + fn hash(&self, state: &mut H) { + self.id.hash(state); + self.internal.idx.hash(state); + } +} + +impl Clone for StoreHandle { + fn clone(&self) -> Self { + Self { + id: self.id, + internal: self.internal, + } + } +} + +impl fmt::Debug for StoreHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StoreHandle") + .field("id", &self.id) + .field("internal", &self.internal.index()) + .finish() + } +} + +impl StoreHandle { + /// Moves the given object into a context and returns a handle to it. + pub fn new(store: &mut StoreObjects, val: T) -> Self { + Self { + id: store.id, + internal: InternalStoreHandle::new(store, val), + } + } + + /// Returns a reference to the object that this handle points to. + pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T { + assert_eq!(self.id, store.id, "object used with the wrong context"); + self.internal.get(store) + } + + /// Returns a mutable reference to the object that this handle points to. + pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T { + assert_eq!(self.id, store.id, "object used with the wrong context"); + self.internal.get_mut(store) + } + + /// Returns the internal handle contains within this handle. + pub fn internal_handle(&self) -> InternalStoreHandle { + self.internal + } + + /// Returns the ID of the context associated with the handle. + #[allow(unused)] + pub fn store_id(&self) -> StoreId { + self.id + } + + /// Overrides the store id with a new ID + #[allow(unused)] + pub fn set_store_id(&mut self, id: StoreId) { + self.id = id; + } + + /// Constructs a `StoreHandle` from a `StoreId` and an `InternalStoreHandle`. + /// + /// # Safety + /// Handling `InternalStoreHandle` values is unsafe because they do not track context ID. + pub unsafe fn from_internal(id: StoreId, internal: InternalStoreHandle) -> Self { + Self { id, internal } + } +} + +/// Internal handle to an object owned by the current context. +/// +/// Unlike `StoreHandle` this does not track the context ID: it is only +/// intended to be used within objects already owned by a context. +#[repr(transparent)] +pub struct InternalStoreHandle { + // Use a NonZero here to reduce the size of Option. + idx: NonZeroUsize, + marker: PhantomData T>, +} + +impl Clone for InternalStoreHandle { + fn clone(&self) -> Self { + *self + } +} +impl Copy for InternalStoreHandle {} + +impl fmt::Debug for InternalStoreHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("InternalStoreHandle") + .field("idx", &self.idx) + .finish() + } +} +impl PartialEq for InternalStoreHandle { + fn eq(&self, other: &Self) -> bool { + self.idx == other.idx + } +} +impl Eq for InternalStoreHandle {} + +impl InternalStoreHandle { + /// Moves the given object into a context and returns a handle to it. + pub fn new(store: &mut StoreObjects, val: T) -> Self { + let list = T::list_mut(store); + let idx = NonZeroUsize::new(list.len() + 1).unwrap(); + list.push(val); + Self { + idx, + marker: PhantomData, + } + } + + /// Returns a reference to the object that this handle points to. + pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T { + &T::list(store)[self.idx.get() - 1] + } + + /// Returns a mutable reference to the object that this handle points to. + pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T { + &mut T::list_mut(store)[self.idx.get() - 1] + } + + pub(crate) fn index(&self) -> usize { + self.idx.get() + } + + pub(crate) fn from_index(idx: usize) -> Option { + NonZeroUsize::new(idx).map(|idx| Self { + idx, + marker: PhantomData, + }) + } +} diff --git a/lib/api/src/rt/v8/entities/table.rs b/lib/api/src/rt/v8/entities/table.rs new file mode 100644 index 00000000000..59ae0c78d98 --- /dev/null +++ b/lib/api/src/rt/v8/entities/table.rs @@ -0,0 +1,233 @@ +//! Data types, functions and traits for `v8` runtime's `Table` implementation. +use wasmer_types::TableType; + +use crate::{ + v8::{ + bindings::{self, *}, + utils::convert::{IntoCApiType, IntoCApiValue, IntoWasmerType, IntoWasmerValue}, + vm::VMTable, + }, + vm::{VMExtern, VMExternTable}, + AsStoreMut, AsStoreRef, RuntimeError, RuntimeTable, Value, +}; + +#[derive(Debug, Clone, PartialEq, Eq)] +/// A WebAssembly `table` in the `v8` runtime. +pub struct Table { + pub(crate) handle: VMTable, +} + +unsafe impl Send for Table {} +unsafe impl Sync for Table {} + +impl Table { + 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 { + min: ty.minimum, + max: match ty.maximum { + Some(v) => v, + None => 0, + }, + })); + + unsafe { wasm_tabletype_new(valtype, limits) } + } + + pub fn new( + store: &mut impl AsStoreMut, + ty: TableType, + init: Value, + ) -> Result { + let store_mut = store.as_store_mut(); + let engine = store_mut.engine(); + + let wasm_tablety = Self::type_to_v8(ty); + let init: wasm_val_t = init.into_cv(); + + Ok(Self { + handle: unsafe { + wasm_table_new( + store_mut.inner.store.as_v8().inner, + wasm_tablety, + init.of.ref_, + ) + }, + }) + } + + pub fn to_vm_extern(&self) -> VMExtern { + VMExtern::V8(unsafe { wasm_table_as_extern(self.handle) }) + } + + pub fn ty(&self, _store: &impl AsStoreRef) -> TableType { + 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(), + minimum: unsafe { (*table_limits).min }, + maximum: unsafe { + if (*table_limits).max == 0 { + None + } else { + Some((*table_limits).max) + } + }, + } + } + + pub fn get(&self, store: &mut impl AsStoreMut, index: u32) -> Option { + unsafe { + let ref_ = wasm_table_get(self.handle, index); + + if ref_.is_null() { + return None; + } + + let kind = match self.ty(store).ty { + wasmer_types::Type::ExternRef => wasm_valkind_enum_WASM_ANYREF, + wasmer_types::Type::FuncRef => wasm_valkind_enum_WASM_FUNCREF, + ty => panic!("unsupported table type: {ty:?}"), + } as u8; + + let value = wasm_val_t { + kind, + of: bindings::wasm_val_t__bindgen_ty_1 { ref_ }, + }; + + Some(value.into_wv()) + } + } + + pub fn set( + &self, + store: &mut impl AsStoreMut, + index: u32, + val: Value, + ) -> Result<(), RuntimeError> { + unsafe { + let init = match val { + Value::ExternRef(None) | Value::FuncRef(None) => std::ptr::null_mut(), + Value::FuncRef(Some(ref r)) => wasm_func_as_ref(r.as_v8().handle), + _ => { + return Err(RuntimeError::new(format!( + "Could not grow table due to unsupported init value type: {val:?} " + ))) + } + }; + + if !wasm_table_set(self.handle, index, init) { + return Err(RuntimeError::new(format!( + "Could not set value {val:?} table at index {index}" + ))); + } + + Ok(()) + } + } + + pub fn size(&self, store: &impl AsStoreRef) -> u32 { + unsafe { wasm_table_size(self.handle) } + } + + pub fn grow( + &self, + store: &mut impl AsStoreMut, + delta: u32, + init: Value, + ) -> Result { + unsafe { + let size = wasm_table_size(self.handle); + let init = match init { + Value::ExternRef(None) | Value::FuncRef(None) => std::ptr::null_mut(), + Value::FuncRef(Some(r)) => wasm_func_as_ref(r.as_v8().handle), + _ => { + return Err(RuntimeError::new(format!( + "Could not grow table due to unsupported init value type: {init:?} " + ))) + } + }; + if !wasm_table_grow(self.handle, delta, init) { + return Err(RuntimeError::new("Could not grow table")); + } + + Ok(size) + } + } + + pub fn copy( + _store: &mut impl AsStoreMut, + _dst_table: &Self, + _dst_index: u32, + _src_table: &Self, + _src_index: u32, + _len: u32, + ) -> Result<(), RuntimeError> { + unimplemented!("Copying tables is currently not implemented!") + } + + pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, vm_extern: VMExternTable) -> Self { + Self { + handle: vm_extern.into_v8(), + } + } + + pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} + +impl crate::Table { + /// Consume [`self`] into [`crate::rt::v8::table::Table`]. + pub fn into_v8(self) -> crate::rt::v8::table::Table { + match self.0 { + RuntimeTable::V8(s) => s, + _ => panic!("Not a `v8` table!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::v8::table::Table`]. + pub fn as_v8(&self) -> &crate::rt::v8::table::Table { + match self.0 { + RuntimeTable::V8(ref s) => s, + _ => panic!("Not a `v8` table!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::v8::table::Table`]. + pub fn as_v8_mut(&mut self) -> &mut crate::rt::v8::table::Table { + match self.0 { + RuntimeTable::V8(ref mut s) => s, + _ => panic!("Not a `v8` table!"), + } + } +} + +impl crate::RuntimeTable { + /// Consume [`self`] into [`crate::rt::v8::table::Table`]. + pub fn into_v8(self) -> crate::rt::v8::table::Table { + match self { + Self::V8(s) => s, + _ => panic!("Not a `v8` table!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::v8::table::Table`]. + pub fn as_v8(&self) -> &crate::rt::v8::table::Table { + match self { + Self::V8(ref s) => s, + _ => panic!("Not a `v8` table!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::v8::table::Table`]. + pub fn as_v8_mut(&mut self) -> &mut crate::rt::v8::table::Table { + match self { + Self::V8(ref mut s) => s, + _ => panic!("Not a `v8` table!"), + } + } +} diff --git a/lib/api/src/c_api/trap.rs b/lib/api/src/rt/v8/error.rs similarity index 88% rename from lib/api/src/c_api/trap.rs rename to lib/api/src/rt/v8/error.rs index caef5fff201..6e1430a7032 100644 --- a/lib/api/src/c_api/trap.rs +++ b/lib/api/src/rt/v8/error.rs @@ -1,12 +1,9 @@ -use crate::bindings::{ - wasm_byte_vec_new, wasm_byte_vec_new_empty, wasm_byte_vec_t, wasm_trap_message, +use std::{ + error::Error, + ffi::{c_char, CStr}, }; -use crate::c_api::bindings::{wasm_message_t, wasm_trap_new, wasm_trap_t}; -use crate::{AsStoreMut, RuntimeError}; -use std::error::Error; -use std::ffi::{c_char, CStr}; -use std::fmt; -use std::mem::size_of; + +use crate::{v8::bindings::*, AsStoreMut}; #[derive(Debug)] enum InnerTrap { @@ -68,7 +65,7 @@ impl Trap { wasm_byte_vec_new(&mut data, _s.len(), _s.as_ptr() as _); std::mem::forget(_s); let store = store.as_store_mut(); - wasm_trap_new(store.inner.store.inner, &mut data) + wasm_trap_new(store.inner.store.as_v8().inner, &mut data) } } } @@ -123,8 +120,8 @@ impl std::error::Error for Trap { } } -impl fmt::Display for Trap { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl std::fmt::Display for Trap { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self.inner { InnerTrap::User(e) => write!(f, "{}", e), InnerTrap::CApi(value) => { @@ -142,3 +139,13 @@ impl fmt::Display for Trap { } } } + +impl From for crate::RuntimeError { + fn from(trap: Trap) -> Self { + if trap.is::() { + return trap.downcast::().unwrap(); + } + + crate::RuntimeError::new_from_source(crate::RuntimeTrap::V8(trap), vec![], None) + } +} diff --git a/lib/api/src/rt/v8/mod.rs b/lib/api/src/rt/v8/mod.rs new file mode 100644 index 00000000000..a0f9e9f6b0c --- /dev/null +++ b/lib/api/src/rt/v8/mod.rs @@ -0,0 +1,9 @@ +//! Data types, functions and traits for the `sys` runtime. + +pub(crate) mod bindings; +pub(crate) mod entities; +pub(crate) mod error; +pub(crate) mod utils; +pub(crate) mod vm; + +pub use entities::{engine::Engine as V8, *}; diff --git a/lib/api/src/rt/v8/utils/convert.rs b/lib/api/src/rt/v8/utils/convert.rs new file mode 100644 index 00000000000..cb932e37638 --- /dev/null +++ b/lib/api/src/rt/v8/utils/convert.rs @@ -0,0 +1,138 @@ +/// Utilities to convert between `v8` and `wasmer` values +use crate::{ + v8::{ + bindings::{self, *}, + function, + }, + Function, RuntimeFunction, Value, +}; +use wasmer_types::Type; + +pub trait IntoCApiValue { + /// Consume [`self`] to produce a [`wasm_val_t`]. + fn into_cv(self) -> wasm_val_t; +} + +impl IntoCApiValue for Value { + fn into_cv(self) -> wasm_val_t { + match self { + Value::I32(val) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_I32 as _, + of: wasm_val_t__bindgen_ty_1 { i32_: val }, + }, + Value::I64(val) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_I64 as _, + of: wasm_val_t__bindgen_ty_1 { i64_: val }, + }, + Value::F32(val) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_F32 as _, + of: wasm_val_t__bindgen_ty_1 { f32_: val }, + }, + Value::F64(val) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_F64 as _, + of: wasm_val_t__bindgen_ty_1 { f64_: val }, + }, + Value::FuncRef(Some(val)) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_FUNCREF as _, + of: wasm_val_t__bindgen_ty_1 { + ref_: unsafe { wasm_func_as_ref(val.as_v8().handle) }, + }, + }, + Value::FuncRef(None) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_FUNCREF as _, + of: wasm_val_t__bindgen_ty_1 { + ref_: unsafe { wasm_func_as_ref(std::ptr::null_mut()) }, + }, + }, + Value::ExternRef(_) => panic!("Creating host values from guest ExternRefs is not currently supported through wasm_c_api.") , + Value::V128(_) => panic!("Creating host values from guest V128s is not currently supported through wasm_c_api."), + } + } +} + +pub trait IntoWasmerValue { + /// Consume [`self`] to produce a [`Value`]. + fn into_wv(self) -> Value; +} + +impl IntoWasmerValue for wasm_val_t { + fn into_wv(self) -> Value { + match self.kind as _ { + bindings::wasm_valkind_enum_WASM_I32 => Value::I32(unsafe { self.of.i32_ }), + bindings::wasm_valkind_enum_WASM_I64 => Value::I64(unsafe { self.of.i64_ }), + bindings::wasm_valkind_enum_WASM_F32 => Value::F32(unsafe { self.of.f32_ }), + bindings::wasm_valkind_enum_WASM_F64 => Value::F64(unsafe { self.of.f64_ }), + bindings::wasm_valkind_enum_WASM_FUNCREF => Value::FuncRef(Some(Function( + RuntimeFunction::V8(crate::rt::v8::function::Function { + handle: unsafe { self.of.ref_ as _ }, + }), + ))), + bindings::wasm_valkind_enum_WASM_ANYREF => { + panic!("ExternRefs are not currently supported through wasm_c_api") + } + + _ => { + panic!("v8 currently does not support V128 values") + } + } + } +} + +pub trait IntoWasmerType { + /// Consume [`self`] to produce a [`Type`]. + fn into_wt(self) -> Type; +} + +impl IntoWasmerType for wasm_valkind_t { + fn into_wt(self) -> Type { + match self as _ { + bindings::wasm_valkind_enum_WASM_I32 => Type::I32, + bindings::wasm_valkind_enum_WASM_I64 => Type::I64, + bindings::wasm_valkind_enum_WASM_F32 => Type::F32, + bindings::wasm_valkind_enum_WASM_F64 => Type::F64, + bindings::wasm_valkind_enum_WASM_ANYREF => Type::ExternRef, + bindings::wasm_valkind_enum_WASM_FUNCREF => Type::FuncRef, + _ => unreachable!("v8 kind {self:?} has no matching wasmer_types::Type"), + } + } +} + +pub trait IntoCApiType { + /// Consume [`self`] to produce a [`wasm_valkind_t`]. + fn into_ct(self) -> wasm_valkind_t; +} + +impl IntoCApiType for Type { + fn into_ct(self) -> wasm_valkind_t { + match self as _ { + Type::I32 => bindings::wasm_valkind_enum_WASM_I32 as _, + Type::I64 => bindings::wasm_valkind_enum_WASM_I64 as _, + Type::F32 => bindings::wasm_valkind_enum_WASM_F32 as _, + Type::F64 => bindings::wasm_valkind_enum_WASM_F64 as _, + Type::FuncRef => bindings::wasm_valkind_enum_WASM_FUNCREF as _, + Type::ExternRef => bindings::wasm_valkind_enum_WASM_ANYREF as _, + Type::V128 => panic!("v8 currently does not support V128 values"), + } + } +} + +impl IntoWasmerType for wasm_valtype_t { + fn into_wt(self) -> Type { + let type_: wasm_valkind_t = unsafe { wasm_valtype_kind(&self as *const _) }; + type_.into_wt() + } +} + +impl IntoWasmerType for *const wasm_valtype_t { + fn into_wt(self) -> Type { + let type_: wasm_valkind_t = unsafe { wasm_valtype_kind(self as _) }; + type_.into_wt() + } +} + +impl IntoWasmerType for *mut wasm_valtype_t { + fn into_wt(self) -> Type { + let type_: wasm_valkind_t = unsafe { wasm_valtype_kind(self as _) }; + type_.into_wt() + } +} diff --git a/lib/api/src/rt/v8/utils/mod.rs b/lib/api/src/rt/v8/utils/mod.rs new file mode 100644 index 00000000000..02d63d4e141 --- /dev/null +++ b/lib/api/src/rt/v8/utils/mod.rs @@ -0,0 +1 @@ +pub(crate) mod convert; diff --git a/lib/api/src/rt/v8/vm/env.rs b/lib/api/src/rt/v8/vm/env.rs new file mode 100644 index 00000000000..d2a93d5f63d --- /dev/null +++ b/lib/api/src/rt/v8/vm/env.rs @@ -0,0 +1,28 @@ +use std::any::Any; + +/// Underlying FunctionEnvironment used by a `VMFunction`. +#[derive(Debug)] +pub struct VMFunctionEnvironment { + pub(crate) contents: Box, +} + +impl VMFunctionEnvironment { + /// Wraps the given value to expose it to Wasm code as a function context. + pub fn new(val: impl Any + Send + 'static) -> Self { + Self { + contents: Box::new(val), + } + } + + #[allow(clippy::should_implement_trait)] + /// Returns a reference to the underlying value. + pub fn as_ref(&self) -> &(dyn Any + Send + 'static) { + &*self.contents + } + + #[allow(clippy::should_implement_trait)] + /// Returns a mutable reference to the underlying value. + pub fn as_mut(&mut self) -> &mut (dyn Any + Send + 'static) { + &mut *self.contents + } +} diff --git a/lib/api/src/rt/v8/vm/mod.rs b/lib/api/src/rt/v8/vm/mod.rs new file mode 100644 index 00000000000..fa7e835fa83 --- /dev/null +++ b/lib/api/src/rt/v8/vm/mod.rs @@ -0,0 +1,123 @@ +mod env; +pub use env::*; + +pub(crate) use super::bindings::{ + wasm_extern_as_func, wasm_extern_as_global, wasm_extern_as_memory, wasm_extern_as_table, + wasm_extern_kind, wasm_extern_t, wasm_func_t, wasm_global_t, wasm_instance_t, wasm_memory_t, + wasm_ref_t, wasm_table_t, +}; +use super::{ + entities::function::env::FunctionEnv, function::Function, global::Global, memory::Memory, + table::Table, +}; +use crate::{AsStoreMut, Extern, RuntimeFunction, RuntimeGlobal, RuntimeMemory, RuntimeTable}; +use wasmer_types::RawValue; + +pub use super::error::Trap; + +pub(crate) type VMExtern = *mut wasm_extern_t; + +pub(crate) type VMFunction = *mut wasm_func_t; +pub(crate) type VMFunctionBody = (); +pub(crate) type VMFunctionCallback = *mut ::std::os::raw::c_void; +pub(crate) type VMTrampoline = *mut ::std::os::raw::c_void; +pub(crate) type VMExternFunction = *mut wasm_func_t; + +pub(crate) type VMGlobal = *mut wasm_global_t; +pub(crate) type VMExternGlobal = *mut wasm_global_t; + +pub(crate) type VMMemory = *mut wasm_memory_t; +pub type VMSharedMemory = VMMemory; +pub(crate) type VMExternMemory = *mut wasm_memory_t; + +pub(crate) type VMTable = *mut wasm_table_t; +pub(crate) type VMExternTable = *mut wasm_table_t; + +pub(crate) type VMInstance = *mut wasm_instance_t; + +pub(crate) type VMExternObj = (); +pub(crate) type VMConfig = (); + +impl crate::VMExternToExtern for VMExtern { + fn to_extern(self, store: &mut impl AsStoreMut) -> Extern { + let kind = unsafe { wasm_extern_kind(&mut *self) }; + + match kind as u32 { + 0 => { + let func = unsafe { wasm_extern_as_func(&mut *self) }; + if func.is_null() { + panic!("The wasm-c-api reported extern as function, but is not"); + } + Extern::Function(crate::Function::from_vm_extern( + store, + crate::vm::VMExternFunction::V8(func), + )) + } + 1 => { + let global = unsafe { wasm_extern_as_global(&mut *self) }; + if global.is_null() { + panic!("The wasm-c-api reported extern as a global, but is not"); + } + Extern::Global(crate::Global::from_vm_extern( + store, + crate::vm::VMExternGlobal::V8(global), + )) + } + 2 => { + let table = unsafe { wasm_extern_as_table(&mut *self) }; + if table.is_null() { + panic!("The wasm-c-api reported extern as a table, but is not"); + } + Extern::Table(crate::Table::from_vm_extern( + store, + crate::vm::VMExternTable::V8(table), + )) + } + 3 => { + let memory = unsafe { wasm_extern_as_memory(&mut *self) }; + if memory.is_null() { + panic!("The wasm-c-api reported extern as a table, but is not"); + } + Extern::Memory(crate::Memory::from_vm_extern( + store, + crate::vm::VMExternMemory::V8(memory), + )) + } + _ => { + unimplemented!() + } + } + } +} + +pub(crate) struct VMExternRef(*mut wasm_ref_t); +impl VMExternRef { + /// Converts the `VMExternRef` into a `RawValue`. + pub fn into_raw(self) -> RawValue { + unimplemented!() + } + + /// Extracts a `VMExternRef` from a `RawValue`. + /// + /// # Safety + /// `raw` must be a valid `VMExternRef` instance. + pub unsafe fn from_raw(_raw: RawValue) -> Option { + unimplemented!(); + } +} + +pub(crate) struct VMFuncRef(*mut wasm_ref_t); +impl VMFuncRef { + /// Converts the `VMExternRef` into a `RawValue`. + pub fn into_raw(self) -> RawValue { + unimplemented!() + } + + /// Extracts a `VMExternRef` from a `RawValue`. + /// + /// # Safety + /// `raw` must be a valid `VMExternRef` instance. + pub unsafe fn from_raw(_raw: RawValue) -> Option { + unimplemented!(); + } +} diff --git a/lib/api/src/rt/wamr/bindings.rs b/lib/api/src/rt/wamr/bindings.rs new file mode 100644 index 00000000000..9e9ae39fb20 --- /dev/null +++ b/lib/api/src/rt/wamr/bindings.rs @@ -0,0 +1,7 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(unused)] + +// This matches bindgen::Builder output +include!(concat!(env!("OUT_DIR"), "/wamr_bindings.rs")); diff --git a/lib/api/src/rt/wamr/entities/engine.rs b/lib/api/src/rt/wamr/entities/engine.rs new file mode 100644 index 00000000000..9b1a076921d --- /dev/null +++ b/lib/api/src/rt/wamr/entities/engine.rs @@ -0,0 +1,89 @@ +//! Data types, functions and traits for `v8` runtime's `Engine` implementation. +use crate::{ + rt::wamr::bindings::{wasm_engine_delete, wasm_engine_new, wasm_engine_t}, + RuntimeEngine, +}; +use std::sync::Arc; + +#[derive(Debug)] +pub(crate) struct CApiEngine { + pub(crate) engine: *mut wasm_engine_t, +} + +impl Default for CApiEngine { + fn default() -> Self { + let engine: *mut wasm_engine_t = unsafe { wasm_engine_new() }; + Self { engine } + } +} + +impl Drop for CApiEngine { + fn drop(&mut self) { + unsafe { wasm_engine_delete(self.engine) } + } +} + +/// The engine for the Web Assembly Micro Runtime. +#[derive(Clone, Debug, Default)] +pub struct Engine { + pub(crate) inner: Arc, +} + +impl Engine { + /// Create a new instance of the `wamr` engine. + pub fn new() -> Self { + Self::default() + } + + pub(crate) fn deterministic_id(&self) -> &str { + "wamr" + } +} + +unsafe impl Send for Engine {} +unsafe impl Sync for Engine {} + +/// Returns the default engine for the JS engine +pub(crate) fn default_engine() -> Engine { + Engine::default() +} + +impl crate::Engine { + /// Consume [`self`] into a [`crate::rt::wamr::engine::Engine`]. + pub fn into_wamr(self) -> crate::rt::wamr::engine::Engine { + match self.rt { + RuntimeEngine::Wamr(s) => s, + _ => panic!("Not a `wamr` engine!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::wamr::engine::Engine`]. + pub fn as_wamr(&self) -> &crate::rt::wamr::engine::Engine { + match &self.rt { + RuntimeEngine::Wamr(s) => s, + _ => panic!("Not a `wamr` engine!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wamr::engine::Engine`]. + pub fn as_wamr_mut(&mut self) -> &mut crate::rt::wamr::engine::Engine { + match self.rt { + RuntimeEngine::Wamr(ref mut s) => s, + _ => panic!("Not a `wamr` engine!"), + } + } + + /// Return true if [`self`] is an engine from the `wamr` runtime. + pub fn is_wamr(&self) -> bool { + matches!(self.rt, RuntimeEngine::Wamr(_)) + } +} + +impl From for crate::Engine { + fn from(value: Engine) -> Self { + crate::Engine { + rt: RuntimeEngine::Wamr(value), + id: crate::Engine::atomic_next_engine_id(), + } + } +} diff --git a/lib/api/src/c_api/extern_ref.rs b/lib/api/src/rt/wamr/entities/external.rs similarity index 82% rename from lib/api/src/c_api/extern_ref.rs rename to lib/api/src/rt/wamr/entities/external.rs index 05b51ed8f67..b5fbad27f61 100644 --- a/lib/api/src/c_api/extern_ref.rs +++ b/lib/api/src/rt/wamr/entities/external.rs @@ -1,10 +1,13 @@ +//! Data types, functions and traits for `wamr`'s `ExternRef` implementation. +use crate::{ + store::{AsStoreMut, AsStoreRef}, + wamr::vm::VMExternRef, +}; use std::any::Any; -use crate::c_api::vm::VMExternRef; -use crate::store::{AsStoreMut, AsStoreRef}; - #[derive(Debug, Clone)] #[repr(transparent)] +/// A WebAssembly `extern ref` in `wamr`. pub struct ExternRef; impl ExternRef { diff --git a/lib/api/src/rt/wamr/entities/function/env.rs b/lib/api/src/rt/wamr/entities/function/env.rs new file mode 100644 index 00000000000..15427faf26d --- /dev/null +++ b/lib/api/src/rt/wamr/entities/function/env.rs @@ -0,0 +1,213 @@ +use std::{any::Any, fmt::Debug, marker::PhantomData}; + +use crate::{ + store::{AsStoreMut, AsStoreRef, StoreRef}, + wamr::{store::StoreHandle, vm::VMFunctionEnvironment}, + StoreMut, +}; + +#[derive(Debug)] +#[repr(transparent)] +/// An opaque reference to a function environment. +/// The function environment data is owned by the `Store`. +pub struct FunctionEnv { + pub(crate) handle: StoreHandle, + marker: PhantomData, +} + +impl FunctionEnv { + /// Make a new FunctionEnv + pub fn new(store: &mut impl AsStoreMut, value: T) -> Self + where + T: Any + Send + 'static + Sized, + { + Self { + handle: StoreHandle::new( + store.as_store_mut().objects_mut().as_wamr_mut(), + VMFunctionEnvironment::new(value), + ), + marker: PhantomData, + } + } + + /// Get the data as reference + pub fn as_ref<'a>(&self, store: &'a impl AsStoreRef) -> &'a T + where + T: Any + Send + 'static + Sized, + { + self.handle + .get(store.as_store_ref().objects().as_wamr()) + .as_ref() + .downcast_ref::() + .unwrap() + } + + pub(crate) fn from_handle(handle: StoreHandle) -> Self { + Self { + handle, + marker: PhantomData, + } + } + + /// Get the data as mutable + pub fn as_mut<'a>(&self, store: &'a mut impl AsStoreMut) -> &'a mut T + where + T: Any + Send + 'static + Sized, + { + self.handle + .get_mut(store.objects_mut().as_wamr_mut()) + .as_mut() + .downcast_mut::() + .unwrap() + } + + /// Convert it into a `FunctionEnvMut` + pub fn into_mut(self, store: &mut impl AsStoreMut) -> FunctionEnvMut + where + T: Any + Send + 'static + Sized, + { + FunctionEnvMut { + store_mut: store.as_store_mut(), + func_env: self, + } + } +} + +impl PartialEq for FunctionEnv { + fn eq(&self, other: &Self) -> bool { + self.handle == other.handle + } +} + +impl Eq for FunctionEnv {} + +impl std::hash::Hash for FunctionEnv { + fn hash(&self, state: &mut H) { + self.handle.hash(state); + self.marker.hash(state); + } +} + +impl Clone for FunctionEnv { + fn clone(&self) -> Self { + Self { + handle: self.handle.clone(), + marker: self.marker, + } + } +} + +/// A temporary handle to a [`FunctionEnv`]. +pub struct FunctionEnvMut<'a, T: 'a> { + pub(crate) store_mut: StoreMut<'a>, + pub(crate) func_env: FunctionEnv, +} + +impl<'a, T> Debug for FunctionEnvMut<'a, T> +where + T: Send + Debug + 'static, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.func_env.as_ref(&self.store_mut).fmt(f) + } +} + +impl FunctionEnvMut<'_, T> { + /// Returns a reference to the host state in this function environement. + pub fn data(&self) -> &T { + self.func_env.as_ref(&self.store_mut) + } + + /// Returns a mutable- reference to the host state in this function environement. + pub fn data_mut(&mut self) -> &mut T { + self.func_env.as_mut(&mut self.store_mut) + } + + /// Borrows a new immmutable reference + pub fn as_ref(&self) -> FunctionEnv { + self.func_env.clone() + } + + /// Borrows a new mutable reference + pub fn as_mut(&mut self) -> FunctionEnvMut<'_, T> { + FunctionEnvMut { + store_mut: self.store_mut.as_store_mut(), + func_env: self.func_env.clone(), + } + } + + /// Borrows a new mutable reference of both the attached Store and host state + pub fn data_and_store_mut(&mut self) -> (&mut T, StoreMut) { + let data = self.func_env.as_mut(&mut self.store_mut) as *mut T; + // telling the borrow check to close his eyes here + // this is still relatively safe to do as func_env are + // stored in a specific vec of Store, separate from the other objects + // and not really directly accessible with the StoreMut + let data = unsafe { &mut *data }; + (data, self.store_mut.as_store_mut()) + } +} + +//impl Into> for FunctionEnv { +// fn into(self) -> crate::FunctionEnv { +// crate::FunctionEnv::Wamr(self) +// } +//} + +impl AsStoreRef for FunctionEnvMut<'_, T> { + fn as_store_ref(&self) -> StoreRef<'_> { + StoreRef { + inner: self.store_mut.inner, + } + } +} + +impl AsStoreMut for FunctionEnvMut<'_, T> { + fn as_store_mut(&mut self) -> StoreMut<'_> { + StoreMut { + inner: self.store_mut.inner, + } + } + + fn objects_mut(&mut self) -> &mut crate::StoreObjects { + self.store_mut.objects_mut() + } +} + +impl crate::FunctionEnv { + /// Consume [`self`] into [`crate::rt::wamr::function::env::FunctionEnv`]. + pub fn into_wamr(self) -> FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::Wamr(s) => s, + _ => panic!("Not a `wamr` function env!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::wamr::function::env::FunctionEnv`]. + pub fn as_wamr(&self) -> &FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::Wamr(ref s) => s, + _ => panic!("Not a `wamr` function env!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wamr::function::env::FunctionEnv`]. + pub fn as_wamr_mut(&mut self) -> &mut FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::Wamr(ref mut s) => s, + _ => panic!("Not a `wamr` function env!"), + } + } +} + +impl<'a, T> From> for crate::FunctionEnvMut<'a, T> { + fn from(value: FunctionEnvMut<'a, T>) -> Self { + crate::FunctionEnvMut(crate::RuntimeFunctionEnvMut::Wamr(value)) + } +} + +impl From> for crate::FunctionEnv { + fn from(value: FunctionEnv) -> Self { + crate::FunctionEnv(crate::RuntimeFunctionEnv::Wamr(value)) + } +} diff --git a/lib/api/src/rt/wamr/entities/function/mod.rs b/lib/api/src/rt/wamr/entities/function/mod.rs new file mode 100644 index 00000000000..2f0f0215b69 --- /dev/null +++ b/lib/api/src/rt/wamr/entities/function/mod.rs @@ -0,0 +1,692 @@ +//! Data types, functions and traits for `wamr`'s `Function` implementation. + +#![allow(non_snake_case)] +use std::{ + ffi::c_void, + panic::{self, AssertUnwindSafe}, +}; + +use crate::{ + vm::{VMExtern, VMExternFunction}, + wamr::{ + bindings::*, + utils::convert::{IntoCApiType, IntoCApiValue, IntoWasmerType, IntoWasmerValue}, + vm::{VMFuncRef, VMFunction, VMFunctionCallback, VMFunctionEnvironment}, + }, + AsStoreMut, AsStoreRef, FromToNativeWasmType, FunctionEnv, FunctionEnvMut, IntoResult, + NativeWasmType, NativeWasmTypeInto, RuntimeError, RuntimeFunction, RuntimeFunctionEnvMut, + RuntimeTrap, StoreMut, Value, WasmTypeList, WithEnv, WithoutEnv, +}; + +use super::{super::error::Trap, store::StoreHandle}; +use wasmer_types::{FunctionType, RawValue}; + +pub(crate) mod env; +pub(crate) mod typed; + +pub use typed::*; + +type CCallback = unsafe extern "C" fn( + *mut c_void, + *const wasm_val_vec_t, + *mut wasm_val_vec_t, +) -> *mut wasm_trap_t; + +#[derive(Clone, PartialEq, Eq)] +/// A WebAssembly `function` in `wamr`. +pub struct Function { + pub(crate) handle: VMFunction, +} + +unsafe impl Send for Function {} +unsafe impl Sync for Function {} + +impl From for Function { + fn from(handle: VMFunction) -> Self { + Self { handle } + } +} + +pub(crate) struct FunctionCallbackEnv<'a, F> { + pub(crate) store: StoreMut<'a>, + pub(crate) func: F, + pub(crate) env_handle: Option>, +} + +impl<'a, F> std::fmt::Debug for FunctionCallbackEnv<'a, F> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FunctionCallbackEnv") + .field("env_is_some", &self.env_handle.is_some()) + .finish() + } +} + +impl Function { + /// To `VMExtern`. + pub fn to_vm_extern(&self) -> VMExtern { + let extern_ = unsafe { wasm_func_as_extern(self.handle) }; + assert!( + !extern_.is_null(), + "Returned null Function extern from wasm-c-api" + ); + VMExtern::Wamr(extern_) + } + + #[allow(clippy::cast_ptr_alignment)] + pub fn new_with_env( + store: &mut impl AsStoreMut, + env: &FunctionEnv, + ty: FT, + func: F, + ) -> Self + where + FT: Into, + F: Fn(FunctionEnvMut<'_, T>, &[Value]) -> Result, RuntimeError> + + 'static + + Send + + Sync, + { + let fn_ty: FunctionType = ty.into(); + let params = fn_ty.params(); + + let mut param_types = params + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_param_types = unsafe { + let mut vec = Default::default(); + wasm_valtype_vec_new(&mut vec, param_types.len(), param_types.as_ptr()); + vec + }; + + let results = fn_ty.results(); + let mut result_types = results + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_result_types = unsafe { + let mut vec = Default::default(); + wasm_valtype_vec_new(&mut vec, result_types.len(), result_types.as_ptr()); + vec + }; + + let wasm_functype = unsafe { + wasm_functype_new( + &mut wasm_param_types as *mut _, + &mut wasm_result_types as *mut _, + ) + }; + + let mut store = store.as_store_mut(); + let inner = store.inner.store.as_wamr().inner; + + let callback: CCallback = make_fn_callback(&func, param_types.len()); + + let mut callback_env: *mut FunctionCallbackEnv<'_, F> = + Box::leak(Box::new(FunctionCallbackEnv { + store, + func, + env_handle: Some(env.as_wamr().handle.clone()), + })); + + let wasm_function = unsafe { + wasm_func_new_with_env( + inner, + wasm_functype, + Some(callback), + callback_env as *mut _ as _, + None, + ) + }; + + if wasm_function.is_null() { + panic!("failed when creating new typed function"); + } + + Function { + handle: wasm_function, + } + } + + /// Creates a new host `Function` from a native function. + pub fn new_typed(store: &mut impl AsStoreMut, func: F) -> Self + where + F: crate::HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync, + Args: WasmTypeList, + Rets: WasmTypeList, + { + let mut param_types = Args::wasm_types() + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_param_types = unsafe { + let mut vec = Default::default(); + wasm_valtype_vec_new(&mut vec, param_types.len(), param_types.as_ptr()); + vec + }; + + let mut result_types = Rets::wasm_types() + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_result_types = unsafe { + let mut vec = Default::default(); + wasm_valtype_vec_new(&mut vec, result_types.len(), result_types.as_ptr()); + vec + }; + + let wasm_functype = + unsafe { wasm_functype_new(&mut wasm_param_types, &mut wasm_result_types) }; + + let mut store = store.as_store_mut(); + let inner = store.inner.store.as_wamr().inner; + + let callback: CCallback = unsafe { + std::mem::transmute(func.function_callback(crate::Runtime::Wamr).into_wamr()) + }; + + let mut callback_env: *mut FunctionCallbackEnv<'_, F> = + Box::into_raw(Box::new(FunctionCallbackEnv { + store, + func, + env_handle: None, + })); + + let wasm_function = unsafe { + wasm_func_new_with_env( + inner, + wasm_functype, + Some(callback), + callback_env as _, + None, + ) + }; + + if wasm_function.is_null() { + panic!("failed when creating new typed function"); + } + + Function { + handle: wasm_function, + } + } + + pub fn new_typed_with_env( + store: &mut impl AsStoreMut, + env: &FunctionEnv, + func: F, + ) -> Self + where + F: crate::HostFunction, + Args: WasmTypeList, + Rets: WasmTypeList, + T: Send + 'static, + { + let mut param_types = Args::wasm_types() + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_param_types = unsafe { + let mut vec = wasm_valtype_vec_t::default(); + wasm_valtype_vec_new(&mut vec, param_types.len(), param_types.as_ptr()); + vec + }; + + let mut result_types = Rets::wasm_types() + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_result_types = unsafe { + let mut vec: wasm_valtype_vec_t = Default::default(); + wasm_valtype_vec_new(&mut vec, result_types.len(), result_types.as_ptr()); + vec + }; + + let wasm_functype = unsafe { + wasm_functype_new( + &mut wasm_param_types as *mut _, + &mut wasm_result_types as *mut _, + ) + }; + + let mut store = store.as_store_mut(); + let inner = store.inner.store.as_wamr().inner; + + let callback: CCallback = unsafe { + std::mem::transmute(func.function_callback(crate::Runtime::Wamr).into_wamr()) + }; + + let mut callback_env: *mut FunctionCallbackEnv<'_, F> = + Box::into_raw(Box::new(FunctionCallbackEnv { + store, + func, + env_handle: Some(env.as_wamr().handle.clone()), + })); + + let wasm_function = unsafe { + wasm_func_new_with_env( + inner, + wasm_functype, + Some(callback), + callback_env as _, + None, + ) + }; + + if wasm_function.is_null() { + panic!("failed when creating new typed function"); + } + + Function { + handle: wasm_function, + } + } + + pub fn ty(&self, _store: &impl AsStoreRef) -> FunctionType { + let type_ = unsafe { wasm_func_type(self.handle) }; + let params: *const wasm_valtype_vec_t = unsafe { wasm_functype_params(type_) }; + let returns: *const wasm_valtype_vec_t = unsafe { wasm_functype_results(type_) }; + + let params: Vec = unsafe { + let mut res = vec![]; + for i in 0..(*params).size { + res.push((*(*params).data.wrapping_add(i)).into_wt()); + } + res + }; + + let returns: Vec = unsafe { + let mut res = vec![]; + for i in 0..(*returns).size { + res.push((*(*returns).data.wrapping_add(i)).into_wt()); + } + res + }; + + FunctionType::new(params, returns) + } + + pub fn call_raw( + &self, + _store: &mut impl AsStoreMut, + _params: Vec, + ) -> Result, RuntimeError> { + // There is no optimal call_raw in JSC, so we just + // simply rely the call + // self.call(store, params) + unimplemented!(); + } + + pub fn call( + &self, + store: &mut impl AsStoreMut, + params: &[Value], + ) -> Result, RuntimeError> { + // unimplemented!(); + let store_mut = store.as_store_mut(); + // let wasm_func_param_arity(self.handle) + + let mut args = { + unsafe { + let mut wasm_params = params + .into_iter() + .map(|v| 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 = { + unsafe { + let mut vec = Default::default(); + wasm_val_vec_new_uninitialized(&mut vec, size); + vec + } + }; + + let trap = unsafe { wasm_func_call(self.handle, &mut args as _, &mut results as *mut _) }; + + if !trap.is_null() { + return Err(Into::::into(trap).into()); + } + + unsafe { + let results = std::ptr::slice_from_raw_parts(results.data, results.size); + return Ok((*results) + .into_iter() + .map(|v| IntoWasmerValue::into_wv(*v)) + .collect::>() + .into_boxed_slice()); + } + } + + pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, internal: VMExternFunction) -> Self { + Self { + handle: internal.into_wamr(), + } + } + + pub(crate) fn vm_funcref(&self, _store: &impl AsStoreRef) -> VMFuncRef { + unimplemented!(); + } + + pub(crate) unsafe fn from_vm_funcref( + _store: &mut impl AsStoreMut, + _funcref: VMFuncRef, + ) -> Self { + unimplemented!(); + } + + /// Checks whether this `Function` can be used with the given context. + pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} + +fn make_fn_callback(func: &F, args: usize) -> CCallback +where + F: Fn(FunctionEnvMut<'_, T>, &[Value]) -> Result, RuntimeError> + + 'static + + Send + + Sync, +{ + unsafe extern "C" fn fn_callback( + env: *mut c_void, + args: *const wasm_val_vec_t, + rets: *mut wasm_val_vec_t, + ) -> *mut wasm_trap_t + where + F: Fn(FunctionEnvMut<'_, T>, &[Value]) -> Result, RuntimeError> + + 'static + + Send + + Sync, + { + let r: *mut (FunctionCallbackEnv<'_, F>) = env as _; + + let mut store = (*r).store.as_store_mut(); + let env_handle = (*r).env_handle.as_ref().unwrap().clone(); + let mut fn_env = env::FunctionEnv::from_handle(env_handle).into_mut(&mut store); + let func: &F = &(*r).func; + + let mut wasmer_args = vec![]; + + for i in 0..(*args).size { + wasmer_args.push((*(*args).data.wrapping_add(i)).into_wv().clone()); + } + + let result = panic::catch_unwind(AssertUnwindSafe(|| unsafe { + func(fn_env.into(), wasmer_args.as_slice()) + })); + + match result { + Ok(Ok(native_results)) => { + let mut c_results: Vec = native_results + .into_iter() + .map(IntoCApiValue::into_cv) + .collect(); + + if c_results.len() != (*rets).size { + panic!("when calling host function: number of observed results differ from wanted results") + } + + unsafe { + for i in 0..(*rets).size { + *((*rets).data.wrapping_add(i)) = c_results[i] + } + } + + unsafe { std::ptr::null_mut() } + } + + Ok(Err(e)) => { + let trap: Trap = Trap::user(Box::new(e)); + unsafe { trap.into_wasm_trap(&mut store) } + } + + Err(e) => { + unimplemented!("host function panicked"); + } + } + } + + return fn_callback::; +} + +impl std::fmt::Debug for Function { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.debug_struct("Function").finish() + } +} + +impl crate::Function { + /// Consume [`self`] into [`crate::rt::wamr::function::Function`]. + pub fn into_wamr(self) -> crate::rt::wamr::function::Function { + match self.0 { + RuntimeFunction::Wamr(s) => s, + _ => panic!("Not a `wamr` function!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::wamr::function::Function`]. + pub fn as_wamr(&self) -> &crate::rt::wamr::function::Function { + match self.0 { + RuntimeFunction::Wamr(ref s) => s, + _ => panic!("Not a `wamr` function!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wamr::function::Function`]. + pub fn as_wamr_mut(&mut self) -> &mut crate::rt::wamr::function::Function { + match self.0 { + RuntimeFunction::Wamr(ref mut s) => s, + _ => panic!("Not a `wamr` function!"), + } + } +} + +macro_rules! impl_host_function { + ([$c_struct_representation:ident] $c_struct_name:ident, $( $x:ident ),* ) => { + paste::paste! { + #[allow(non_snake_case)] + pub(crate) fn [] + <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, Func: Fn($( $x , )*) -> RetsAsResult + 'static> + (this: &Func) -> crate::rt::wamr::vm::VMFunctionCallback { + + /// This is a function that wraps the real host + /// function. Its address will be used inside the + /// runtime. + unsafe extern "C" fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func>(env: *mut c_void, args: *const wasm_val_vec_t, results: *mut wasm_val_vec_t) -> *mut wasm_trap_t + where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + RetsAsResult: IntoResult, + Func: Fn($( $x , )*) -> RetsAsResult + 'static, + { + let mut r: *mut crate::rt::wamr::function::FunctionCallbackEnv = unsafe {std::mem::transmute(env)}; + let store = &mut (*r).store.as_store_mut(); + let mut i = 0; + + $( + let c_arg = (*(*args).data.wrapping_add(i)).clone(); + let wasmer_arg = c_arg.into_wv(); + let raw_arg : RawValue = wasmer_arg.as_raw(store); + let $x : $x = FromToNativeWasmType::from_native($x::Native::from_raw(store, raw_arg)); + + i += 1; + )* + + let result = panic::catch_unwind(AssertUnwindSafe(|| unsafe { + ((*r).func)( $( $x, )* ).into_result() + })); + + match result { + Ok(Ok(result)) => { + + let types = Rets::wasm_types(); + let mut native_results = result.into_array(store); + let native_results = native_results.as_mut(); + + let native_results: Vec = native_results.into_iter().enumerate() + .map(|(i, r)| Value::from_raw(store, types[i], r.clone())) + .collect(); + + let mut c_results: Vec = native_results.into_iter().map(IntoCApiValue::into_cv).collect(); + + if c_results.len() != (*results).size { + panic!("when calling host function: number of observed results differ from wanted results") + } + + unsafe { + for i in 0..(*results).size { + *((*results).data.wrapping_add(i)) = c_results[i] + } + } + + unsafe { std::ptr::null_mut() } + }, + + Ok(Err(e)) => { + let trap = crate::rt::wamr::error::Trap::user(Box::new(e)); + unsafe { trap.into_wasm_trap(store) } + // unimplemented!("host function panicked"); + }, + + Err(e) => { + unimplemented!("host function panicked"); + } + } + } + func_wrapper::< $( $x, )* Rets, RetsAsResult, Func> as _ + } + + + #[allow(non_snake_case)] + pub(crate) fn [] + <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, T: Send + 'static, Func: Fn(FunctionEnvMut, $( $x , )*) -> RetsAsResult + 'static> + (this: &Func) -> crate::rt::wamr::vm::VMFunctionCallback { + unsafe extern "C" fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func, T>(env: *mut c_void, args: *const wasm_val_vec_t, results: *mut wasm_val_vec_t) -> *mut wasm_trap_t + where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + RetsAsResult: IntoResult, + T: Send + 'static, + Func: Fn(FunctionEnvMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static, + { + + let r: *mut (crate::rt::wamr::function::FunctionCallbackEnv<'_, Func>) = env as _; + let store = &mut (*r).store.as_store_mut(); + + let mut i = 0; + + $( + let c_arg = (*(*args).data.wrapping_add(i)).clone(); + let wasmer_arg = c_arg.into_wv(); + let raw_arg : RawValue = wasmer_arg.as_raw(store); + let $x : $x = FromToNativeWasmType::from_native($x::Native::from_raw(store, raw_arg)); + + i += 1; + )* + + let env_handle = (*r).env_handle.as_ref().unwrap().clone(); + let mut fn_env = crate::rt::wamr::function::env::FunctionEnv::from_handle(env_handle).into_mut(store); + let func: &Func = &(*r).func; + + let result = panic::catch_unwind(AssertUnwindSafe(|| unsafe { + ((*r).func)(RuntimeFunctionEnvMut::Wamr(fn_env).into(), $( $x, )* ).into_result() + })); + + + match result { + Ok(Ok(result)) => { + let types = Rets::wasm_types(); + let mut native_results = result.into_array(store); + let native_results = native_results.as_mut(); + + let native_results: Vec = native_results.into_iter().enumerate().map(|(i, r)| Value::from_raw(store, types[i], r.clone())).collect(); + + let mut c_results: Vec = native_results.into_iter().map(IntoCApiValue::into_cv).collect(); + + if c_results.len() != (*results).size { + panic!("when calling host function: number of observed results differ from wanted results") + } + + unsafe { + for i in 0..(*results).size { + *((*results).data.wrapping_add(i)) = c_results[i] + } + + } + + unsafe { std::ptr::null_mut() } + }, + + Ok(Err(e)) => { let trap = crate::rt::wamr::error::Trap::user(Box::new(e)); unsafe { trap.into_wasm_trap(store) } }, + + Err(e) => { unimplemented!("host function panicked"); } + } + } + + func_wrapper::< $( $x, )* Rets, RetsAsResult, Func, T> as _ + } + } + }; +} + +// Here we go! Let's generate all the C struct, `WasmTypeList` +// implementations and `HostFunction` implementations. +impl_host_function!([C] S0,); +impl_host_function!([transparent] S1, A1); +impl_host_function!([C] S2, A1, A2); +impl_host_function!([C] S3, A1, A2, A3); +impl_host_function!([C] S4, A1, A2, A3, A4); +impl_host_function!([C] S5, A1, A2, A3, A4, A5); +impl_host_function!([C] S6, A1, A2, A3, A4, A5, A6); +impl_host_function!([C] S7, A1, A2, A3, A4, A5, A6, A7); +impl_host_function!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8); +impl_host_function!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9); +impl_host_function!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); +impl_host_function!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); +impl_host_function!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); +impl_host_function!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); +impl_host_function!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); +impl_host_function!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); +impl_host_function!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16); +impl_host_function!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17); +impl_host_function!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18); +impl_host_function!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19); +impl_host_function!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20); +impl_host_function!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21); +impl_host_function!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22); +impl_host_function!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23); +impl_host_function!([C] S24, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24); +impl_host_function!([C] S25, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25); +impl_host_function!([C] S26, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26); diff --git a/lib/api/src/c_api/typed_function.rs b/lib/api/src/rt/wamr/entities/function/typed.rs similarity index 65% rename from lib/api/src/c_api/typed_function.rs rename to lib/api/src/rt/wamr/entities/function/typed.rs index aae515d3c3c..720a1d08791 100644 --- a/lib/api/src/c_api/typed_function.rs +++ b/lib/api/src/rt/wamr/entities/function/typed.rs @@ -7,21 +7,14 @@ //! let add_one = instance.exports.get_function("function_name")?; //! let add_one_native: TypedFunction = add_one.typed().unwrap(); //! ``` -use crate::as_c::result_to_value; -use crate::bindings::{ - wasm_byte_vec_t, wasm_extern_as_func, wasm_func_call, wasm_func_t, wasm_global_set, - wasm_global_t, wasm_trap_message, wasm_val_vec_new, wasm_val_vec_new_uninitialized, -}; -use crate::as_c::param_from_c; -use crate::c_api::externals::function::Function; -use crate::c_api::trap::Trap; -use crate::native_type::NativeWasmTypeInto; -use crate::Value; -use crate::{AsStoreMut, TypedFunction}; -use crate::{FromToNativeWasmType, RuntimeError, WasmTypeList}; -// use std::panic::{catch_unwind, AssertUnwindSafe}; use std::iter::FromIterator; + +use crate::{ + rt::wamr::{bindings::*, error::Trap, function::Function, utils::convert::*}, + AsStoreMut, FromToNativeWasmType, NativeWasmType, NativeWasmTypeInto, RuntimeError, + TypedFunction, Value, WasmTypeList, +}; use wasmer_types::RawValue; macro_rules! impl_native_traits { @@ -34,56 +27,38 @@ macro_rules! impl_native_traits { { /// Call the typed func and return results. #[allow(clippy::too_many_arguments)] - pub fn call(&self, mut store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result where - $( $x: FromToNativeWasmType + NativeWasmTypeInto, )* + pub fn call_wamr(&self, mut store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result where + $( $x: FromToNativeWasmType, )* { // // let store_ptr = Value::I64(store.as_store_mut().as_raw() as _).as_jsvalue(store); #[allow(unused_unsafe)] let params_list: Vec<_> = unsafe { vec![ $( { - let raw = $x.into_raw(store); - let value = Value::from_raw(&mut store, $x::WASM_TYPE, raw); - result_to_value(&value) + 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() } ),* ] }; let mut results = { - #[cfg(any(feature = "wamr", feature = "wasmi"))] unsafe { let mut ret = std::mem::zeroed(); wasm_val_vec_new_uninitialized(&mut ret, Rets::wasm_types().len()); ret } - - #[cfg(feature = "v8")] - unsafe { - - let rets_len = Rets::wasm_types().len(); - let mut vec = Vec::::with_capacity(rets_len); - let ptr = vec.as_mut_ptr(); - std::mem::forget(vec); - ptr as *mut _ - - } }; - let func = unsafe { wasm_extern_as_func(self.func.to_vm_extern()) }; + let func = unsafe { wasm_extern_as_func(self.func.to_vm_extern().into_wamr()) }; let trap = { - #[cfg(any(feature = "wamr", feature = "wasmi"))] unsafe { let mut params = std::mem::zeroed(); wasm_val_vec_new(&mut params, params_list.len(), params_list.as_ptr()); wasm_func_call(func, ¶ms, &mut results) } - - #[cfg(feature = "v8")] - unsafe { - wasm_func_call(func, params_list.as_ptr() as *const _, results) - } - }; + }; if !trap.is_null() { unsafe { @@ -92,18 +67,6 @@ macro_rules! impl_native_traits { } } - #[cfg(feature = "v8")] - unsafe { - let rets_len = Rets::wasm_types().len(); - let mut results: *const [crate::bindings::wasm_val_t] = std::ptr::slice_from_raw_parts(results, rets_len); - - unsafe { - let results: Vec<_> = (*results).into_iter().map(|v| param_from_c(&v).as_raw(&mut store)).collect(); - Ok(unsafe {Rets::from_slice(store, &results).unwrap()}) - } - } - - #[cfg(any(feature = "wamr", feature = "wasmi"))] unsafe { let mut rets_list_array = Rets::empty_array(); let mut_rets = rets_list_array.as_mut() as *mut [RawValue] as *mut RawValue; @@ -112,13 +75,13 @@ macro_rules! impl_native_traits { 0 => {}, 1 => { let val = (*results.data.wrapping_add(0)).clone(); - let val = param_from_c(&val); + let val = val.into_wv(); *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 = param_from_c(&val); + let val = val.into_wv(); let slot = mut_rets.add(i); *slot = val.as_raw(&mut store); } @@ -128,8 +91,17 @@ macro_rules! impl_native_traits { Ok(unsafe { Rets::from_array(store, rets_list_array) }) } } + + #[doc(hidden)] + #[allow(missing_docs)] + #[allow(unused_mut)] + #[allow(clippy::too_many_arguments)] + pub fn call_raw_wamr(&self, store: &mut impl AsStoreMut, mut params_list: Vec ) -> Result { + todo!("Raw calls from wamr are not supported yet!") + } } }; + } impl_native_traits!(); diff --git a/lib/api/src/c_api/externals/global.rs b/lib/api/src/rt/wamr/entities/global.rs similarity index 64% rename from lib/api/src/c_api/externals/global.rs rename to lib/api/src/rt/wamr/entities/global.rs index e3a08948e5d..51ae7c87ae8 100644 --- a/lib/api/src/c_api/externals/global.rs +++ b/lib/api/src/rt/wamr/entities/global.rs @@ -1,22 +1,22 @@ -use std::ptr; - -use crate::as_c::{self, param_from_c, result_to_value, type_to_c, valtype_to_type}; -use crate::bindings::{ - wasm_frame_copy, wasm_global_get, wasm_global_new, wasm_global_set, wasm_global_type, - wasm_globaltype_content, wasm_globaltype_mutability, wasm_globaltype_new, - wasm_mutability_enum_WASM_CONST, wasm_mutability_enum_WASM_VAR, wasm_mutability_t, wasm_val_t, - wasm_val_t__bindgen_ty_1, wasm_valtype_new, +//! Data types, functions and traits for `wamr`'s `Global` implementation. +use wasmer_types::{GlobalType, Mutability}; + +use crate::{ + vm::{VMExtern, VMExternGlobal}, + wamr::{ + bindings::{ + self, wasm_global_as_extern, wasm_global_get, wasm_global_new, wasm_global_set, + wasm_global_type, wasm_globaltype_content, wasm_globaltype_mutability, + wasm_globaltype_new, wasm_mutability_t, wasm_valtype_new, + }, + utils::convert::{IntoCApiType, IntoCApiValue, IntoWasmerType, IntoWasmerValue}, + vm::VMGlobal, + }, + AsStoreMut, AsStoreRef, RuntimeError, Value, }; -use crate::c_api::bindings::wasm_global_as_extern; -use crate::c_api::vm::{VMExtern, VMGlobal}; -use crate::errors::RuntimeError; -use crate::store::{AsStoreMut, AsStoreRef}; -use crate::value::Value; -use crate::GlobalType; -use crate::Mutability; -use wasmer_types::{RawValue, Type}; - -#[derive(Debug, Clone, PartialEq)] + +#[derive(Debug, Clone, PartialEq, Eq)] +/// A WebAssembly `global` in `wamr`. pub struct Global { pub(crate) handle: VMGlobal, } @@ -35,7 +35,7 @@ impl Global { !extern_.is_null(), "Returned null Global extern from wasm-c-api" ); - extern_ + VMExtern::Wamr(extern_) } /// Create a `Global` with the initial value [`Value`] and the provided [`Mutability`]. @@ -46,12 +46,12 @@ impl Global { ) -> Result { let store = store.as_store_mut(); - let wamr_value = result_to_value(&val); - let wamr_type = type_to_c(&val.ty()); + let wamr_type = val.ty().into_ct(); + let wamr_value = val.into_cv(); let wamr_mutability = if mutability.is_mutable() { - wasm_mutability_enum_WASM_VAR + bindings::wasm_mutability_enum_WASM_VAR } else { - wasm_mutability_enum_WASM_CONST + bindings::wasm_mutability_enum_WASM_CONST } as wasm_mutability_t; let wamr_global_type = @@ -59,7 +59,11 @@ impl Global { Ok(Self { handle: unsafe { - wasm_global_new(store.inner.store.inner, wamr_global_type, &wamr_value) + wasm_global_new( + store.inner.store.as_wamr().inner, + wamr_global_type, + &wamr_value, + ) }, }) } @@ -68,11 +72,11 @@ impl Global { let r#type = unsafe { wasm_global_type(self.handle) }; let mutability = unsafe { wasm_globaltype_mutability(&*r#type) }; let valtype = unsafe { wasm_globaltype_content(r#type) }; - let wasmer_type = valtype_to_type(valtype); + let wasmer_type = valtype.into_wt(); GlobalType::new( wasmer_type, - if mutability == wasm_mutability_enum_WASM_VAR as u8 { + if mutability == bindings::wasm_mutability_enum_WASM_VAR as u8 { Mutability::Var } else { Mutability::Const @@ -83,7 +87,7 @@ impl Global { pub fn get(&self, store: &mut impl AsStoreMut) -> Value { let mut out = unsafe { std::mem::zeroed() }; unsafe { wasm_global_get(self.handle, &mut out) }; - param_from_c(&out) + out.into_wv() } pub fn set(&self, store: &mut impl AsStoreMut, val: Value) -> Result<(), RuntimeError> { @@ -99,15 +103,17 @@ impl Global { return Err(RuntimeError::new("The global is immutable".to_owned())); } - let value = result_to_value(&val); + let value = val.into_cv(); unsafe { wasm_global_set(self.handle, &value) }; Ok(()) } - pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_global: VMGlobal) -> Self { - Self { handle: vm_global } + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_global: VMExternGlobal) -> Self { + Self { + handle: vm_global.into_wamr(), + } } pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { diff --git a/lib/api/src/rt/wamr/entities/instance.rs b/lib/api/src/rt/wamr/entities/instance.rs new file mode 100644 index 00000000000..7454e8c1ee2 --- /dev/null +++ b/lib/api/src/rt/wamr/entities/instance.rs @@ -0,0 +1,161 @@ +//! Data types, functions and traits for `wamr`'s `Instance` implementation. +use std::sync::Arc; + +use crate::{ + rt::wamr::bindings::*, vm::VMExtern, wamr::error::Trap, AsStoreMut, AsStoreRef, Exports, + Extern, Imports, InstantiationError, Module, +}; + +#[derive(PartialEq, Eq)] +pub(crate) struct InstanceHandle(pub(crate) *mut wasm_instance_t); + +unsafe impl Send for InstanceHandle {} +unsafe impl Sync for InstanceHandle {} + +impl InstanceHandle { + fn new( + store: *mut wasm_store_t, + module: *mut wasm_module_t, + mut externs: Vec, + ) -> Result { + // Check if the thread env was already initialised. + //unsafe { + // if !wasm_runtime_thread_env_inited() { + // if !wasm_runtime_init_thread_env() { + // panic!("Failed to initialize the thread environment!"); + // } + // } + //} + + let mut trap: *mut wasm_trap_t = std::ptr::null_mut() as _; + let externs: Vec<_> = externs.into_iter().map(|v| v.into_wamr()).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); + + let stack_size = 2 * 1024 * 1024; + let heap_size = 2 * 1024 * 1024; + + wasm_instance_new_with_args( + store, + module, + &mut imports, + &mut trap, + stack_size, + heap_size, + ) + }; + + if instance.is_null() { + let trap = Trap::from(trap); + return Err(InstantiationError::Start(trap.into())); + } + + Ok(InstanceHandle(instance)) + } + + fn get_exports(&self, mut store: &mut impl AsStoreMut, module: &Module) -> Exports { + let mut exports = unsafe { + let mut vec = Default::default(); + wasm_instance_exports(self.0, &mut vec); + vec + }; + + let wasm_exports: &[*mut wasm_extern_t] = + unsafe { std::slice::from_raw_parts(exports.data, exports.size) }; + + let exports_ty = module.exports().collect::>(); + let exports = exports_ty + .iter() + .zip(wasm_exports.into_iter()) + .map(|(export_type, wasm_export)| { + let name = export_type.name(); + let mut store = store.as_store_mut(); + let extern_type = export_type.ty(); + // Annotation is here to prevent spurious IDE warnings. + + let extern_ = Extern::from_vm_extern(&mut store, VMExtern::Wamr(*wasm_export)); + (name.to_string(), extern_) + }) + .collect::(); + exports + } +} +impl Drop for InstanceHandle { + fn drop(&mut self) { + unsafe { wasm_instance_delete(self.0) } + } +} + +#[derive(Clone, PartialEq, Eq)] +/// A WebAssembly `instance` in `wamr`. +pub struct Instance { + pub(crate) handle: Arc, +} + +impl Instance { + pub(crate) fn new( + store: &mut impl AsStoreMut, + module: &Module, + imports: &Imports, + ) -> Result<(Self, Exports), InstantiationError> { + let externs = module + .imports() + .map(|import_ty| { + imports + .get_export(import_ty.module(), import_ty.name()) + .expect("Extern not found") + }) + .collect::>(); + + _ = store; + // Hacky: we need to tie a *module* to a store before instantiating it.. + let wamr_module = module.as_wamr(); + let mut store_from_module = wamr_module.handle.store.lock().unwrap(); + let mut store = store_from_module.as_store_mut(); + + return Self::new_by_index(&mut store, module, &externs); + } + + pub(crate) fn new_by_index( + store: &mut impl AsStoreMut, + module: &Module, + externs: &[Extern], + ) -> Result<(Self, Exports), InstantiationError> { + let store_ref = store.as_store_ref(); + let externs: Vec = externs + .iter() + .map(|extern_| extern_.to_vm_extern()) + .collect::>(); + + let instance = InstanceHandle::new( + store_ref.inner.store.as_wamr().inner, + module.as_wamr().handle.inner, + externs, + )?; + let exports = instance.get_exports(store, module); + + Ok(( + Self { + handle: Arc::new(instance), + }, + exports, + )) + } +} + +impl crate::RuntimeInstance { + /// Consume [`self`] into a [`crate::rt::wamr::instance::Instance`]. + pub(crate) fn into_wamr(self) -> crate::rt::wamr::instance::Instance { + match self { + Self::Wamr(s) => s, + _ => panic!("Not a `wamr` instance"), + } + } +} diff --git a/lib/api/src/rt/wamr/entities/memory/mod.rs b/lib/api/src/rt/wamr/entities/memory/mod.rs new file mode 100644 index 00000000000..b99fdc864f4 --- /dev/null +++ b/lib/api/src/rt/wamr/entities/memory/mod.rs @@ -0,0 +1,345 @@ +//! Data types, functions and traits for `wamr`'s `Memory` implementation. +//! +use std::{marker::PhantomData, mem::MaybeUninit}; + +use tracing::warn; +pub use wasmer_types::MemoryError; +use wasmer_types::{MemoryType, Pages, WASM_PAGE_SIZE}; + +use crate::{ + shared::SharedMemory, + vm::{VMExtern, VMExternMemory}, + wamr::{ + bindings::{ + wasm_limits_max_default, wasm_limits_t, wasm_memory_as_extern, wasm_memory_copy, + wasm_memory_new, wasm_memory_type, wasm_memorytype_limits, wasm_memorytype_new, + wasm_memorytype_t, + }, + vm::VMMemory, + }, + AsStoreMut, AsStoreRef, MemoryAccessError, RuntimeMemory, +}; + +pub(crate) mod view; +pub use view::*; + +#[derive(Debug, Clone)] +/// A WebAssembly `memory` in `wamr`. +pub struct Memory { + pub(crate) handle: VMMemory, +} + +unsafe impl Send for Memory {} +unsafe impl Sync for Memory {} + +impl Memory { + pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result { + let limits = Box::into_raw(Box::new(wasm_limits_t { + min: ty.minimum.0, + max: match ty.maximum { + Some(v) => v.0, + None => wasm_limits_max_default, + }, + })); + + let memorytype = unsafe { wasm_memorytype_new(limits) }; + + let mut store = store.as_store_mut(); + let inner = store.inner.store.as_wamr().inner; + let c_memory = unsafe { wasm_memory_new(inner, memorytype) }; + + Ok(Self { handle: c_memory }) + } + + pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { + Self { handle: memory } + } + + pub(crate) fn to_vm_extern(&self) -> VMExtern { + VMExtern::Wamr(unsafe { wasm_memory_as_extern(self.handle) }) + } + + pub fn ty(&self, _store: &impl AsStoreRef) -> MemoryType { + let memory_type: *mut wasm_memorytype_t = unsafe { wasm_memory_type(self.handle) }; + let limits: *const wasm_limits_t = unsafe { wasm_memorytype_limits(memory_type) }; + + MemoryType { + // [TODO]: Find a way to extract this from the inner memory type instead + // of hardcoding. + shared: true, + minimum: unsafe { wasmer_types::Pages((*limits).min) }, + maximum: unsafe { Some(wasmer_types::Pages((*limits).max)) }, + } + } + + pub fn view<'a>(&self, store: &'a impl AsStoreRef) -> MemoryView<'a> { + MemoryView::new(self, store) + } + + // Note: the return value is the memory size (in [`Pages`]) *before* growing it. + pub fn grow( + &self, + store: &mut impl AsStoreMut, + delta: IntoPages, + ) -> Result + where + IntoPages: Into, + { + unimplemented!( + "calling grow from host is not supported! Use the memory.grow opcode instead." + ); + } + + pub fn grow_at_least( + &self, + store: &mut impl AsStoreMut, + min_size: u64, + ) -> Result<(), MemoryError> { + unimplemented!( + "calling grow from host is not supported! Use the memory.grow opcode instead." + ); + } + + pub fn reset(&self, _store: &mut impl AsStoreMut) -> Result<(), MemoryError> { + Ok(()) + } + + pub fn copy_to_store( + &self, + store: &impl AsStoreRef, + new_store: &mut impl AsStoreMut, + ) -> Result { + unimplemented!(); + // let view = self.view(store); + // let ty = self.ty(store); + // let amount = view.data_size() as usize; + + // let new_memory = Self::new(new_store, ty)?; + // let mut new_view = new_memory.view(&new_store); + // let new_view_size = new_view.data_size() as usize; + // if amount > new_view_size { + // let delta = amount - new_view_size; + // let pages = ((delta - 1) / wasmer_types::WASM_PAGE_SIZE) + 1; + // new_memory.grow(new_store, Pages(pages as u32))?; + // new_view = new_memory.view(&new_store); + // } + + // // Copy the bytes + // view.copy_to_memory(amount as u64, &new_view) + // .map_err(|err| MemoryError::Generic(err.to_string()))?; + // // // Return the new memory + // Ok(new_memory) + } + + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, internal: VMExternMemory) -> Self { + Self { + handle: internal.into_wamr(), + } + } + + /// Cloning memory will create another reference to the same memory that + /// can be put into a new store + pub fn try_clone(&self, _store: &impl AsStoreRef) -> Result { + Ok(self.handle.clone()) + } + + /// Copying the memory will actually copy all the bytes in the memory to + /// a identical byte copy of the original that can be put into a new store + pub fn try_copy(&self, store: &impl AsStoreRef) -> Result { + let res = unsafe { wasm_memory_copy(self.handle) }; + if res.is_null() { + Err(MemoryError::Generic("memory copy failed".to_owned())) + } else { + Ok(res) + } + } + + pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } + + #[allow(unused)] + pub fn duplicate(&mut self, store: &impl AsStoreRef) -> Result { + unimplemented!(); + // self.handle.duplicate(store) + } + + pub fn as_shared(&self, _store: &impl AsStoreRef) -> Option { + // Not supported. + None + } +} + +impl std::cmp::PartialEq for Memory { + fn eq(&self, other: &Self) -> bool { + self.handle == other.handle + } +} + +impl std::cmp::Eq for Memory {} + +/// Underlying buffer for a memory. +#[derive(Debug, Copy, Clone)] +pub(crate) struct MemoryBuffer<'a> { + pub(crate) base: *mut u8, + pub(crate) len: usize, + pub(crate) marker: PhantomData<&'a MemoryView<'a>>, +} + +impl<'a> MemoryBuffer<'a> { + pub(crate) fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> { + let end = offset + .checked_add(buf.len() as u64) + .ok_or(MemoryAccessError::Overflow)?; + + let len: u64 = self.len.try_into().unwrap(); + if end > len { + warn!( + "attempted to read {} bytes, but the end offset is beyond the bounds of the memory view ({} > {}, diff. {} bytes)", + buf.len(), + end, + len, + end - len, + ); + return Err(MemoryAccessError::HeapOutOfBounds); + } + unsafe { + volatile_memcpy_read(self.base.add(offset as usize), buf.as_mut_ptr(), buf.len()); + } + Ok(()) + } + + pub(crate) fn read_uninit<'b>( + &self, + offset: u64, + buf: &'b mut [MaybeUninit], + ) -> Result<&'b mut [u8], MemoryAccessError> { + let end = offset + .checked_add(buf.len() as u64) + .ok_or(MemoryAccessError::Overflow)?; + + let len: u64 = self.len.try_into().unwrap(); + if end > len { + warn!( + "attempted to read {} bytes, but the end offset is beyond the bounds of the memory view ({} > {}, diff. {} bytes)", + buf.len(), + end, + len, + end - len, + ); + return Err(MemoryAccessError::HeapOutOfBounds); + } + let buf_ptr = buf.as_mut_ptr() as *mut u8; + unsafe { + volatile_memcpy_read(self.base.add(offset as usize), buf_ptr, buf.len()); + } + + Ok(unsafe { std::slice::from_raw_parts_mut(buf_ptr, buf.len()) }) + } + + pub(crate) fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { + let end = offset + .checked_add(data.len() as u64) + .ok_or(MemoryAccessError::Overflow)?; + if end > self.len.try_into().unwrap() { + warn!( + "attempted to write ({} bytes) beyond the bounds of the memory view ({} > {})", + data.len(), + end, + self.len + ); + return Err(MemoryAccessError::HeapOutOfBounds); + } + unsafe { + volatile_memcpy_write(data.as_ptr(), self.base.add(offset as usize), data.len()); + } + Ok(()) + } +} + +// We can't use a normal memcpy here because it has undefined behavior if the +// memory is being concurrently modified. So we need to write our own memcpy +// implementation which uses volatile operations. +// +// The implementation of these functions can optimize very well when inlined +// with a fixed length: they should compile down to a single load/store +// instruction for small (8/16/32/64-bit) copies. +#[inline] +unsafe fn volatile_memcpy_read(mut src: *const u8, mut dst: *mut u8, mut len: usize) { + #[inline] + unsafe fn copy_one(src: &mut *const u8, dst: &mut *mut u8, len: &mut usize) { + #[repr(packed)] + struct Unaligned(T); + let val = (*src as *const Unaligned).read_volatile(); + (*dst as *mut Unaligned).write(val); + *src = src.add(std::mem::size_of::()); + *dst = dst.add(std::mem::size_of::()); + *len -= std::mem::size_of::(); + } + + while len >= 8 { + copy_one::(&mut src, &mut dst, &mut len); + } + if len >= 4 { + copy_one::(&mut src, &mut dst, &mut len); + } + if len >= 2 { + copy_one::(&mut src, &mut dst, &mut len); + } + if len >= 1 { + copy_one::(&mut src, &mut dst, &mut len); + } +} + +#[inline] +unsafe fn volatile_memcpy_write(mut src: *const u8, mut dst: *mut u8, mut len: usize) { + #[inline] + unsafe fn copy_one(src: &mut *const u8, dst: &mut *mut u8, len: &mut usize) { + #[repr(packed)] + struct Unaligned(T); + let val = (*src as *const Unaligned).read(); + (*dst as *mut Unaligned).write_volatile(val); + *src = src.add(std::mem::size_of::()); + *dst = dst.add(std::mem::size_of::()); + *len -= std::mem::size_of::(); + } + + while len >= 8 { + copy_one::(&mut src, &mut dst, &mut len); + } + if len >= 4 { + copy_one::(&mut src, &mut dst, &mut len); + } + if len >= 2 { + copy_one::(&mut src, &mut dst, &mut len); + } + if len >= 1 { + copy_one::(&mut src, &mut dst, &mut len); + } +} + +impl crate::Memory { + /// Consume [`self`] into a [`crate::rt::wamr::memory::Memory`]. + pub fn into_wamr(self) -> crate::rt::wamr::memory::Memory { + match self.0 { + RuntimeMemory::Wamr(s) => s, + _ => panic!("Not a `wamr` memory!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::wamr::memory::Memory`]. + pub fn as_wamr(&self) -> &crate::rt::wamr::memory::Memory { + match self.0 { + RuntimeMemory::Wamr(ref s) => s, + _ => panic!("Not a `wamr` memory!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference to [`crate::rt::wamr::memory::Memory`]. + pub fn as_wamr_mut(&mut self) -> &mut crate::rt::wamr::memory::Memory { + match self.0 { + RuntimeMemory::Wamr(ref mut s) => s, + _ => panic!("Not a `wamr` memory!"), + } + } +} diff --git a/lib/api/src/externals/memory_view.rs b/lib/api/src/rt/wamr/entities/memory/view.rs similarity index 78% rename from lib/api/src/externals/memory_view.rs rename to lib/api/src/rt/wamr/entities/memory/view.rs index 4745074d660..c91d67cdf45 100644 --- a/lib/api/src/externals/memory_view.rs +++ b/lib/api/src/rt/wamr/entities/memory/view.rs @@ -1,31 +1,42 @@ -use super::memory::{Memory, MemoryBuffer}; -use crate::store::AsStoreRef; -use crate::MemoryAccessError; -use std::mem::MaybeUninit; -use std::ops::Range; +use std::{marker::PhantomData, mem::MaybeUninit, ops::Range}; + use wasmer_types::Pages; -#[cfg(feature = "wasm-c-api")] -use crate::c_api::externals::memory_view as memory_view_impl; -#[cfg(feature = "js")] -use crate::js::externals::memory_view as memory_view_impl; -#[cfg(feature = "jsc")] -use crate::jsc::externals::memory_view as memory_view_impl; -#[cfg(feature = "sys")] -use crate::sys::externals::memory_view as memory_view_impl; +use super::{Memory, MemoryBuffer}; +use crate::{ + rt::wamr::bindings::{wasm_memory_data, wasm_memory_data_size, wasm_memory_size}, + wamr::bindings::wasm_memory_t, + AsStoreRef, MemoryAccessError, +}; /// A WebAssembly `memory` view. /// /// A memory view is used to read and write to the linear memory. /// /// After a memory is grown a view must not be used anymore. Views are -/// created using the Memory.view() method. +/// created using the Memory.grow() method. #[derive(Debug)] -pub struct MemoryView<'a>(pub(crate) memory_view_impl::MemoryView<'a>); +pub struct MemoryView<'a> { + pub(crate) buffer: MemoryBuffer<'a>, + pub(crate) size: u32, +} impl<'a> MemoryView<'a> { pub(crate) fn new(memory: &Memory, store: &'a (impl AsStoreRef + ?Sized)) -> Self { - MemoryView(memory_view_impl::MemoryView::new(&memory.0, store)) + let c_memory: *mut wasm_memory_t = memory.handle; + + let len = unsafe { wasm_memory_data_size(c_memory as _).try_into().unwrap() }; + let base: *mut u8 = unsafe { wasm_memory_data(c_memory as _) as _ }; + let size = unsafe { wasm_memory_size(c_memory as _) }; + + Self { + buffer: MemoryBuffer { + base, + len, + marker: PhantomData, + }, + size, + } } /// Returns the pointer to the raw bytes of the `Memory`. @@ -34,12 +45,12 @@ impl<'a> MemoryView<'a> { // as deprecated and not used in future code. #[doc(hidden)] pub fn data_ptr(&self) -> *mut u8 { - self.0.data_ptr() + self.buffer.base } /// Returns the size (in bytes) of the `Memory`. pub fn data_size(&self) -> u64 { - self.0.data_size() + self.buffer.len.try_into().unwrap() } /// Retrieve a slice of the memory contents. @@ -51,7 +62,7 @@ impl<'a> MemoryView<'a> { /// function that writes to the memory or by resizing the memory. #[doc(hidden)] pub unsafe fn data_unchecked(&self) -> &[u8] { - self.0.data_unchecked() + self.data_unchecked_mut() } /// Retrieve a mutable slice of the memory contents. @@ -66,7 +77,7 @@ impl<'a> MemoryView<'a> { #[allow(clippy::mut_from_ref)] #[doc(hidden)] pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] { - self.0.data_unchecked_mut() + std::slice::from_raw_parts_mut(self.buffer.base, self.buffer.len) } /// Returns the size (in [`Pages`]) of the `Memory`. @@ -82,12 +93,12 @@ impl<'a> MemoryView<'a> { /// assert_eq!(m.view(&mut store).size(), Pages(1)); /// ``` pub fn size(&self) -> Pages { - self.0.size() + Pages(self.size) } #[inline] pub(crate) fn buffer(&'a self) -> MemoryBuffer<'a> { - MemoryBuffer(self.0.buffer()) + self.buffer } /// Safely reads bytes from the memory at the given offset. @@ -98,7 +109,7 @@ impl<'a> MemoryView<'a> { /// This method is guaranteed to be safe (from the host side) in the face of /// concurrent writes. pub fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> { - self.0.read(offset, buf) + self.buffer.read(offset, buf) } /// Safely reads a single byte from memory at the given offset @@ -106,7 +117,9 @@ impl<'a> MemoryView<'a> { /// This method is guaranteed to be safe (from the host side) in the face of /// concurrent writes. pub fn read_u8(&self, offset: u64) -> Result { - self.0.read_u8(offset) + let mut buf = [0u8; 1]; + self.read(offset, &mut buf)?; + Ok(buf[0]) } /// Safely reads bytes from the memory at the given offset. @@ -124,7 +137,7 @@ impl<'a> MemoryView<'a> { offset: u64, buf: &'b mut [MaybeUninit], ) -> Result<&'b mut [u8], MemoryAccessError> { - self.0.read_uninit(offset, buf) + self.buffer.read_uninit(offset, buf) } /// Safely writes bytes to the memory at the given offset. @@ -135,22 +148,26 @@ impl<'a> MemoryView<'a> { /// This method is guaranteed to be safe (from the host side) in the face of /// concurrent reads/writes. pub fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { - self.0.write(offset, data) + self.buffer.write(offset, data) } - /// Safely writes a single byte from memory at the given offset + /// Safely reads a single byte from memory at the given offset /// /// This method is guaranteed to be safe (from the host side) in the face of /// concurrent writes. pub fn write_u8(&self, offset: u64, val: u8) -> Result<(), MemoryAccessError> { - self.0.write_u8(offset, val) + let buf = [val]; + self.write(offset, &buf)?; + Ok(()) } + #[allow(unused)] /// Copies the memory and returns it as a vector of bytes pub fn copy_to_vec(&self) -> Result, MemoryAccessError> { self.copy_range_to_vec(0..self.data_size()) } + #[allow(unused)] /// Copies a range of the memory and returns it as a vector of bytes pub fn copy_range_to_vec(&self, range: Range) -> Result, MemoryAccessError> { let mut new_memory = Vec::new(); @@ -167,6 +184,7 @@ impl<'a> MemoryView<'a> { Ok(new_memory) } + #[allow(unused)] /// Copies the memory to another new memory object pub fn copy_to_memory(&self, amount: u64, new_memory: &Self) -> Result<(), MemoryAccessError> { let mut offset = 0; diff --git a/lib/api/src/rt/wamr/entities/mod.rs b/lib/api/src/rt/wamr/entities/mod.rs new file mode 100644 index 00000000000..e842f2f6659 --- /dev/null +++ b/lib/api/src/rt/wamr/entities/mod.rs @@ -0,0 +1,9 @@ +pub mod engine; +pub(crate) mod external; +pub(crate) mod function; +pub(crate) mod global; +pub(crate) mod instance; +pub(crate) mod memory; +pub(crate) mod module; +pub(crate) mod store; +pub(crate) mod table; diff --git a/lib/api/src/c_api/module.rs b/lib/api/src/rt/wamr/entities/module.rs similarity index 66% rename from lib/api/src/c_api/module.rs rename to lib/api/src/rt/wamr/entities/module.rs index 7e760943e8b..566c801a78d 100644 --- a/lib/api/src/c_api/module.rs +++ b/lib/api/src/rt/wamr/entities/module.rs @@ -1,29 +1,17 @@ -use super::bindings::wasm_module_t; -use crate::bindings::wasm_byte_vec_new; -use crate::bindings::wasm_byte_vec_new_empty; -use crate::bindings::wasm_byte_vec_t; -use crate::bindings::wasm_module_delete; -use crate::bindings::wasm_module_new; -use crate::bindings::wasm_store_new; -use crate::bindings::wasm_store_t; -use crate::errors::InstantiationError; -use crate::errors::RuntimeError; -use crate::imports::Imports; -use crate::store::AsStoreMut; -use crate::store::AsStoreRef; -use crate::vm::VMInstance; -use crate::Extern; -use crate::IntoBytes; -use crate::{AsEngineRef, ExportType, ImportType}; +//! Data types, functions and traits for `wamr`'s `Module` implementation. +use std::{path::Path, sync::Arc}; + +use crate::{ + rt::wamr::bindings::{wasm_byte_vec_t, wasm_module_delete, wasm_module_new, wasm_module_t}, + AsEngineRef, IntoBytes, RuntimeModule, +}; + use bytes::Bytes; -use std::path::Path; -use std::sync::Arc; -use tracing::{debug, warn}; use wasmer_types::{ - CompileError, DeserializeError, ExportsIterator, ExternType, FunctionType, GlobalType, - ImportsIterator, MemoryType, ModuleInfo, Mutability, Pages, SerializeError, TableType, Type, + CompileError, DeserializeError, ExportType, ExportsIterator, ExternType, FunctionType, + GlobalType, ImportType, ImportsIterator, MemoryType, ModuleInfo, Mutability, Pages, + SerializeError, TableType, Type, }; - pub(crate) struct ModuleHandle { pub(crate) inner: *mut wasm_module_t, pub(crate) store: std::sync::Mutex, @@ -40,7 +28,6 @@ impl Eq for ModuleHandle {} impl ModuleHandle { fn new(engine: &impl AsEngineRef, binary: &[u8]) -> Result { - #[cfg(feature = "wamr")] let bytes = wasm_byte_vec_t { size: binary.len(), data: binary.as_ptr() as _, @@ -49,15 +36,10 @@ impl ModuleHandle { lock: std::ptr::null_mut(), }; - #[cfg(any(feature = "wasmi", feature = "v8"))] - let bytes = wasm_byte_vec_t { - size: binary.len(), - data: binary.as_ptr() as _, - }; - let store = crate::store::Store::new(engine.as_engine_ref().engine().clone()); - let inner = unsafe { wasm_module_new(store.inner.store.inner, &bytes as *const _) }; + let inner = + unsafe { wasm_module_new(store.inner.store.as_wamr().inner, &bytes as *const _) }; let store = std::sync::Mutex::new(store); if inner.is_null() { @@ -74,6 +56,7 @@ impl Drop for ModuleHandle { } #[derive(Clone, PartialEq, Eq)] +/// A WebAssembly `module` in `wamr`. pub struct Module { pub(crate) handle: Arc, name: Option, @@ -99,7 +82,7 @@ impl Module { let mut binary = binary.to_vec(); let binary = binary.into_bytes(); let module = ModuleHandle::new(engine, &binary)?; - let info = crate::module_info_polyfill::translate_module(&binary[..]) + let info = crate::utils::polyfill::translate_module(&binary[..]) .unwrap() .info; @@ -162,15 +145,18 @@ impl Module { true } - pub fn imports<'a>(&'a self) -> ImportsIterator + 'a> { + pub fn imports<'a>(&'a self) -> ImportsIterator + 'a>> { self.info().imports() } - pub fn exports<'a>(&'a self) -> ExportsIterator + 'a> { + pub fn exports<'a>(&'a self) -> ExportsIterator + 'a>> { self.info().exports() } - pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator> + 'a { + pub fn custom_sections<'a>( + &'a self, + name: &'a str, + ) -> Box> + 'a> { self.info().custom_sections(name) } @@ -178,3 +164,29 @@ impl Module { &self.info } } + +impl crate::Module { + /// Consume [`self`] into a reference [`crate::rt::wamr::module::Module`]. + pub fn into_wamr(self) -> crate::rt::wamr::module::Module { + match self.0 { + RuntimeModule::Wamr(s) => s, + _ => panic!("Not a `wamr` module!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::wamr::module::Module`]. + pub fn as_wamr(&self) -> &crate::rt::wamr::module::Module { + match self.0 { + RuntimeModule::Wamr(ref s) => s, + _ => panic!("Not a `wamr` module!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wamr::module::Module`]. + pub fn as_wamr_mut(&mut self) -> &mut crate::rt::wamr::module::Module { + match self.0 { + RuntimeModule::Wamr(ref mut s) => s, + _ => panic!("Not a `wamr` module!"), + } + } +} diff --git a/lib/api/src/rt/wamr/entities/store/mod.rs b/lib/api/src/rt/wamr/entities/store/mod.rs new file mode 100644 index 00000000000..cb6dde92d6b --- /dev/null +++ b/lib/api/src/rt/wamr/entities/store/mod.rs @@ -0,0 +1,103 @@ +//! Data types, functions and traits for `wamr`'s `Store` implementation. +use crate::{ + engine::{AsEngineRef, Engine, EngineRef}, + rt::wamr::bindings::{wasm_store_delete, wasm_store_new, wasm_store_t}, + AsStoreRef, RuntimeStore, StoreRef, +}; + +mod obj; +pub use obj::*; + +/// A WebAssembly `store` in `wamr`. +pub(crate) struct Store { + pub(crate) engine: Engine, + pub(crate) inner: *mut wasm_store_t, +} + +impl std::fmt::Debug for Store { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Store") + .field("engine", &self.engine) + .finish() + } +} + +impl Store { + pub(crate) fn new(engine: crate::engine::Engine) -> Self { + let inner: *mut wasm_store_t = unsafe { wasm_store_new(engine.as_wamr().inner.engine) }; + Store { inner, engine } + } + + pub(crate) fn engine(&self) -> &Engine { + &self.engine + } + + pub(crate) fn engine_mut(&mut self) -> &mut Engine { + &mut self.engine + } +} + +impl Drop for Store { + fn drop(&mut self) { + unsafe { wasm_store_delete(self.inner) } + } +} + +impl AsEngineRef for Store { + fn as_engine_ref(&self) -> EngineRef<'_> { + EngineRef::new(&self.engine) + } +} + +impl crate::RuntimeStore { + /// Consume [`self`] into [`crate::rt::wamr::store::Store`]. + pub fn into_wamr(self) -> crate::rt::wamr::store::Store { + match self { + Self::Wamr(s) => s, + _ => panic!("Not a `wamr` store!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::wamr::store::Store`]. + pub fn as_wamr(&self) -> &crate::rt::wamr::store::Store { + match self { + Self::Wamr(s) => s, + _ => panic!("Not a `wamr` store!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wamr::store::Store`]. + pub fn as_wamr_mut(&mut self) -> &mut crate::rt::wamr::store::Store { + match self { + Self::Wamr(s) => s, + _ => panic!("Not a `wamr` store!"), + } + } + + /// Return true if [`self`] is a store from the `wamr` runtime. + pub fn is_wamr(&self) -> bool { + matches!(self, Self::Wamr(_)) + } +} + +impl crate::Store { + /// Consume [`self`] into [`crate::rt::wamr::store::Store`]. + pub(crate) fn into_wamr(self) -> crate::rt::wamr::store::Store { + self.inner.store.into_wamr() + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::wamr::store::Store`]. + pub(crate) fn as_wamr(&self) -> &crate::rt::wamr::store::Store { + self.inner.store.as_wamr() + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wamr::store::Store`]. + pub(crate) fn as_wamr_mut(&mut self) -> &mut crate::rt::wamr::store::Store { + self.inner.store.as_wamr_mut() + } + + /// Return true if [`self`] is a store from the `wamr` runtime. + pub fn is_wamr(&self) -> bool { + self.inner.store.is_wamr() + } +} diff --git a/lib/api/src/rt/wamr/entities/store/obj.rs b/lib/api/src/rt/wamr/entities/store/obj.rs new file mode 100644 index 00000000000..510a8361715 --- /dev/null +++ b/lib/api/src/rt/wamr/entities/store/obj.rs @@ -0,0 +1,288 @@ +use std::{fmt, marker::PhantomData, num::NonZeroUsize}; + +use crate::{ + rt::wamr::vm::{VMFunctionEnvironment, VMGlobal}, + AsStoreMut, +}; + +pub use wasmer_types::StoreId; + +impl crate::StoreObjects { + /// Consume [`self`] into [`crate::rt::wamr::store::StoreObjects`]. + pub fn into_wamr(self) -> crate::rt::wamr::store::StoreObjects { + match self { + crate::StoreObjects::Wamr(s) => s, + _ => panic!("Not a `wamr` store!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::wamr::store::StoreObjects`]. + pub fn as_wamr(&self) -> &crate::rt::wamr::store::StoreObjects { + match self { + crate::StoreObjects::Wamr(s) => s, + _ => panic!("Not a `wamr` store!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wamr::store::StoreObjects`]. + pub fn as_wamr_mut(&mut self) -> &mut crate::rt::wamr::store::StoreObjects { + match self { + crate::StoreObjects::Wamr(s) => s, + _ => panic!("Not a `wamr` store!"), + } + } +} + +/// Trait to represent an object managed by a context. This is implemented on +/// the VM types managed by the context. +pub trait StoreObject: Sized { + fn list(store: &StoreObjects) -> &Vec; + fn list_mut(store: &mut StoreObjects) -> &mut Vec; +} + +macro_rules! impl_store_object { + ($($field:ident => $ty:ty,)*) => { + $( + impl StoreObject for $ty { + fn list(store: &StoreObjects) -> &Vec { + &store.$field + } + fn list_mut(store: &mut StoreObjects) -> &mut Vec { + &mut store.$field + } + } + )* + }; + } + +impl_store_object! { + // Note: we store the globals in order to be able to access them later via + // `StoreObjects::iter_globals`. + globals => VMGlobal, + // functions => VMFunction, + // tables => VMTable, + // memories => VMMemory, + // The function environments are the only things attached to a store, + // since the other JS objects (table, globals, memory and functions) + // live in the JS VM Store by default + function_environments => VMFunctionEnvironment, +} + +/// Set of objects managed by a context. +#[derive(Default, Debug)] +pub struct StoreObjects { + id: StoreId, + globals: Vec, + function_environments: Vec, +} + +impl StoreObjects { + /// Returns the ID of this context. + pub fn id(&self) -> StoreId { + self.id + } + + /// Sets the ID of this store + pub fn set_id(&mut self, id: StoreId) { + self.id = id; + } + + /// Returns a pair of mutable references from two handles. + /// + /// Panics if both handles point to the same object. + pub fn get_2_mut( + &mut self, + a: InternalStoreHandle, + b: InternalStoreHandle, + ) -> (&mut T, &mut T) { + assert_ne!(a.index(), b.index()); + let list = T::list_mut(self); + if a.index() < b.index() { + let (low, high) = list.split_at_mut(b.index()); + (&mut low[a.index()], &mut high[0]) + } else { + let (low, high) = list.split_at_mut(a.index()); + (&mut high[0], &mut low[a.index()]) + } + } + + /// Return an immutable iterator over all globals + pub fn iter_globals(&self) -> core::slice::Iter { + self.globals.iter() + } + + /// Return an vector of all globals and converted to u128 + pub fn as_u128_globals(&self) -> Vec { + vec![] + // self.iter_globals() + // .map(|v| v.global.value().as_f64().unwrap() as u128) + // .collect() + } + + /// Set a global, at index idx. Will panic if idx is out of range + /// Safety: the caller should check taht the raw value is compatible + /// with destination VMGlobal type + pub fn set_global_unchecked(&self, idx: usize, new_val: u128) { + assert!(idx < self.globals.len()); + // let g = &self.globals[idx].global; + // let cur_val = g.value().as_f64().unwrap(); + // let new_val = new_val as f64; + // if cur_val != new_val { + // let new_value = JSValue::from(new_val); + // g.set_value(&new_value); + // } + } +} + +/// Handle to an object managed by a context. +/// +/// Internally this is just an integer index into a context. A reference to the +/// context must be passed in separately to access the actual object. +pub struct StoreHandle { + id: StoreId, + internal: InternalStoreHandle, +} + +impl core::cmp::PartialEq for StoreHandle { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl std::hash::Hash for StoreHandle { + fn hash(&self, state: &mut H) { + self.id.hash(state); + self.internal.idx.hash(state); + } +} + +impl Clone for StoreHandle { + fn clone(&self) -> Self { + Self { + id: self.id, + internal: self.internal, + } + } +} + +impl fmt::Debug for StoreHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StoreHandle") + .field("id", &self.id) + .field("internal", &self.internal.index()) + .finish() + } +} + +impl StoreHandle { + /// Moves the given object into a context and returns a handle to it. + pub fn new(store: &mut StoreObjects, val: T) -> Self { + Self { + id: store.id, + internal: InternalStoreHandle::new(store, val), + } + } + + /// Returns a reference to the object that this handle points to. + pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T { + assert_eq!(self.id, store.id, "object used with the wrong context"); + self.internal.get(store) + } + + /// Returns a mutable reference to the object that this handle points to. + pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T { + assert_eq!(self.id, store.id, "object used with the wrong context"); + self.internal.get_mut(store) + } + + /// Returns the internal handle contains within this handle. + pub fn internal_handle(&self) -> InternalStoreHandle { + self.internal + } + + /// Returns the ID of the context associated with the handle. + #[allow(unused)] + pub fn store_id(&self) -> StoreId { + self.id + } + + /// Overrides the store id with a new ID + #[allow(unused)] + pub fn set_store_id(&mut self, id: StoreId) { + self.id = id; + } + + /// Constructs a `StoreHandle` from a `StoreId` and an `InternalStoreHandle`. + /// + /// # Safety + /// Handling `InternalStoreHandle` values is unsafe because they do not track context ID. + pub unsafe fn from_internal(id: StoreId, internal: InternalStoreHandle) -> Self { + Self { id, internal } + } +} + +/// Internal handle to an object owned by the current context. +/// +/// Unlike `StoreHandle` this does not track the context ID: it is only +/// intended to be used within objects already owned by a context. +#[repr(transparent)] +pub struct InternalStoreHandle { + // Use a NonZero here to reduce the size of Option. + idx: NonZeroUsize, + marker: PhantomData T>, +} + +impl Clone for InternalStoreHandle { + fn clone(&self) -> Self { + *self + } +} +impl Copy for InternalStoreHandle {} + +impl fmt::Debug for InternalStoreHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("InternalStoreHandle") + .field("idx", &self.idx) + .finish() + } +} +impl PartialEq for InternalStoreHandle { + fn eq(&self, other: &Self) -> bool { + self.idx == other.idx + } +} +impl Eq for InternalStoreHandle {} + +impl InternalStoreHandle { + /// Moves the given object into a context and returns a handle to it. + pub fn new(store: &mut StoreObjects, val: T) -> Self { + let list = T::list_mut(store); + let idx = NonZeroUsize::new(list.len() + 1).unwrap(); + list.push(val); + Self { + idx, + marker: PhantomData, + } + } + + /// Returns a reference to the object that this handle points to. + pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T { + &T::list(store)[self.idx.get() - 1] + } + + /// Returns a mutable reference to the object that this handle points to. + pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T { + &mut T::list_mut(store)[self.idx.get() - 1] + } + + pub(crate) fn index(&self) -> usize { + self.idx.get() + } + + pub(crate) fn from_index(idx: usize) -> Option { + NonZeroUsize::new(idx).map(|idx| Self { + idx, + marker: PhantomData, + }) + } +} diff --git a/lib/api/src/c_api/externals/table.rs b/lib/api/src/rt/wamr/entities/table.rs similarity index 55% rename from lib/api/src/c_api/externals/table.rs rename to lib/api/src/rt/wamr/entities/table.rs index 0ca09749856..98233dadd91 100644 --- a/lib/api/src/c_api/externals/table.rs +++ b/lib/api/src/rt/wamr/entities/table.rs @@ -1,26 +1,18 @@ -use crate::as_c::{param_from_c, result_to_value}; -use crate::bindings::{ - wasm_extern_as_ref, wasm_func_as_ref, wasm_limits_t, wasm_ref_as_func, wasm_ref_as_trap, - wasm_ref_t, wasm_table_copy, wasm_table_get, wasm_table_grow, wasm_table_new, wasm_table_set, - wasm_table_size, wasm_table_type, wasm_tabletype_element, wasm_tabletype_limits, - wasm_tabletype_new, wasm_tabletype_t, wasm_val_t, wasm_valkind_enum_WASM_FUNCREF, - wasm_valtype_new, +//! Data types, functions and traits for `wamr`'s `Table` implementation. +use wasmer_types::TableType; + +use crate::{ + vm::{VMExtern, VMExternTable}, + wamr::{ + bindings::{self, *}, + utils::convert::{IntoCApiType, IntoCApiValue, IntoWasmerType, IntoWasmerValue}, + vm::VMTable, + }, + AsStoreMut, AsStoreRef, Runtime, RuntimeError, RuntimeTable, Value, }; -#[cfg(not(feature = "v8"))] -use crate::bindings::wasm_valkind_enum_WASM_EXTERNREF; - -#[cfg(feature = "v8")] -use crate::bindings::wasm_valkind_enum_WASM_ANYREF as wasm_valkind_enum_WASM_EXTERNREF; - -use crate::c_api::bindings::wasm_table_as_extern; -use crate::c_api::vm::{VMExtern, VMExternTable, VMFunction, VMTable}; -use crate::errors::RuntimeError; -use crate::store::{AsStoreMut, AsStoreRef}; -use crate::value::Value; -use crate::{as_c, FunctionType, TableType}; - -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] +/// A WebAssembly `table` in `wamr`. pub struct Table { pub(crate) handle: VMTable, } @@ -30,7 +22,7 @@ unsafe impl Sync for Table {} impl Table { pub(crate) fn type_to_wamr(ty: TableType) -> *mut wasm_tabletype_t { - let valtype = unsafe { wasm_valtype_new(as_c::type_to_c(&ty.ty)) }; + let valtype = unsafe { wasm_valtype_new(ty.ty.into_ct()) }; let limits = Box::into_raw(Box::new(wasm_limits_t { min: ty.minimum, @@ -52,26 +44,30 @@ impl Table { let engine = store_mut.engine(); let wasm_tablety = Self::type_to_wamr(ty); - let init: wasm_val_t = as_c::result_to_value(&init); + let init: wasm_val_t = init.into_cv(); Ok(Self { handle: unsafe { - wasm_table_new(store_mut.inner.store.inner, wasm_tablety, init.of.ref_) + wasm_table_new( + store_mut.inner.store.as_wamr().inner, + wasm_tablety, + init.of.ref_, + ) }, }) } pub fn to_vm_extern(&self) -> VMExtern { - unsafe { wasm_table_as_extern(self.handle) } + VMExtern::Wamr(unsafe { wasm_table_as_extern(self.handle) }) } pub fn ty(&self, _store: &impl AsStoreRef) -> TableType { - let wamr_table_type: *mut wasm_tabletype_t = unsafe { wasm_table_type(self.handle) }; - let table_limits = unsafe { wasm_tabletype_limits(wamr_table_type) }; - let table_type = unsafe { wasm_tabletype_element(wamr_table_type) }; + 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: unsafe { as_c::valtype_to_type(table_type) }, + ty: table_type.into_wt(), minimum: unsafe { (*table_limits).min }, maximum: unsafe { if (*table_limits).max == 0 { @@ -97,25 +93,13 @@ impl Table { ty => panic!("unsupported table type: {ty:?}"), } as u8; - let value = { - #[cfg(feature = "wamr")] - { - wasm_val_t { - kind, - _paddings: Default::default(), - of: crate::bindings::wasm_val_t__bindgen_ty_1 { ref_ }, - } - } - #[cfg(not(feature = "wamr"))] - { - wasm_val_t { - kind, - of: crate::bindings::wasm_val_t__bindgen_ty_1 { ref_ }, - } - } + let value = wasm_val_t { + kind, + _paddings: Default::default(), + of: bindings::wasm_val_t__bindgen_ty_1 { ref_ }, }; - Some(param_from_c(&value)) + Some(value.into_wv()) } } @@ -128,7 +112,7 @@ impl Table { unsafe { let init = match val { Value::ExternRef(None) | Value::FuncRef(None) => std::ptr::null_mut(), - Value::FuncRef(Some(ref r)) => wasm_func_as_ref(r.0.handle), + Value::FuncRef(Some(ref r)) => wasm_func_as_ref(r.as_wamr().handle), _ => { return Err(RuntimeError::new(format!( "Could not grow table due to unsupported init value type: {val:?} " @@ -160,7 +144,7 @@ impl Table { let size = wasm_table_size(self.handle); let init = match init { Value::ExternRef(None) | Value::FuncRef(None) => std::ptr::null_mut(), - Value::FuncRef(Some(r)) => wasm_func_as_ref(r.0.handle), + Value::FuncRef(Some(r)) => wasm_func_as_ref(r.as_wamr().handle), _ => { return Err(RuntimeError::new(format!( "Could not grow table due to unsupported init value type: {init:?} " @@ -187,10 +171,64 @@ impl Table { } pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, vm_extern: VMExternTable) -> Self { - Self { handle: vm_extern } + Self { + handle: vm_extern.into_wamr(), + } } pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { true } } + +impl crate::Table { + /// Consume [`self`] into [`crate::rt::wamr::table::Table`]. + pub fn into_wamr(self) -> crate::rt::wamr::table::Table { + match self.0 { + RuntimeTable::Wamr(s) => s, + _ => panic!("Not a `wamr` table!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::wamr::table::Table`]. + pub fn as_wamr(&self) -> &crate::rt::wamr::table::Table { + match self.0 { + RuntimeTable::Wamr(ref s) => s, + _ => panic!("Not a `wamr` table!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wamr::table::Table`]. + pub fn as_wamr_mut(&mut self) -> &mut crate::rt::wamr::table::Table { + match self.0 { + RuntimeTable::Wamr(ref mut s) => s, + _ => panic!("Not a `wamr` table!"), + } + } +} + +impl crate::RuntimeTable { + /// Consume [`self`] into [`crate::rt::wamr::table::Table`]. + pub fn into_wamr(self) -> crate::rt::wamr::table::Table { + match self { + Self::Wamr(s) => s, + _ => panic!("Not a `wamr` table!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::wamr::table::Table`]. + pub fn as_wamr(&self) -> &crate::rt::wamr::table::Table { + match self { + Self::Wamr(ref s) => s, + _ => panic!("Not a `wamr` table!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wamr::table::Table`]. + pub fn as_wamr_mut(&mut self) -> &mut crate::rt::wamr::table::Table { + match self { + Self::Wamr(ref mut s) => s, + _ => panic!("Not a `wamr` table!"), + } + } +} diff --git a/lib/api/src/rt/wamr/error.rs b/lib/api/src/rt/wamr/error.rs new file mode 100644 index 00000000000..c612f48a207 --- /dev/null +++ b/lib/api/src/rt/wamr/error.rs @@ -0,0 +1,172 @@ +use std::{ + error::Error, + ffi::{c_char, CStr}, +}; + +use crate::{wamr::bindings::*, AsStoreMut}; + +#[derive(Debug)] +enum InnerTrap { + User(Box), + CApi(*mut wasm_trap_t), +} + +/// A struct representing a Trap +pub struct Trap { + inner: InnerTrap, +} + +unsafe impl Send for Trap {} +unsafe impl Sync for Trap {} + +impl Trap { + pub fn user(error: Box) -> Self { + Self { + inner: InnerTrap::User(error), + } + } + + /// Attempts to downcast the `Trap` to a concrete type. + pub fn downcast(self) -> Result { + match self.inner { + // We only try to downcast user errors + InnerTrap::User(err) if err.is::() => Ok(*err.downcast::().unwrap()), + _ => Err(self), + } + } + + /// Attempts to downcast the `Trap` to a concrete type. + pub fn downcast_ref(&self) -> Option<&T> { + match &self.inner { + // We only try to downcast user errors + InnerTrap::User(err) if err.is::() => err.downcast_ref::(), + _ => None, + } + } + + /// Returns true if the `Trap` is the same as T + pub fn is(&self) -> bool { + match &self.inner { + InnerTrap::User(err) => err.is::(), + _ => false, + } + } + + pub unsafe fn into_wasm_trap(self, store: &mut impl AsStoreMut) -> *mut wasm_trap_t { + match self.inner { + InnerTrap::CApi(t) => t, + InnerTrap::User(err) => { + let err_ptr = Box::leak(Box::new(err)); + let mut data = std::mem::zeroed(); + // let x = format!("") + let s1 = format!("🐛{:p}", err_ptr); + let _s = s1.into_bytes().into_boxed_slice(); + wasm_byte_vec_new(&mut data, _s.len(), _s.as_ptr() as _); + std::mem::forget(_s); + let store = store.as_store_mut(); + wasm_trap_new(store.inner.store.as_wamr().inner, &mut data) + } + } + } + + // pub unsafe fn deserialize_from_wasm_trap(trap: *mut wasm_trap_t) -> Self { + // let mut data = std::mem::zeroed(); + // wasm_trap_message(trap, data); + // println!("data: {:p}", data); + + // std::ptr::read(data as *const _) + // } +} + +impl From<*mut wasm_trap_t> for Trap { + fn from(value: *mut wasm_trap_t) -> Self { + let message = unsafe { + let mut message = std::mem::zeroed(); + wasm_trap_message(value, &mut message); + + CStr::from_ptr(message.data as *const c_char) + .to_str() + .unwrap() + }; + + println!("{message}"); + + if message.starts_with("Exception: 🐛") { + let ptr_str = message.replace("Exception: 🐛", ""); + 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), + ) + }; + + Self { + inner: InnerTrap::User(ptr), + } + } else { + Self { + inner: InnerTrap::CApi(value), + } + } + } +} + +impl std::error::Error for Trap { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self.inner { + InnerTrap::User(err) => Some(&**err), + _ => None, + } + } +} + +impl std::fmt::Display for Trap { + 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()) + } + } + } +} + +impl std::fmt::Debug for Trap { + 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()) + } + } + } +} + +impl From for crate::RuntimeError { + fn from(trap: Trap) -> Self { + if trap.is::() { + return trap.downcast::().unwrap(); + } + + crate::RuntimeError::new_from_source(crate::RuntimeTrap::Wamr(trap), vec![], None) + } +} diff --git a/lib/api/src/rt/wamr/mod.rs b/lib/api/src/rt/wamr/mod.rs new file mode 100644 index 00000000000..1347849ee1b --- /dev/null +++ b/lib/api/src/rt/wamr/mod.rs @@ -0,0 +1,9 @@ +//! Data types, functions and traits for `wamr`. + +pub(crate) mod bindings; +pub(crate) mod entities; +pub(crate) mod error; +pub(crate) mod utils; +pub(crate) mod vm; + +pub use entities::{engine::Engine as Wamr, *}; diff --git a/lib/api/src/rt/wamr/utils/convert.rs b/lib/api/src/rt/wamr/utils/convert.rs new file mode 100644 index 00000000000..a774f1a46c1 --- /dev/null +++ b/lib/api/src/rt/wamr/utils/convert.rs @@ -0,0 +1,148 @@ +/// Utilities to convert between `wamr` and `wasmer` values +use crate::{ + wamr::{ + bindings::{ + self, wasm_func_as_ref, wasm_val_t, wasm_val_t__bindgen_ty_1, wasm_valkind_t, + wasm_valtype_kind, wasm_valtype_t, + }, + function, + }, + Function, RuntimeFunction, Value, +}; +use wasmer_types::Type; + +pub trait IntoCApiValue { + /// Consume [`self`] to produce a [`wasm_val_t`]. + fn into_cv(self) -> wasm_val_t; +} + +impl IntoCApiValue for Value { + fn into_cv(self) -> wasm_val_t { + match self { + Value::I32(val) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_I32 as _, + _paddings: Default::default(), + of: wasm_val_t__bindgen_ty_1 { i32_: val }, + }, + Value::I64(val) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_I64 as _, + _paddings: Default::default(), + of: wasm_val_t__bindgen_ty_1 { i64_: val }, + }, + Value::F32(val) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_F32 as _, + _paddings: Default::default(), + of: wasm_val_t__bindgen_ty_1 { f32_: val }, + }, + Value::F64(val) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_F64 as _, + _paddings: Default::default(), + of: wasm_val_t__bindgen_ty_1 { f64_: val }, + }, + Value::FuncRef(Some(val)) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_FUNCREF as _, + _paddings: Default::default(), + of: wasm_val_t__bindgen_ty_1 { + ref_: unsafe { wasm_func_as_ref(val.as_wamr().handle) }, + }, + }, + Value::FuncRef(None) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_FUNCREF as _, + _paddings: Default::default(), + of: wasm_val_t__bindgen_ty_1 { + ref_: unsafe { wasm_func_as_ref(std::ptr::null_mut()) }, + }, + }, + Value::ExternRef(_) => panic!("Creating host values from guest ExternRefs is not currently supported through wasm_c_api.") , + Value::V128(_) => panic!("Creating host values from guest V128s is not currently supported through wasm_c_api."), + } + } +} + +pub trait IntoWasmerValue { + /// Consume [`self`] to produce a [`Value`]. + fn into_wv(self) -> Value; +} + +impl IntoWasmerValue for wasm_val_t { + fn into_wv(self) -> Value { + match self.kind as _ { + bindings::wasm_valkind_enum_WASM_I32 => Value::I32(unsafe { self.of.i32_ }), + bindings::wasm_valkind_enum_WASM_I64 => Value::I64(unsafe { self.of.i64_ }), + bindings::wasm_valkind_enum_WASM_F32 => Value::F32(unsafe { self.of.f32_ }), + bindings::wasm_valkind_enum_WASM_F64 => Value::F64(unsafe { self.of.f64_ }), + bindings::wasm_valkind_enum_WASM_FUNCREF => Value::FuncRef(Some(Function( + RuntimeFunction::Wamr(crate::rt::wamr::function::Function { + handle: unsafe { self.of.ref_ as _ }, + }), + ))), + bindings::wasm_valkind_enum_WASM_EXTERNREF => { + panic!("ExternRefs are not currently supported through wasm_c_api") + } + + _ => { + panic!("wamr currently does not support V128 values") + } + } + } +} + +pub trait IntoWasmerType { + /// Consume [`self`] to produce a [`Type`]. + fn into_wt(self) -> Type; +} + +impl IntoWasmerType for wasm_valkind_t { + fn into_wt(self) -> Type { + match self as _ { + bindings::wasm_valkind_enum_WASM_I32 => Type::I32, + bindings::wasm_valkind_enum_WASM_I64 => Type::I64, + bindings::wasm_valkind_enum_WASM_F32 => Type::F32, + bindings::wasm_valkind_enum_WASM_F64 => Type::F64, + bindings::wasm_valkind_enum_WASM_V128 => Type::V128, + bindings::wasm_valkind_enum_WASM_EXTERNREF => Type::ExternRef, + bindings::wasm_valkind_enum_WASM_FUNCREF => Type::FuncRef, + _ => unreachable!("wamr kind {self:?} has no matching wasmer_types::Type"), + } + } +} + +pub trait IntoCApiType { + /// Consume [`self`] to produce a [`wasm_valkind_t`]. + fn into_ct(self) -> wasm_valkind_t; +} + +impl IntoCApiType for Type { + fn into_ct(self) -> wasm_valkind_t { + match self as _ { + Type::I32 => bindings::wasm_valkind_enum_WASM_I32 as _, + Type::I64 => bindings::wasm_valkind_enum_WASM_I64 as _, + Type::F32 => bindings::wasm_valkind_enum_WASM_F32 as _, + Type::F64 => bindings::wasm_valkind_enum_WASM_F64 as _, + Type::FuncRef => bindings::wasm_valkind_enum_WASM_FUNCREF as _, + Type::ExternRef => bindings::wasm_valkind_enum_WASM_EXTERNREF as _, + Type::V128 => bindings::wasm_valkind_enum_WASM_V128 as _, + } + } +} + +impl IntoWasmerType for wasm_valtype_t { + fn into_wt(self) -> Type { + let type_: wasm_valkind_t = unsafe { wasm_valtype_kind(&self as *const _) }; + type_.into_wt() + } +} + +impl IntoWasmerType for *const wasm_valtype_t { + fn into_wt(self) -> Type { + let type_: wasm_valkind_t = unsafe { wasm_valtype_kind(self as _) }; + type_.into_wt() + } +} + +impl IntoWasmerType for *mut wasm_valtype_t { + fn into_wt(self) -> Type { + let type_: wasm_valkind_t = unsafe { wasm_valtype_kind(self as _) }; + type_.into_wt() + } +} diff --git a/lib/api/src/rt/wamr/utils/mod.rs b/lib/api/src/rt/wamr/utils/mod.rs new file mode 100644 index 00000000000..02d63d4e141 --- /dev/null +++ b/lib/api/src/rt/wamr/utils/mod.rs @@ -0,0 +1 @@ +pub(crate) mod convert; diff --git a/lib/api/src/rt/wamr/vm/env.rs b/lib/api/src/rt/wamr/vm/env.rs new file mode 100644 index 00000000000..d2a93d5f63d --- /dev/null +++ b/lib/api/src/rt/wamr/vm/env.rs @@ -0,0 +1,28 @@ +use std::any::Any; + +/// Underlying FunctionEnvironment used by a `VMFunction`. +#[derive(Debug)] +pub struct VMFunctionEnvironment { + pub(crate) contents: Box, +} + +impl VMFunctionEnvironment { + /// Wraps the given value to expose it to Wasm code as a function context. + pub fn new(val: impl Any + Send + 'static) -> Self { + Self { + contents: Box::new(val), + } + } + + #[allow(clippy::should_implement_trait)] + /// Returns a reference to the underlying value. + pub fn as_ref(&self) -> &(dyn Any + Send + 'static) { + &*self.contents + } + + #[allow(clippy::should_implement_trait)] + /// Returns a mutable reference to the underlying value. + pub fn as_mut(&mut self) -> &mut (dyn Any + Send + 'static) { + &mut *self.contents + } +} diff --git a/lib/api/src/rt/wamr/vm/mod.rs b/lib/api/src/rt/wamr/vm/mod.rs new file mode 100644 index 00000000000..25dc7d361ab --- /dev/null +++ b/lib/api/src/rt/wamr/vm/mod.rs @@ -0,0 +1,123 @@ +mod env; +pub use env::*; + +pub use super::bindings::{ + wasm_extern_as_func, wasm_extern_as_global, wasm_extern_as_memory, wasm_extern_as_table, + wasm_extern_kind, wasm_extern_t, wasm_func_t, wasm_global_t, wasm_instance_t, wasm_memory_t, + wasm_ref_t, wasm_table_t, +}; +use super::{ + entities::function::env::FunctionEnv, function::Function, global::Global, memory::Memory, + table::Table, +}; +use crate::{AsStoreMut, Extern, RuntimeFunction, RuntimeGlobal, RuntimeMemory, RuntimeTable}; +use wasmer_types::RawValue; + +pub use super::error::Trap; + +pub(crate) type VMExtern = *mut wasm_extern_t; + +pub(crate) type VMFunction = *mut wasm_func_t; +pub(crate) type VMFunctionBody = (); +pub(crate) type VMFunctionCallback = *mut ::std::os::raw::c_void; +pub(crate) type VMTrampoline = *mut ::std::os::raw::c_void; +pub(crate) type VMExternFunction = *mut wasm_func_t; + +pub(crate) type VMGlobal = *mut wasm_global_t; +pub(crate) type VMExternGlobal = *mut wasm_global_t; + +pub(crate) type VMMemory = *mut wasm_memory_t; +pub type VMSharedMemory = VMMemory; +pub(crate) type VMExternMemory = *mut wasm_memory_t; + +pub(crate) type VMTable = *mut wasm_table_t; +pub(crate) type VMExternTable = *mut wasm_table_t; + +pub(crate) type VMInstance = *mut wasm_instance_t; + +pub(crate) type VMExternObj = (); +pub(crate) type VMConfig = (); + +impl crate::VMExternToExtern for VMExtern { + fn to_extern(self, store: &mut impl AsStoreMut) -> Extern { + let kind = unsafe { wasm_extern_kind(&mut *self) }; + + match kind as u32 { + 0 => { + let func = unsafe { wasm_extern_as_func(&mut *self) }; + if func.is_null() { + panic!("The wasm-c-api reported extern as function, but is not"); + } + Extern::Function(crate::Function::from_vm_extern( + store, + crate::vm::VMExternFunction::Wamr(func), + )) + } + 1 => { + let global = unsafe { wasm_extern_as_global(&mut *self) }; + if global.is_null() { + panic!("The wasm-c-api reported extern as a global, but is not"); + } + Extern::Global(crate::Global::from_vm_extern( + store, + crate::vm::VMExternGlobal::Wamr(global), + )) + } + 2 => { + let table = unsafe { wasm_extern_as_table(&mut *self) }; + if table.is_null() { + panic!("The wasm-c-api reported extern as a table, but is not"); + } + Extern::Table(crate::Table::from_vm_extern( + store, + crate::vm::VMExternTable::Wamr(table), + )) + } + 3 => { + let memory = unsafe { wasm_extern_as_memory(&mut *self) }; + if memory.is_null() { + panic!("The wasm-c-api reported extern as a table, but is not"); + } + Extern::Memory(crate::Memory::from_vm_extern( + store, + crate::vm::VMExternMemory::Wamr(memory), + )) + } + _ => { + unimplemented!() + } + } + } +} + +pub(crate) struct VMExternRef(*mut wasm_ref_t); +impl VMExternRef { + /// Converts the `VMExternRef` into a `RawValue`. + pub fn into_raw(self) -> RawValue { + unimplemented!() + } + + /// Extracts a `VMExternRef` from a `RawValue`. + /// + /// # Safety + /// `raw` must be a valid `VMExternRef` instance. + pub unsafe fn from_raw(_raw: RawValue) -> Option { + unimplemented!(); + } +} + +pub(crate) struct VMFuncRef(*mut wasm_ref_t); +impl VMFuncRef { + /// Converts the `VMExternRef` into a `RawValue`. + pub fn into_raw(self) -> RawValue { + unimplemented!() + } + + /// Extracts a `VMExternRef` from a `RawValue`. + /// + /// # Safety + /// `raw` must be a valid `VMExternRef` instance. + pub unsafe fn from_raw(_raw: RawValue) -> Option { + unimplemented!(); + } +} diff --git a/lib/api/src/c_api/bindings.rs b/lib/api/src/rt/wasmi/bindings.rs similarity index 62% rename from lib/api/src/c_api/bindings.rs rename to lib/api/src/rt/wasmi/bindings.rs index e7abcbbcc33..cc5fc874c5f 100644 --- a/lib/api/src/c_api/bindings.rs +++ b/lib/api/src/rt/wasmi/bindings.rs @@ -3,10 +3,8 @@ #![allow(non_snake_case)] #![allow(unused)] -// This matches bindgen::Builder output -include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +include!(concat!(env!("OUT_DIR"), "/wasmi_bindings.rs")); -#[cfg(feature = "wasmi")] #[allow(unused_imports)] // This is here to force its linking. use wasmi_c_api::*; diff --git a/lib/api/src/rt/wasmi/entities/engine.rs b/lib/api/src/rt/wasmi/entities/engine.rs new file mode 100644 index 00000000000..5584d501c1a --- /dev/null +++ b/lib/api/src/rt/wasmi/entities/engine.rs @@ -0,0 +1,89 @@ +//! Data types, functions and traits for `v8` runtime's `Engine` implementation. +use crate::{ + rt::wasmi::bindings::{wasm_engine_delete, wasm_engine_new, wasm_engine_t}, + RuntimeEngine, +}; +use std::sync::Arc; + +#[derive(Debug)] +pub(crate) struct CApiEngine { + pub(crate) engine: *mut wasm_engine_t, +} + +impl Default for CApiEngine { + fn default() -> Self { + let engine: *mut wasm_engine_t = unsafe { wasm_engine_new() }; + Self { engine } + } +} + +impl Drop for CApiEngine { + fn drop(&mut self) { + unsafe { wasm_engine_delete(self.engine) } + } +} + +/// The engine for the Web Assembly Micro Runtime. +#[derive(Clone, Debug, Default)] +pub struct Engine { + pub(crate) inner: Arc, +} + +impl Engine { + /// Create a new instance of the `wasmi` engine. + pub fn new() -> Self { + Self::default() + } + + pub(crate) fn deterministic_id(&self) -> &str { + "wasmi" + } +} + +unsafe impl Send for Engine {} +unsafe impl Sync for Engine {} + +/// Returns the default engine for the JS engine +pub(crate) fn default_engine() -> Engine { + Engine::default() +} + +impl crate::Engine { + /// Consume [`self`] into a [`crate::rt::wasmi::engine::Engine`]. + pub fn into_wasmi(self) -> crate::rt::wasmi::engine::Engine { + match self.rt { + RuntimeEngine::Wasmi(s) => s, + _ => panic!("Not a `wasmi` engine!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::wasmi::engine::Engine`]. + pub fn as_wasmi(&self) -> &crate::rt::wasmi::engine::Engine { + match &self.rt { + RuntimeEngine::Wasmi(s) => s, + _ => panic!("Not a `wasmi` engine!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wasmi::engine::Engine`]. + pub fn as_wasmi_mut(&mut self) -> &mut crate::rt::wasmi::engine::Engine { + match self.rt { + RuntimeEngine::Wasmi(ref mut s) => s, + _ => panic!("Not a `wasmi` engine!"), + } + } + + /// Return true if [`self`] is an engine from the `wasmi` runtime. + pub fn is_wasmi(&self) -> bool { + matches!(self.rt, RuntimeEngine::Wasmi(_)) + } +} + +impl From for crate::Engine { + fn from(value: Engine) -> Self { + crate::Engine { + rt: RuntimeEngine::Wasmi(value), + id: crate::Engine::atomic_next_engine_id(), + } + } +} diff --git a/lib/api/src/rt/wasmi/entities/external.rs b/lib/api/src/rt/wasmi/entities/external.rs new file mode 100644 index 00000000000..b6224e52f74 --- /dev/null +++ b/lib/api/src/rt/wasmi/entities/external.rs @@ -0,0 +1,42 @@ +//! Data types, functions and traits for `wasmi`'s `ExternRef` implementation. +use crate::{ + store::{AsStoreMut, AsStoreRef}, + wasmi::vm::VMExternRef, +}; +use std::any::Any; + +#[derive(Debug, Clone)] +#[repr(transparent)] +/// A WebAssembly `extern ref` in `wasmi`. +pub struct ExternRef; + +impl ExternRef { + pub fn new(_store: &mut impl AsStoreMut, _value: T) -> Self + where + T: Any + Send + Sync + 'static + Sized, + { + unimplemented!("ExternRef is not yet supported with wasm_c_api"); + } + + pub fn downcast<'a, T>(&self, _store: &'a impl AsStoreRef) -> Option<&'a T> + where + T: Any + Send + Sync + 'static + Sized, + { + unimplemented!("ExternRef is not yet supported in wasm_c_api"); + } + + pub(crate) fn vm_externref(&self) -> VMExternRef { + unimplemented!("ExternRef is not yet supported in wasm_c_api"); + } + + pub(crate) unsafe fn from_vm_externref( + _store: &mut impl AsStoreMut, + _vm_externref: VMExternRef, + ) -> Self { + unimplemented!("ExternRef is not yet supported in wasm_c_api"); + } + + pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} diff --git a/lib/api/src/rt/wasmi/entities/function/env.rs b/lib/api/src/rt/wasmi/entities/function/env.rs new file mode 100644 index 00000000000..252a9c08861 --- /dev/null +++ b/lib/api/src/rt/wasmi/entities/function/env.rs @@ -0,0 +1,213 @@ +use std::{any::Any, fmt::Debug, marker::PhantomData}; + +use crate::{ + store::{AsStoreMut, AsStoreRef, StoreRef}, + wasmi::{store::StoreHandle, vm::VMFunctionEnvironment}, + StoreMut, +}; + +#[derive(Debug)] +#[repr(transparent)] +/// An opaque reference to a function environment. +/// The function environment data is owned by the `Store`. +pub struct FunctionEnv { + pub(crate) handle: StoreHandle, + marker: PhantomData, +} + +impl FunctionEnv { + /// Make a new FunctionEnv + pub fn new(store: &mut impl AsStoreMut, value: T) -> Self + where + T: Any + Send + 'static + Sized, + { + Self { + handle: StoreHandle::new( + store.as_store_mut().objects_mut().as_wasmi_mut(), + VMFunctionEnvironment::new(value), + ), + marker: PhantomData, + } + } + + /// Get the data as reference + pub fn as_ref<'a>(&self, store: &'a impl AsStoreRef) -> &'a T + where + T: Any + Send + 'static + Sized, + { + self.handle + .get(store.as_store_ref().objects().as_wasmi()) + .as_ref() + .downcast_ref::() + .unwrap() + } + + pub(crate) fn from_handle(handle: StoreHandle) -> Self { + Self { + handle, + marker: PhantomData, + } + } + + /// Get the data as mutable + pub fn as_mut<'a>(&self, store: &'a mut impl AsStoreMut) -> &'a mut T + where + T: Any + Send + 'static + Sized, + { + self.handle + .get_mut(store.objects_mut().as_wasmi_mut()) + .as_mut() + .downcast_mut::() + .unwrap() + } + + /// Convert it into a `FunctionEnvMut` + pub fn into_mut(self, store: &mut impl AsStoreMut) -> FunctionEnvMut + where + T: Any + Send + 'static + Sized, + { + FunctionEnvMut { + store_mut: store.as_store_mut(), + func_env: self, + } + } +} + +impl PartialEq for FunctionEnv { + fn eq(&self, other: &Self) -> bool { + self.handle == other.handle + } +} + +impl Eq for FunctionEnv {} + +impl std::hash::Hash for FunctionEnv { + fn hash(&self, state: &mut H) { + self.handle.hash(state); + self.marker.hash(state); + } +} + +impl Clone for FunctionEnv { + fn clone(&self) -> Self { + Self { + handle: self.handle.clone(), + marker: self.marker, + } + } +} + +/// A temporary handle to a [`FunctionEnv`]. +pub struct FunctionEnvMut<'a, T: 'a> { + pub(crate) store_mut: StoreMut<'a>, + pub(crate) func_env: FunctionEnv, +} + +impl<'a, T> Debug for FunctionEnvMut<'a, T> +where + T: Send + Debug + 'static, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.func_env.as_ref(&self.store_mut).fmt(f) + } +} + +impl FunctionEnvMut<'_, T> { + /// Returns a reference to the host state in this function environement. + pub fn data(&self) -> &T { + self.func_env.as_ref(&self.store_mut) + } + + /// Returns a mutable- reference to the host state in this function environement. + pub fn data_mut(&mut self) -> &mut T { + self.func_env.as_mut(&mut self.store_mut) + } + + /// Borrows a new immmutable reference + pub fn as_ref(&self) -> FunctionEnv { + self.func_env.clone() + } + + /// Borrows a new mutable reference + pub fn as_mut(&mut self) -> FunctionEnvMut<'_, T> { + FunctionEnvMut { + store_mut: self.store_mut.as_store_mut(), + func_env: self.func_env.clone(), + } + } + + /// Borrows a new mutable reference of both the attached Store and host state + pub fn data_and_store_mut(&mut self) -> (&mut T, StoreMut) { + let data = self.func_env.as_mut(&mut self.store_mut) as *mut T; + // telling the borrow check to close his eyes here + // this is still relatively safe to do as func_env are + // stored in a specific vec of Store, separate from the other objects + // and not really directly accessible with the StoreMut + let data = unsafe { &mut *data }; + (data, self.store_mut.as_store_mut()) + } +} + +//impl Into> for FunctionEnv { +// fn into(self) -> crate::FunctionEnv { +// crate::FunctionEnv::Wasmi(self) +// } +//} + +impl AsStoreRef for FunctionEnvMut<'_, T> { + fn as_store_ref(&self) -> StoreRef<'_> { + StoreRef { + inner: self.store_mut.inner, + } + } +} + +impl AsStoreMut for FunctionEnvMut<'_, T> { + fn as_store_mut(&mut self) -> StoreMut<'_> { + StoreMut { + inner: self.store_mut.inner, + } + } + + fn objects_mut(&mut self) -> &mut crate::StoreObjects { + self.store_mut.objects_mut() + } +} + +impl crate::FunctionEnv { + /// Consume [`self`] into [`crate::rt::wasmi::function::env::FunctionEnv`]. + pub fn into_wasmi(self) -> FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::Wasmi(s) => s, + _ => panic!("Not a `wasmi` function env!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::wasmi::function::env::FunctionEnv`]. + pub fn as_wasmi(&self) -> &FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::Wasmi(ref s) => s, + _ => panic!("Not a `wasmi` function env!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wasmi::function::env::FunctionEnv`]. + pub fn as_wasmi_mut(&mut self) -> &mut FunctionEnv { + match self.0 { + crate::RuntimeFunctionEnv::Wasmi(ref mut s) => s, + _ => panic!("Not a `wasmi` function env!"), + } + } +} + +impl<'a, T> From> for crate::FunctionEnvMut<'a, T> { + fn from(value: FunctionEnvMut<'a, T>) -> Self { + crate::FunctionEnvMut(crate::RuntimeFunctionEnvMut::Wasmi(value)) + } +} + +impl From> for crate::FunctionEnv { + fn from(value: FunctionEnv) -> Self { + crate::FunctionEnv(crate::RuntimeFunctionEnv::Wasmi(value)) + } +} diff --git a/lib/api/src/rt/wasmi/entities/function/mod.rs b/lib/api/src/rt/wasmi/entities/function/mod.rs new file mode 100644 index 00000000000..82443a92607 --- /dev/null +++ b/lib/api/src/rt/wasmi/entities/function/mod.rs @@ -0,0 +1,692 @@ +//! Data types, functions and traits for `wasmi`'s `Function` implementation. + +#![allow(non_snake_case)] +use std::{ + ffi::c_void, + panic::{self, AssertUnwindSafe}, +}; + +use crate::{ + vm::{VMExtern, VMExternFunction}, + wasmi::{ + bindings::*, + utils::convert::{IntoCApiType, IntoCApiValue, IntoWasmerType, IntoWasmerValue}, + vm::{VMFuncRef, VMFunction, VMFunctionCallback, VMFunctionEnvironment}, + }, + AsStoreMut, AsStoreRef, FromToNativeWasmType, FunctionEnv, FunctionEnvMut, IntoResult, + NativeWasmType, NativeWasmTypeInto, RuntimeError, RuntimeFunction, RuntimeFunctionEnvMut, + RuntimeTrap, StoreMut, Value, WasmTypeList, WithEnv, WithoutEnv, +}; + +use super::{super::error::Trap, store::StoreHandle}; +use wasmer_types::{FunctionType, RawValue}; + +pub(crate) mod env; +pub(crate) mod typed; + +pub use typed::*; + +type CCallback = unsafe extern "C" fn( + *mut c_void, + *const wasm_val_vec_t, + *mut wasm_val_vec_t, +) -> *mut wasm_trap_t; + +#[derive(Clone, PartialEq, Eq)] +/// A WebAssembly `function` in `wasmi`. +pub struct Function { + pub(crate) handle: VMFunction, +} + +unsafe impl Send for Function {} +unsafe impl Sync for Function {} + +impl From for Function { + fn from(handle: VMFunction) -> Self { + Self { handle } + } +} + +pub(crate) struct FunctionCallbackEnv<'a, F> { + pub(crate) store: StoreMut<'a>, + pub(crate) func: F, + pub(crate) env_handle: Option>, +} + +impl<'a, F> std::fmt::Debug for FunctionCallbackEnv<'a, F> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FunctionCallbackEnv") + .field("env_is_some", &self.env_handle.is_some()) + .finish() + } +} + +impl Function { + /// To `VMExtern`. + pub fn to_vm_extern(&self) -> VMExtern { + let extern_ = unsafe { wasm_func_as_extern(self.handle) }; + assert!( + !extern_.is_null(), + "Returned null Function extern from wasm-c-api" + ); + VMExtern::Wasmi(extern_) + } + + #[allow(clippy::cast_ptr_alignment)] + pub fn new_with_env( + store: &mut impl AsStoreMut, + env: &FunctionEnv, + ty: FT, + func: F, + ) -> Self + where + FT: Into, + F: Fn(FunctionEnvMut<'_, T>, &[Value]) -> Result, RuntimeError> + + 'static + + Send + + Sync, + { + let fn_ty: FunctionType = ty.into(); + let params = fn_ty.params(); + + let mut param_types = params + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_param_types = unsafe { + let mut vec = Default::default(); + wasm_valtype_vec_new(&mut vec, param_types.len(), param_types.as_ptr()); + vec + }; + + let results = fn_ty.results(); + let mut result_types = results + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_result_types = unsafe { + let mut vec = Default::default(); + wasm_valtype_vec_new(&mut vec, result_types.len(), result_types.as_ptr()); + vec + }; + + let wasm_functype = unsafe { + wasm_functype_new( + &mut wasm_param_types as *mut _, + &mut wasm_result_types as *mut _, + ) + }; + + let mut store = store.as_store_mut(); + let inner = store.inner.store.as_wasmi().inner; + + let callback: CCallback = make_fn_callback(&func, param_types.len()); + + let mut callback_env: *mut FunctionCallbackEnv<'_, F> = + Box::leak(Box::new(FunctionCallbackEnv { + store, + func, + env_handle: Some(env.as_wasmi().handle.clone()), + })); + + let wasm_function = unsafe { + wasm_func_new_with_env( + inner, + wasm_functype, + Some(callback), + callback_env as *mut _ as _, + None, + ) + }; + + if wasm_function.is_null() { + panic!("failed when creating new typed function"); + } + + Function { + handle: wasm_function, + } + } + + /// Creates a new host `Function` from a native function. + pub fn new_typed(store: &mut impl AsStoreMut, func: F) -> Self + where + F: crate::HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync, + Args: WasmTypeList, + Rets: WasmTypeList, + { + let mut param_types = Args::wasm_types() + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_param_types = unsafe { + let mut vec = Default::default(); + wasm_valtype_vec_new(&mut vec, param_types.len(), param_types.as_ptr()); + vec + }; + + let mut result_types = Rets::wasm_types() + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_result_types = unsafe { + let mut vec = Default::default(); + wasm_valtype_vec_new(&mut vec, result_types.len(), result_types.as_ptr()); + vec + }; + + let wasm_functype = + unsafe { wasm_functype_new(&mut wasm_param_types, &mut wasm_result_types) }; + + let mut store = store.as_store_mut(); + let inner = store.inner.store.as_wasmi().inner; + + let callback: CCallback = unsafe { + std::mem::transmute(func.function_callback(crate::Runtime::Wasmi).into_wasmi()) + }; + + let mut callback_env: *mut FunctionCallbackEnv<'_, F> = + Box::into_raw(Box::new(FunctionCallbackEnv { + store, + func, + env_handle: None, + })); + + let wasm_function = unsafe { + wasm_func_new_with_env( + inner, + wasm_functype, + Some(callback), + callback_env as _, + None, + ) + }; + + if wasm_function.is_null() { + panic!("failed when creating new typed function"); + } + + Function { + handle: wasm_function, + } + } + + pub fn new_typed_with_env( + store: &mut impl AsStoreMut, + env: &FunctionEnv, + func: F, + ) -> Self + where + F: crate::HostFunction, + Args: WasmTypeList, + Rets: WasmTypeList, + T: Send + 'static, + { + let mut param_types = Args::wasm_types() + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_param_types = unsafe { + let mut vec = wasm_valtype_vec_t::default(); + wasm_valtype_vec_new(&mut vec, param_types.len(), param_types.as_ptr()); + vec + }; + + let mut result_types = Rets::wasm_types() + .into_iter() + .map(|param| { + let kind = param.into_ct(); + unsafe { wasm_valtype_new(kind) } + }) + .collect::>(); + + let mut wasm_result_types = unsafe { + let mut vec: wasm_valtype_vec_t = Default::default(); + wasm_valtype_vec_new(&mut vec, result_types.len(), result_types.as_ptr()); + vec + }; + + let wasm_functype = unsafe { + wasm_functype_new( + &mut wasm_param_types as *mut _, + &mut wasm_result_types as *mut _, + ) + }; + + let mut store = store.as_store_mut(); + let inner = store.inner.store.as_wasmi().inner; + + let callback: CCallback = unsafe { + std::mem::transmute(func.function_callback(crate::Runtime::Wasmi).into_wasmi()) + }; + + let mut callback_env: *mut FunctionCallbackEnv<'_, F> = + Box::into_raw(Box::new(FunctionCallbackEnv { + store, + func, + env_handle: Some(env.as_wasmi().handle.clone()), + })); + + let wasm_function = unsafe { + wasm_func_new_with_env( + inner, + wasm_functype, + Some(callback), + callback_env as _, + None, + ) + }; + + if wasm_function.is_null() { + panic!("failed when creating new typed function"); + } + + Function { + handle: wasm_function, + } + } + + pub fn ty(&self, _store: &impl AsStoreRef) -> FunctionType { + let type_ = unsafe { wasm_func_type(self.handle) }; + let params: *const wasm_valtype_vec_t = unsafe { wasm_functype_params(type_) }; + let returns: *const wasm_valtype_vec_t = unsafe { wasm_functype_results(type_) }; + + let params: Vec = unsafe { + let mut res = vec![]; + for i in 0..(*params).size { + res.push((*(*params).data.wrapping_add(i)).into_wt()); + } + res + }; + + let returns: Vec = unsafe { + let mut res = vec![]; + for i in 0..(*returns).size { + res.push((*(*returns).data.wrapping_add(i)).into_wt()); + } + res + }; + + FunctionType::new(params, returns) + } + + pub fn call_raw( + &self, + _store: &mut impl AsStoreMut, + _params: Vec, + ) -> Result, RuntimeError> { + // There is no optimal call_raw in JSC, so we just + // simply rely the call + // self.call(store, params) + unimplemented!(); + } + + pub fn call( + &self, + store: &mut impl AsStoreMut, + params: &[Value], + ) -> Result, RuntimeError> { + // unimplemented!(); + let store_mut = store.as_store_mut(); + // let wasm_func_param_arity(self.handle) + + let mut args = { + unsafe { + let mut wasm_params = params + .into_iter() + .map(|v| 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 = { + unsafe { + let mut vec = Default::default(); + wasm_val_vec_new_uninitialized(&mut vec, size); + vec + } + }; + + let trap = unsafe { wasm_func_call(self.handle, &mut args as _, &mut results as *mut _) }; + + if !trap.is_null() { + return Err(Into::::into(trap).into()); + } + + unsafe { + let results = std::ptr::slice_from_raw_parts(results.data, results.size); + return Ok((*results) + .into_iter() + .map(|v| IntoWasmerValue::into_wv(*v)) + .collect::>() + .into_boxed_slice()); + } + } + + pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, internal: VMExternFunction) -> Self { + Self { + handle: internal.into_wasmi(), + } + } + + pub(crate) fn vm_funcref(&self, _store: &impl AsStoreRef) -> VMFuncRef { + unimplemented!(); + } + + pub(crate) unsafe fn from_vm_funcref( + _store: &mut impl AsStoreMut, + _funcref: VMFuncRef, + ) -> Self { + unimplemented!(); + } + + /// Checks whether this `Function` can be used with the given context. + pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} + +fn make_fn_callback(func: &F, args: usize) -> CCallback +where + F: Fn(FunctionEnvMut<'_, T>, &[Value]) -> Result, RuntimeError> + + 'static + + Send + + Sync, +{ + unsafe extern "C" fn fn_callback( + env: *mut c_void, + args: *const wasm_val_vec_t, + rets: *mut wasm_val_vec_t, + ) -> *mut wasm_trap_t + where + F: Fn(FunctionEnvMut<'_, T>, &[Value]) -> Result, RuntimeError> + + 'static + + Send + + Sync, + { + let r: *mut (FunctionCallbackEnv<'_, F>) = env as _; + + let mut store = (*r).store.as_store_mut(); + let env_handle = (*r).env_handle.as_ref().unwrap().clone(); + let mut fn_env = env::FunctionEnv::from_handle(env_handle).into_mut(&mut store); + let func: &F = &(*r).func; + + let mut wasmer_args = vec![]; + + for i in 0..(*args).size { + wasmer_args.push((*(*args).data.wrapping_add(i)).into_wv().clone()); + } + + let result = panic::catch_unwind(AssertUnwindSafe(|| unsafe { + func(fn_env.into(), wasmer_args.as_slice()) + })); + + match result { + Ok(Ok(native_results)) => { + let mut c_results: Vec = native_results + .into_iter() + .map(IntoCApiValue::into_cv) + .collect(); + + if c_results.len() != (*rets).size { + panic!("when calling host function: number of observed results differ from wanted results") + } + + unsafe { + for i in 0..(*rets).size { + *((*rets).data.wrapping_add(i)) = c_results[i] + } + } + + unsafe { std::ptr::null_mut() } + } + + Ok(Err(e)) => { + let trap: Trap = Trap::user(Box::new(e)); + unsafe { trap.into_wasm_trap(&mut store) } + } + + Err(e) => { + unimplemented!("host function panicked"); + } + } + } + + return fn_callback::; +} + +impl std::fmt::Debug for Function { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.debug_struct("Function").finish() + } +} + +impl crate::Function { + /// Consume [`self`] into [`crate::rt::wasmi::function::Function`]. + pub fn into_wasmi(self) -> crate::rt::wasmi::function::Function { + match self.0 { + RuntimeFunction::Wasmi(s) => s, + _ => panic!("Not a `wasmi` function!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::wasmi::function::Function`]. + pub fn as_wasmi(&self) -> &crate::rt::wasmi::function::Function { + match self.0 { + RuntimeFunction::Wasmi(ref s) => s, + _ => panic!("Not a `wasmi` function!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wasmi::function::Function`]. + pub fn as_wasmi_mut(&mut self) -> &mut crate::rt::wasmi::function::Function { + match self.0 { + RuntimeFunction::Wasmi(ref mut s) => s, + _ => panic!("Not a `wasmi` function!"), + } + } +} + +macro_rules! impl_host_function { + ([$c_struct_representation:ident] $c_struct_name:ident, $( $x:ident ),* ) => { + paste::paste! { + #[allow(non_snake_case)] + pub(crate) fn [] + <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, Func: Fn($( $x , )*) -> RetsAsResult + 'static> + (this: &Func) -> crate::rt::wasmi::vm::VMFunctionCallback { + + /// This is a function that wraps the real host + /// function. Its address will be used inside the + /// runtime. + unsafe extern "C" fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func>(env: *mut c_void, args: *const wasm_val_vec_t, results: *mut wasm_val_vec_t) -> *mut wasm_trap_t + where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + RetsAsResult: IntoResult, + Func: Fn($( $x , )*) -> RetsAsResult + 'static, + { + let mut r: *mut crate::rt::wasmi::function::FunctionCallbackEnv = unsafe {std::mem::transmute(env)}; + let store = &mut (*r).store.as_store_mut(); + let mut i = 0; + + $( + let c_arg = (*(*args).data.wrapping_add(i)).clone(); + let wasmer_arg = c_arg.into_wv(); + let raw_arg : RawValue = wasmer_arg.as_raw(store); + let $x : $x = FromToNativeWasmType::from_native($x::Native::from_raw(store, raw_arg)); + + i += 1; + )* + + let result = panic::catch_unwind(AssertUnwindSafe(|| unsafe { + ((*r).func)( $( $x, )* ).into_result() + })); + + match result { + Ok(Ok(result)) => { + + let types = Rets::wasm_types(); + let mut native_results = result.into_array(store); + let native_results = native_results.as_mut(); + + let native_results: Vec = native_results.into_iter().enumerate() + .map(|(i, r)| Value::from_raw(store, types[i], r.clone())) + .collect(); + + let mut c_results: Vec = native_results.into_iter().map(IntoCApiValue::into_cv).collect(); + + if c_results.len() != (*results).size { + panic!("when calling host function: number of observed results differ from wanted results") + } + + unsafe { + for i in 0..(*results).size { + *((*results).data.wrapping_add(i)) = c_results[i] + } + } + + unsafe { std::ptr::null_mut() } + }, + + Ok(Err(e)) => { + let trap = crate::rt::wasmi::error::Trap::user(Box::new(e)); + unsafe { trap.into_wasm_trap(store) } + // unimplemented!("host function panicked"); + }, + + Err(e) => { + unimplemented!("host function panicked"); + } + } + } + func_wrapper::< $( $x, )* Rets, RetsAsResult, Func> as _ + } + + + #[allow(non_snake_case)] + pub(crate) fn [] + <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult, T: Send + 'static, Func: Fn(FunctionEnvMut, $( $x , )*) -> RetsAsResult + 'static> + (this: &Func) -> crate::rt::wasmi::vm::VMFunctionCallback { + unsafe extern "C" fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func, T>(env: *mut c_void, args: *const wasm_val_vec_t, results: *mut wasm_val_vec_t) -> *mut wasm_trap_t + where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + RetsAsResult: IntoResult, + T: Send + 'static, + Func: Fn(FunctionEnvMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static, + { + + let r: *mut (crate::rt::wasmi::function::FunctionCallbackEnv<'_, Func>) = env as _; + let store = &mut (*r).store.as_store_mut(); + + let mut i = 0; + + $( + let c_arg = (*(*args).data.wrapping_add(i)).clone(); + let wasmer_arg = c_arg.into_wv(); + let raw_arg : RawValue = wasmer_arg.as_raw(store); + let $x : $x = FromToNativeWasmType::from_native($x::Native::from_raw(store, raw_arg)); + + i += 1; + )* + + let env_handle = (*r).env_handle.as_ref().unwrap().clone(); + let mut fn_env = crate::rt::wasmi::function::env::FunctionEnv::from_handle(env_handle).into_mut(store); + let func: &Func = &(*r).func; + + let result = panic::catch_unwind(AssertUnwindSafe(|| unsafe { + ((*r).func)(RuntimeFunctionEnvMut::Wasmi(fn_env).into(), $( $x, )* ).into_result() + })); + + + match result { + Ok(Ok(result)) => { + let types = Rets::wasm_types(); + let mut native_results = result.into_array(store); + let native_results = native_results.as_mut(); + + let native_results: Vec = native_results.into_iter().enumerate().map(|(i, r)| Value::from_raw(store, types[i], r.clone())).collect(); + + let mut c_results: Vec = native_results.into_iter().map(IntoCApiValue::into_cv).collect(); + + if c_results.len() != (*results).size { + panic!("when calling host function: number of observed results differ from wanted results") + } + + unsafe { + for i in 0..(*results).size { + *((*results).data.wrapping_add(i)) = c_results[i] + } + + } + + unsafe { std::ptr::null_mut() } + }, + + Ok(Err(e)) => { let trap = crate::rt::wasmi::error::Trap::user(Box::new(e)); unsafe { trap.into_wasm_trap(store) } }, + + Err(e) => { unimplemented!("host function panicked"); } + } + } + + func_wrapper::< $( $x, )* Rets, RetsAsResult, Func, T> as _ + } + } + }; +} + +// Here we go! Let's generate all the C struct, `WasmTypeList` +// implementations and `HostFunction` implementations. +impl_host_function!([C] S0,); +impl_host_function!([transparent] S1, A1); +impl_host_function!([C] S2, A1, A2); +impl_host_function!([C] S3, A1, A2, A3); +impl_host_function!([C] S4, A1, A2, A3, A4); +impl_host_function!([C] S5, A1, A2, A3, A4, A5); +impl_host_function!([C] S6, A1, A2, A3, A4, A5, A6); +impl_host_function!([C] S7, A1, A2, A3, A4, A5, A6, A7); +impl_host_function!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8); +impl_host_function!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9); +impl_host_function!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); +impl_host_function!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); +impl_host_function!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); +impl_host_function!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); +impl_host_function!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); +impl_host_function!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); +impl_host_function!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16); +impl_host_function!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17); +impl_host_function!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18); +impl_host_function!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19); +impl_host_function!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20); +impl_host_function!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21); +impl_host_function!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22); +impl_host_function!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23); +impl_host_function!([C] S24, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24); +impl_host_function!([C] S25, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25); +impl_host_function!([C] S26, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26); diff --git a/lib/api/src/rt/wasmi/entities/function/typed.rs b/lib/api/src/rt/wasmi/entities/function/typed.rs new file mode 100644 index 00000000000..32c02a8f01c --- /dev/null +++ b/lib/api/src/rt/wasmi/entities/function/typed.rs @@ -0,0 +1,133 @@ +//! Native Functions. +//! +//! This module creates the helper `TypedFunction` that let us call WebAssembly +//! functions with the native ABI, that is: +//! +//! ```ignore +//! let add_one = instance.exports.get_function("function_name")?; +//! let add_one_native: TypedFunction = add_one.typed().unwrap(); +//! ``` + +use std::iter::FromIterator; + +use crate::{ + rt::wasmi::{bindings::*, error::Trap, function::Function, utils::convert::*}, + AsStoreMut, FromToNativeWasmType, NativeWasmType, NativeWasmTypeInto, RuntimeError, + TypedFunction, Value, WasmTypeList, +}; +use wasmer_types::RawValue; + +macro_rules! impl_native_traits { + ( $( $x:ident ),* ) => { + #[allow(unused_parens, non_snake_case)] + impl<$( $x , )* Rets> TypedFunction<( $( $x ),* ), Rets> + where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + { + /// Call the typed func and return results. + #[allow(clippy::too_many_arguments)] + pub fn call_wasmi(&self, mut store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result where + $( $x: FromToNativeWasmType, )* + { + + // // let store_ptr = Value::I64(store.as_store_mut().as_raw() as _).as_jsvalue(store); + #[allow(unused_unsafe)] + let params_list: Vec<_> = unsafe { + 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() + } ),* ] + }; + + let mut results = { + 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().into_wasmi()) }; + + let trap = { + unsafe { + let mut params = std::mem::zeroed(); + wasm_val_vec_new(&mut params, params_list.len(), params_list.as_ptr()); + wasm_func_call(func, ¶ms, &mut results) + } + }; + + if !trap.is_null() { + unsafe { + let trap: Trap = trap.into(); + return Err(RuntimeError::from(trap)); + } + } + + unsafe { + let mut rets_list_array = Rets::empty_array(); + let mut_rets = rets_list_array.as_mut() as *mut [RawValue] as *mut RawValue; + + match Rets::size() { + 0 => {}, + 1 => { + let val = (*results.data.wrapping_add(0)).clone(); + let val = val.into_wv(); + *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 slot = mut_rets.add(i); + *slot = val.as_raw(&mut store); + } + } + } + + Ok(unsafe { Rets::from_array(store, rets_list_array) }) + } + } + + #[doc(hidden)] + #[allow(missing_docs)] + #[allow(unused_mut)] + #[allow(clippy::too_many_arguments)] + pub fn call_raw_wasmi(&self, store: &mut impl AsStoreMut, mut params_list: Vec ) -> Result { + todo!("Raw calls from wasmi are not supported yet!") + } + } + }; + +} + +impl_native_traits!(); +impl_native_traits!(A1); +impl_native_traits!(A1, A2); +impl_native_traits!(A1, A2, A3); +impl_native_traits!(A1, A2, A3, A4); +impl_native_traits!(A1, A2, A3, A4, A5); +impl_native_traits!(A1, A2, A3, A4, A5, A6); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17); +impl_native_traits!( + A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18 +); +impl_native_traits!( + A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19 +); +impl_native_traits!( + A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20 +); diff --git a/lib/api/src/rt/wasmi/entities/global.rs b/lib/api/src/rt/wasmi/entities/global.rs new file mode 100644 index 00000000000..33040834230 --- /dev/null +++ b/lib/api/src/rt/wasmi/entities/global.rs @@ -0,0 +1,122 @@ +//! Data types, functions and traits for `wasmi`'s `Global` implementation. +use wasmer_types::{GlobalType, Mutability}; + +use crate::{ + vm::{VMExtern, VMExternGlobal}, + wasmi::{ + bindings::{ + self, wasm_global_as_extern, wasm_global_get, wasm_global_new, wasm_global_set, + wasm_global_type, wasm_globaltype_content, wasm_globaltype_mutability, + wasm_globaltype_new, wasm_mutability_t, wasm_valtype_new, + }, + utils::convert::{IntoCApiType, IntoCApiValue, IntoWasmerType, IntoWasmerValue}, + vm::VMGlobal, + }, + AsStoreMut, AsStoreRef, RuntimeError, Value, +}; + +#[derive(Debug, Clone, PartialEq, Eq)] +/// A WebAssembly `global` in `wasmi`. +pub struct Global { + pub(crate) handle: VMGlobal, +} + +unsafe impl Send for Global {} +unsafe impl Sync for Global {} + +// Global can't be Send in js because it dosen't support `structuredClone` +// https://developer.mozilla.org/en-US/docs/Web/API/structuredClone +// unsafe impl Send for Global {} + +impl Global { + pub(crate) fn to_vm_extern(&self) -> VMExtern { + let extern_ = unsafe { wasm_global_as_extern(self.handle) }; + assert!( + !extern_.is_null(), + "Returned null Global extern from wasm-c-api" + ); + VMExtern::Wasmi(extern_) + } + + /// Create a `Global` with the initial value [`Value`] and the provided [`Mutability`]. + pub(crate) fn from_value( + store: &mut impl AsStoreMut, + val: Value, + mutability: Mutability, + ) -> Result { + let store = store.as_store_mut(); + + let wasmi_type = val.ty().into_ct(); + let wasmi_value = val.into_cv(); + let wasmi_mutability = if mutability.is_mutable() { + bindings::wasm_mutability_enum_WASM_VAR + } else { + bindings::wasm_mutability_enum_WASM_CONST + } as wasm_mutability_t; + + let wasmi_global_type = + unsafe { wasm_globaltype_new(wasm_valtype_new(wasmi_type), wasmi_mutability) }; + + Ok(Self { + handle: unsafe { + wasm_global_new( + store.inner.store.as_wasmi().inner, + wasmi_global_type, + &wasmi_value, + ) + }, + }) + } + + pub fn ty(&self, _store: &impl AsStoreRef) -> GlobalType { + let r#type = unsafe { wasm_global_type(self.handle) }; + let mutability = unsafe { wasm_globaltype_mutability(&*r#type) }; + let valtype = unsafe { wasm_globaltype_content(r#type) }; + let wasmer_type = valtype.into_wt(); + + GlobalType::new( + wasmer_type, + if mutability == bindings::wasm_mutability_enum_WASM_VAR as u8 { + Mutability::Var + } else { + Mutability::Const + }, + ) + } + + pub fn get(&self, store: &mut impl AsStoreMut) -> Value { + let mut out = unsafe { std::mem::zeroed() }; + unsafe { wasm_global_get(self.handle, &mut out) }; + out.into_wv() + } + + pub fn set(&self, store: &mut impl AsStoreMut, val: Value) -> Result<(), RuntimeError> { + if val.ty() != self.ty(store).ty { + return Err(RuntimeError::new(format!( + "Incompatible types: {} != {}", + val.ty(), + self.ty(store) + ))); + } + + if self.ty(store).mutability == Mutability::Const { + return Err(RuntimeError::new("The global is immutable".to_owned())); + } + + let value = val.into_cv(); + + unsafe { wasm_global_set(self.handle, &value) }; + + Ok(()) + } + + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_global: VMExternGlobal) -> Self { + Self { + handle: vm_global.into_wasmi(), + } + } + + pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} diff --git a/lib/api/src/rt/wasmi/entities/instance.rs b/lib/api/src/rt/wasmi/entities/instance.rs new file mode 100644 index 00000000000..145221764e0 --- /dev/null +++ b/lib/api/src/rt/wasmi/entities/instance.rs @@ -0,0 +1,145 @@ +//! Data types, functions and traits for `wasmi`'s `Instance` implementation. +use std::sync::Arc; + +use crate::{ + rt::wasmi::bindings::*, vm::VMExtern, wasmi::error::Trap, AsStoreMut, AsStoreRef, Exports, + Extern, Imports, InstantiationError, Module, +}; + +#[derive(PartialEq, Eq)] +pub(crate) struct InstanceHandle(pub(crate) *mut wasm_instance_t); + +unsafe impl Send for InstanceHandle {} +unsafe impl Sync for InstanceHandle {} + +impl InstanceHandle { + fn new( + store: *mut wasm_store_t, + module: *mut wasm_module_t, + mut externs: Vec, + ) -> Result { + // Check if the thread env was already initialised. + //unsafe { + // if !wasm_runtime_thread_env_inited() { + // if !wasm_runtime_init_thread_env() { + // panic!("Failed to initialize the thread environment!"); + // } + // } + //} + + let mut trap: *mut wasm_trap_t = std::ptr::null_mut() as _; + let externs: Vec<_> = externs.into_iter().map(|v| v.into_wasmi()).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, module, &mut imports, &mut trap) + }; + + if instance.is_null() { + let trap = Trap::from(trap); + return Err(InstantiationError::Start(trap.into())); + } + + Ok(InstanceHandle(instance)) + } + + fn get_exports(&self, mut store: &mut impl AsStoreMut, module: &Module) -> Exports { + let mut exports = unsafe { + let mut vec = Default::default(); + wasm_instance_exports(self.0, &mut vec); + vec + }; + + let wasm_exports: &[*mut wasm_extern_t] = + unsafe { std::slice::from_raw_parts(exports.data, exports.size) }; + + let exports_ty = module.exports().collect::>(); + let exports = exports_ty + .iter() + .zip(wasm_exports.into_iter()) + .map(|(export_type, wasm_export)| { + let name = export_type.name(); + let mut store = store.as_store_mut(); + let extern_type = export_type.ty(); + // Annotation is here to prevent spurious IDE warnings. + + let extern_ = Extern::from_vm_extern(&mut store, VMExtern::Wasmi(*wasm_export)); + (name.to_string(), extern_) + }) + .collect::(); + exports + } +} +impl Drop for InstanceHandle { + fn drop(&mut self) { + unsafe { wasm_instance_delete(self.0) } + } +} + +#[derive(Clone, PartialEq, Eq)] +/// A WebAssembly `instance` in `wasmi`. +pub struct Instance { + pub(crate) handle: Arc, +} + +impl Instance { + pub(crate) fn new( + store: &mut impl AsStoreMut, + module: &Module, + imports: &Imports, + ) -> Result<(Self, Exports), InstantiationError> { + let externs = module + .imports() + .map(|import_ty| { + imports + .get_export(import_ty.module(), import_ty.name()) + .expect("Extern not found") + }) + .collect::>(); + + return Self::new_by_index(store, module, &externs); + } + + pub(crate) fn new_by_index( + store: &mut impl AsStoreMut, + module: &Module, + externs: &[Extern], + ) -> Result<(Self, Exports), InstantiationError> { + let store_ref = store.as_store_ref(); + let externs: Vec = externs + .iter() + .map(|extern_| extern_.to_vm_extern()) + .collect::>(); + + let instance = InstanceHandle::new( + store_ref.inner.store.as_wasmi().inner, + module.as_wasmi().handle.inner, + externs, + )?; + let exports = instance.get_exports(store, module); + + Ok(( + Self { + handle: Arc::new(instance), + }, + exports, + )) + } +} + +impl crate::RuntimeInstance { + /// Consume [`self`] into a [`crate::rt::wasmi::instance::Instance`]. + pub(crate) fn into_wasmi(self) -> crate::rt::wasmi::instance::Instance { + match self { + Self::Wasmi(s) => s, + _ => panic!("Not a `wasmi` instance"), + } + } +} diff --git a/lib/api/src/jsc/externals/memory.rs b/lib/api/src/rt/wasmi/entities/memory/mod.rs similarity index 53% rename from lib/api/src/jsc/externals/memory.rs rename to lib/api/src/rt/wasmi/entities/memory/mod.rs index c0d9343c3be..da252b2c4f2 100644 --- a/lib/api/src/jsc/externals/memory.rs +++ b/lib/api/src/rt/wasmi/entities/memory/mod.rs @@ -1,97 +1,75 @@ -use crate::jsc::vm::{VMExtern, VMMemory}; -use crate::mem_access::MemoryAccessError; -use crate::store::{AsStoreMut, AsStoreRef, StoreObjects}; -use crate::MemoryType; -use rusty_jsc::{JSObject, JSValue}; -use std::convert::TryInto; -use std::marker::PhantomData; -use std::mem::{self, MaybeUninit}; -use std::slice; +//! Data types, functions and traits for `wasmi`'s `Memory` implementation. +//! +use std::{marker::PhantomData, mem::MaybeUninit}; use tracing::warn; +pub use wasmer_types::MemoryError; +use wasmer_types::{MemoryType, Pages, WASM_PAGE_SIZE}; -use wasmer_types::{Pages, WASM_PAGE_SIZE}; - -use super::memory_view::MemoryView; +use crate::{ + shared::SharedMemory, + vm::{VMExtern, VMExternMemory}, + wasmi::{bindings::*, vm::VMMemory}, + AsStoreMut, AsStoreRef, MemoryAccessError, RuntimeMemory, +}; -pub use wasmer_types::MemoryError; +pub(crate) mod view; +pub use view::*; #[derive(Debug, Clone)] +/// A WebAssembly `memory` in `wasmi`. pub struct Memory { pub(crate) handle: VMMemory, } -// Only SharedMemories can be Send in js, becuase they support `structuredClone`. -// Normal memories will fail while doing structuredClone. -// In this case, we implement Send just in case as it can be a shared memory. -// https://developer.mozilla.org/en-US/docs/Web/API/structuredClone -// ```js -// const memory = new WebAssembly.Memory({ -// initial: 10, -// maximum: 100, -// shared: true // <--- It must be shared, otherwise structuredClone will fail -// }); -// structuredClone(memory) -// ``` unsafe impl Send for Memory {} unsafe impl Sync for Memory {} impl Memory { pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result { - let vm_memory = VMMemory::new(Self::js_memory_from_type(store, &ty)?, ty); - Ok(Self::from_vm_extern(store, vm_memory)) - } + let limits = Box::into_raw(Box::new(wasm_limits_t { + min: ty.minimum.0, + max: match ty.maximum { + Some(v) => v.0, + None => wasm_limits_max_default, + }, + })); - pub(crate) fn js_memory_from_type( - store: &impl AsStoreRef, - ty: &MemoryType, - ) -> Result { - let store_ref = store.as_store_ref(); - let engine = store_ref.engine(); - let context = engine.0.context(); - - let mut descriptor = JSObject::new(&context); - descriptor.set_property( - &context, - "initial".to_string(), - JSValue::number(&context, ty.minimum.0.into()), - ); - if let Some(max) = ty.maximum { - descriptor.set_property( - &context, - "maximum".to_string(), - JSValue::number(&context, max.0.into()), - ); - } - descriptor.set_property( - &context, - "shared".to_string(), - JSValue::boolean(&context, ty.shared), - ); + let memorytype = unsafe { wasm_memorytype_new(limits) }; - engine - .0 - .wasm_memory_type() - .construct(&context, &[descriptor.to_jsvalue()]) - .map_err(|e| MemoryError::Generic(format!("{:?}", e))) + let mut store = store.as_store_mut(); + let inner = store.inner.store.as_wasmi().inner; + let c_memory = unsafe { wasm_memory_new(inner, memorytype) }; + + Ok(Self { handle: c_memory }) } pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { - Self::from_vm_extern(new_store, memory) + Self { handle: memory } } pub(crate) fn to_vm_extern(&self) -> VMExtern { - VMExtern::Memory(self.handle.clone()) + VMExtern::Wasmi(unsafe { wasm_memory_as_extern(self.handle) }) } pub fn ty(&self, _store: &impl AsStoreRef) -> MemoryType { - self.handle.ty + let memory_type: *mut wasm_memorytype_t = unsafe { wasm_memory_type(self.handle) }; + let limits: *const wasm_limits_t = unsafe { wasm_memorytype_limits(memory_type) }; + + MemoryType { + // [TODO]: Find a way to extract this from the inner memory type instead + // of hardcoding. + shared: false, + minimum: unsafe { wasmer_types::Pages((*limits).min) }, + maximum: unsafe { Some(wasmer_types::Pages((*limits).max)) }, + } } pub fn view<'a>(&self, store: &'a impl AsStoreRef) -> MemoryView<'a> { MemoryView::new(self, store) } + // Note: the return value is the memory size (in [`Pages`]) *before* growing it. pub fn grow( &self, store: &mut impl AsStoreMut, @@ -100,44 +78,19 @@ impl Memory { where IntoPages: Into, { - let pages = delta.into(); - - let store_mut = store.as_store_mut(); - let engine = store_mut.engine(); - let context = engine.0.context(); - let func = self - .handle - .memory - .get_property(&context, "grow".to_string()) - .to_object(&context) - .unwrap(); - match func.call( - &context, - Some(&self.handle.memory), - &[JSValue::number(&context, pages.0 as _)], - ) { - Ok(val) => Ok(Pages(val.to_number(&context).unwrap() as _)), - Err(e) => { - let old_pages = pages; + unsafe { + let delta: Pages = delta.into(); + let current = Pages(wasm_memory_size(self.handle)); + + if !wasm_memory_grow(self.handle, delta.0) { Err(MemoryError::CouldNotGrow { - current: old_pages, - attempted_delta: pages, + current, + attempted_delta: delta, }) + } else { + Ok(current) } } - // let js_memory = &self.handle.memory; - // let our_js_memory: &JSMemory = JsCast::unchecked_from_js_ref(js_memory); - // let new_pages = our_js_memory.grow(pages.0).map_err(|err| { - // if err.is_instance_of::() { - // MemoryError::CouldNotGrow { - // current: self.view(&store.as_store_ref()).size(), - // attempted_delta: pages, - // } - // } else { - // MemoryError::Generic(err.as_string().unwrap()) - // } - // })?; - // Ok(Pages(new_pages)) } pub fn grow_at_least( @@ -145,13 +98,14 @@ impl Memory { store: &mut impl AsStoreMut, min_size: u64, ) -> Result<(), MemoryError> { - let cur_size = self.view(store).data_size(); - if min_size > cur_size { - let delta = min_size - cur_size; - let pages = ((delta - 1) / wasmer_types::WASM_PAGE_SIZE as u64) + 1; - - self.grow(store, Pages(pages as u32))?; + unsafe { + let current = wasm_memory_size(self.handle); + let delta = (min_size as u32) - current; + if delta > 0 { + self.grow(store, delta)?; + } } + Ok(()) } @@ -164,42 +118,49 @@ impl Memory { store: &impl AsStoreRef, new_store: &mut impl AsStoreMut, ) -> Result { - let view = self.view(store); - let ty = self.ty(store); - let amount = view.data_size() as usize; - - let new_memory = Self::new(new_store, ty)?; - let mut new_view = new_memory.view(&new_store); - let new_view_size = new_view.data_size() as usize; - if amount > new_view_size { - let delta = amount - new_view_size; - let pages = ((delta - 1) / wasmer_types::WASM_PAGE_SIZE) + 1; - new_memory.grow(new_store, Pages(pages as u32))?; - new_view = new_memory.view(&new_store); - } - - // Copy the bytes - view.copy_to_memory(amount as u64, &new_view) - .map_err(|err| MemoryError::Generic(err.to_string()))?; - // // Return the new memory - Ok(new_memory) + unimplemented!(); + // let view = self.view(store); + // let ty = self.ty(store); + // let amount = view.data_size() as usize; + + // let new_memory = Self::new(new_store, ty)?; + // let mut new_view = new_memory.view(&new_store); + // let new_view_size = new_view.data_size() as usize; + // if amount > new_view_size { + // let delta = amount - new_view_size; + // let pages = ((delta - 1) / wasmer_types::WASM_PAGE_SIZE) + 1; + // new_memory.grow(new_store, Pages(pages as u32))?; + // new_view = new_memory.view(&new_store); + // } + + // // Copy the bytes + // view.copy_to_memory(amount as u64, &new_view) + // .map_err(|err| MemoryError::Generic(err.to_string()))?; + // // // Return the new memory + // Ok(new_memory) } - pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, internal: VMMemory) -> Self { - Self { handle: internal } + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, internal: VMExternMemory) -> Self { + Self { + handle: internal.into_wasmi(), + } } /// Cloning memory will create another reference to the same memory that /// can be put into a new store pub fn try_clone(&self, _store: &impl AsStoreRef) -> Result { - self.handle.try_clone() + Ok(self.handle.clone()) } /// Copying the memory will actually copy all the bytes in the memory to /// a identical byte copy of the original that can be put into a new store pub fn try_copy(&self, store: &impl AsStoreRef) -> Result { - let mut cloned = self.try_clone(store)?; - cloned.copy(store) + let res = unsafe { wasm_memory_copy(self.handle) }; + if res.is_null() { + Err(MemoryError::Generic("memory copy failed".to_owned())) + } else { + Ok(res) + } } pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { @@ -208,10 +169,11 @@ impl Memory { #[allow(unused)] pub fn duplicate(&mut self, store: &impl AsStoreRef) -> Result { - self.handle.copy(store) + unimplemented!(); + // self.handle.duplicate(store) } - pub fn as_shared(&self, _store: &impl AsStoreRef) -> Option { + pub fn as_shared(&self, _store: &impl AsStoreRef) -> Option { // Not supported. None } @@ -238,12 +200,15 @@ impl<'a> MemoryBuffer<'a> { let end = offset .checked_add(buf.len() as u64) .ok_or(MemoryAccessError::Overflow)?; - if end > self.len.try_into().unwrap() { + + let len: u64 = self.len.try_into().unwrap(); + if end > len { warn!( - "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", + "attempted to read {} bytes, but the end offset is beyond the bounds of the memory view ({} > {}, diff. {} bytes)", buf.len(), end, - self.len + len, + end - len, ); return Err(MemoryAccessError::HeapOutOfBounds); } @@ -261,12 +226,15 @@ impl<'a> MemoryBuffer<'a> { let end = offset .checked_add(buf.len() as u64) .ok_or(MemoryAccessError::Overflow)?; - if end > self.len.try_into().unwrap() { + + let len: u64 = self.len.try_into().unwrap(); + if end > len { warn!( - "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", + "attempted to read {} bytes, but the end offset is beyond the bounds of the memory view ({} > {}, diff. {} bytes)", buf.len(), end, - self.len + len, + end - len, ); return Err(MemoryAccessError::HeapOutOfBounds); } @@ -275,7 +243,7 @@ impl<'a> MemoryBuffer<'a> { volatile_memcpy_read(self.base.add(offset as usize), buf_ptr, buf.len()); } - Ok(unsafe { slice::from_raw_parts_mut(buf_ptr, buf.len()) }) + Ok(unsafe { std::slice::from_raw_parts_mut(buf_ptr, buf.len()) }) } pub(crate) fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { @@ -313,9 +281,9 @@ unsafe fn volatile_memcpy_read(mut src: *const u8, mut dst: *mut u8, mut len: us struct Unaligned(T); let val = (*src as *const Unaligned).read_volatile(); (*dst as *mut Unaligned).write(val); - *src = src.add(mem::size_of::()); - *dst = dst.add(mem::size_of::()); - *len -= mem::size_of::(); + *src = src.add(std::mem::size_of::()); + *dst = dst.add(std::mem::size_of::()); + *len -= std::mem::size_of::(); } while len >= 8 { @@ -331,6 +299,7 @@ unsafe fn volatile_memcpy_read(mut src: *const u8, mut dst: *mut u8, mut len: us copy_one::(&mut src, &mut dst, &mut len); } } + #[inline] unsafe fn volatile_memcpy_write(mut src: *const u8, mut dst: *mut u8, mut len: usize) { #[inline] @@ -339,9 +308,9 @@ unsafe fn volatile_memcpy_write(mut src: *const u8, mut dst: *mut u8, mut len: u struct Unaligned(T); let val = (*src as *const Unaligned).read(); (*dst as *mut Unaligned).write_volatile(val); - *src = src.add(mem::size_of::()); - *dst = dst.add(mem::size_of::()); - *len -= mem::size_of::(); + *src = src.add(std::mem::size_of::()); + *dst = dst.add(std::mem::size_of::()); + *len -= std::mem::size_of::(); } while len >= 8 { @@ -357,3 +326,29 @@ unsafe fn volatile_memcpy_write(mut src: *const u8, mut dst: *mut u8, mut len: u copy_one::(&mut src, &mut dst, &mut len); } } + +impl crate::Memory { + /// Consume [`self`] into a [`crate::rt::wasmi::memory::Memory`]. + pub fn into_wasmi(self) -> crate::rt::wasmi::memory::Memory { + match self.0 { + RuntimeMemory::Wasmi(s) => s, + _ => panic!("Not a `wasmi` memory!"), + } + } + + /// Convert a reference to [`self`] into a reference to [`crate::rt::wasmi::memory::Memory`]. + pub fn as_wasmi(&self) -> &crate::rt::wasmi::memory::Memory { + match self.0 { + RuntimeMemory::Wasmi(ref s) => s, + _ => panic!("Not a `wasmi` memory!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference to [`crate::rt::wasmi::memory::Memory`]. + pub fn as_wasmi_mut(&mut self) -> &mut crate::rt::wasmi::memory::Memory { + match self.0 { + RuntimeMemory::Wasmi(ref mut s) => s, + _ => panic!("Not a `wasmi` memory!"), + } + } +} diff --git a/lib/api/src/rt/wasmi/entities/memory/view.rs b/lib/api/src/rt/wasmi/entities/memory/view.rs new file mode 100644 index 00000000000..8743221c1a3 --- /dev/null +++ b/lib/api/src/rt/wasmi/entities/memory/view.rs @@ -0,0 +1,203 @@ +use std::{marker::PhantomData, mem::MaybeUninit, ops::Range}; + +use wasmer_types::Pages; + +use super::{Memory, MemoryBuffer}; +use crate::{ + rt::wasmi::bindings::{wasm_memory_data, wasm_memory_data_size, wasm_memory_size}, + wasmi::bindings::wasm_memory_t, + AsStoreRef, MemoryAccessError, +}; + +/// A WebAssembly `memory` view. +/// +/// A memory view is used to read and write to the linear memory. +/// +/// After a memory is grown a view must not be used anymore. Views are +/// created using the Memory.grow() method. +#[derive(Debug)] +pub struct MemoryView<'a> { + pub(crate) buffer: MemoryBuffer<'a>, + pub(crate) size: u32, +} + +impl<'a> MemoryView<'a> { + pub(crate) fn new(memory: &Memory, store: &'a (impl AsStoreRef + ?Sized)) -> Self { + let c_memory: *mut wasm_memory_t = memory.handle; + + let len = unsafe { wasm_memory_data_size(c_memory as _).try_into().unwrap() }; + let base: *mut u8 = unsafe { wasm_memory_data(c_memory as _) as _ }; + let size = unsafe { wasm_memory_size(c_memory as _) }; + + Self { + buffer: MemoryBuffer { + base, + len, + marker: PhantomData, + }, + size, + } + } + + /// Returns the pointer to the raw bytes of the `Memory`. + // + // This used by wasmer-c-api, but should be treated + // as deprecated and not used in future code. + #[doc(hidden)] + pub fn data_ptr(&self) -> *mut u8 { + self.buffer.base + } + + /// Returns the size (in bytes) of the `Memory`. + pub fn data_size(&self) -> u64 { + self.buffer.len.try_into().unwrap() + } + + /// Retrieve a slice of the memory contents. + /// + /// # Safety + /// + /// Until the returned slice is dropped, it is undefined behaviour to + /// modify the memory contents in any way including by calling a wasm + /// function that writes to the memory or by resizing the memory. + #[doc(hidden)] + pub unsafe fn data_unchecked(&self) -> &[u8] { + self.data_unchecked_mut() + } + + /// Retrieve a mutable slice of the memory contents. + /// + /// # Safety + /// + /// This method provides interior mutability without an UnsafeCell. Until + /// the returned value is dropped, it is undefined behaviour to read or + /// write to the pointed-to memory in any way except through this slice, + /// including by calling a wasm function that reads the memory contents or + /// by resizing this Memory. + #[allow(clippy::mut_from_ref)] + #[doc(hidden)] + pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] { + std::slice::from_raw_parts_mut(self.buffer.base, self.buffer.len) + } + + /// Returns the size (in [`Pages`]) of the `Memory`. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; + /// # let mut store = Store::default(); + /// # + /// let m = Memory::new(&mut store, MemoryType::new(1, None, false)).unwrap(); + /// + /// assert_eq!(m.view(&mut store).size(), Pages(1)); + /// ``` + pub fn size(&self) -> Pages { + Pages(self.size) + } + + #[inline] + pub(crate) fn buffer(&'a self) -> MemoryBuffer<'a> { + self.buffer + } + + /// Safely reads bytes from the memory at the given offset. + /// + /// The full buffer will be filled, otherwise a `MemoryAccessError` is returned + /// to indicate an out-of-bounds access. + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent writes. + pub fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> { + self.buffer.read(offset, buf) + } + + /// Safely reads a single byte from memory at the given offset + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent writes. + pub fn read_u8(&self, offset: u64) -> Result { + let mut buf = [0u8; 1]; + self.read(offset, &mut buf)?; + Ok(buf[0]) + } + + /// Safely reads bytes from the memory at the given offset. + /// + /// This method is similar to `read` but allows reading into an + /// uninitialized buffer. An initialized view of the buffer is returned. + /// + /// The full buffer will be filled, otherwise a `MemoryAccessError` is returned + /// to indicate an out-of-bounds access. + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent writes. + pub fn read_uninit<'b>( + &self, + offset: u64, + buf: &'b mut [MaybeUninit], + ) -> Result<&'b mut [u8], MemoryAccessError> { + self.buffer.read_uninit(offset, buf) + } + + /// Safely writes bytes to the memory at the given offset. + /// + /// If the write exceeds the bounds of the memory then a `MemoryAccessError` is + /// returned. + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent reads/writes. + pub fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { + self.buffer.write(offset, data) + } + + /// Safely reads a single byte from memory at the given offset + /// + /// This method is guaranteed to be safe (from the host side) in the face of + /// concurrent writes. + pub fn write_u8(&self, offset: u64, val: u8) -> Result<(), MemoryAccessError> { + let buf = [val]; + self.write(offset, &buf)?; + Ok(()) + } + + #[allow(unused)] + /// Copies the memory and returns it as a vector of bytes + pub fn copy_to_vec(&self) -> Result, MemoryAccessError> { + self.copy_range_to_vec(0..self.data_size()) + } + + #[allow(unused)] + /// Copies a range of the memory and returns it as a vector of bytes + pub fn copy_range_to_vec(&self, range: Range) -> Result, MemoryAccessError> { + let mut new_memory = Vec::new(); + let mut offset = range.start; + let end = range.end.min(self.data_size()); + let mut chunk = [0u8; 40960]; + while offset < end { + let remaining = end - offset; + let sublen = remaining.min(chunk.len() as u64) as usize; + self.read(offset, &mut chunk[..sublen])?; + new_memory.extend_from_slice(&chunk[..sublen]); + offset += sublen as u64; + } + Ok(new_memory) + } + + #[allow(unused)] + /// Copies the memory to another new memory object + pub fn copy_to_memory(&self, amount: u64, new_memory: &Self) -> Result<(), MemoryAccessError> { + let mut offset = 0; + let mut chunk = [0u8; 40960]; + while offset < amount { + let remaining = amount - offset; + let sublen = remaining.min(chunk.len() as u64) as usize; + self.read(offset, &mut chunk[..sublen])?; + + new_memory.write(offset, &chunk[..sublen])?; + + offset += sublen as u64; + } + Ok(()) + } +} diff --git a/lib/api/src/rt/wasmi/entities/mod.rs b/lib/api/src/rt/wasmi/entities/mod.rs new file mode 100644 index 00000000000..e842f2f6659 --- /dev/null +++ b/lib/api/src/rt/wasmi/entities/mod.rs @@ -0,0 +1,9 @@ +pub mod engine; +pub(crate) mod external; +pub(crate) mod function; +pub(crate) mod global; +pub(crate) mod instance; +pub(crate) mod memory; +pub(crate) mod module; +pub(crate) mod store; +pub(crate) mod table; diff --git a/lib/api/src/rt/wasmi/entities/module.rs b/lib/api/src/rt/wasmi/entities/module.rs new file mode 100644 index 00000000000..0bb7c524b2d --- /dev/null +++ b/lib/api/src/rt/wasmi/entities/module.rs @@ -0,0 +1,189 @@ +//! Data types, functions and traits for `wasmi`'s `Module` implementation. +use std::{path::Path, sync::Arc}; + +use crate::{ + rt::wasmi::bindings::{wasm_byte_vec_t, wasm_module_delete, wasm_module_new, wasm_module_t}, + AsEngineRef, IntoBytes, RuntimeModule, +}; + +use bytes::Bytes; +use wasmer_types::{ + CompileError, DeserializeError, ExportType, ExportsIterator, ExternType, FunctionType, + GlobalType, ImportType, ImportsIterator, MemoryType, ModuleInfo, Mutability, Pages, + SerializeError, TableType, Type, +}; +pub(crate) struct ModuleHandle { + pub(crate) inner: *mut wasm_module_t, + pub(crate) store: std::sync::Mutex, +} + +impl PartialEq for ModuleHandle { + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner + // && self.store.lock() == other.store.lock() + } +} + +impl Eq for ModuleHandle {} + +impl ModuleHandle { + fn new(engine: &impl AsEngineRef, binary: &[u8]) -> Result { + let bytes = wasm_byte_vec_t { + size: binary.len(), + data: binary.as_ptr() as _, + }; + + let store = crate::store::Store::new(engine.as_engine_ref().engine().clone()); + + let inner = + unsafe { wasm_module_new(store.inner.store.as_wasmi().inner, &bytes as *const _) }; + let store = std::sync::Mutex::new(store); + + if inner.is_null() { + return Err(CompileError::Validate(format!("module is null"))); + } + + Ok(ModuleHandle { inner, store }) + } +} +impl Drop for ModuleHandle { + fn drop(&mut self) { + unsafe { wasm_module_delete(self.inner) } + } +} + +#[derive(Clone, PartialEq, Eq)] +/// A WebAssembly `module` in `wasmi`. +pub struct Module { + pub(crate) handle: Arc, + name: Option, + raw_bytes: Option, + info: ModuleInfo, +} + +unsafe impl Send for Module {} +unsafe impl Sync for Module {} + +impl Module { + pub(crate) fn from_binary( + _engine: &impl AsEngineRef, + binary: &[u8], + ) -> Result { + unsafe { Self::from_binary_unchecked(_engine, binary) } + } + + pub(crate) unsafe fn from_binary_unchecked( + engine: &impl AsEngineRef, + binary: &[u8], + ) -> Result { + let mut binary = binary.to_vec(); + let binary = binary.into_bytes(); + let module = ModuleHandle::new(engine, &binary)?; + let info = crate::utils::polyfill::translate_module(&binary[..]) + .unwrap() + .info; + + Ok(Self { + handle: Arc::new(module), + name: info.name.clone(), + raw_bytes: Some(binary.into_bytes()), + info, + }) + } + + pub fn validate(engine: &impl AsEngineRef, binary: &[u8]) -> Result<(), CompileError> { + let engine = engine.as_engine_ref(); + unimplemented!(); + } + + pub fn name(&self) -> Option<&str> { + self.name.as_ref().map(|s| s.as_ref()) + } + + pub fn serialize(&self) -> Result { + return self.raw_bytes.clone().ok_or(SerializeError::Generic( + "Not able to serialize module".to_string(), + )); + } + + pub unsafe fn deserialize_unchecked( + engine: &impl AsEngineRef, + bytes: impl IntoBytes, + ) -> Result { + Self::deserialize(engine, bytes) + } + + pub unsafe fn deserialize( + engine: &impl AsEngineRef, + bytes: impl IntoBytes, + ) -> Result { + return Self::from_binary(engine, &bytes.into_bytes()) + .map_err(|e| DeserializeError::Compiler(e)); + } + + pub unsafe fn deserialize_from_file_unchecked( + engine: &impl AsEngineRef, + path: impl AsRef, + ) -> Result { + let bytes = std::fs::read(path.as_ref())?; + Self::deserialize_unchecked(engine, bytes) + } + + pub unsafe fn deserialize_from_file( + engine: &impl AsEngineRef, + path: impl AsRef, + ) -> Result { + let bytes = std::fs::read(path.as_ref())?; + Self::deserialize(engine, bytes) + } + + pub fn set_name(&mut self, name: &str) -> bool { + self.name = Some(name.to_string()); + true + } + + pub fn imports<'a>(&'a self) -> ImportsIterator + 'a>> { + self.info().imports() + } + + pub fn exports<'a>(&'a self) -> ExportsIterator + 'a>> { + self.info().exports() + } + + pub fn custom_sections<'a>( + &'a self, + name: &'a str, + ) -> Box> + 'a> { + self.info().custom_sections(name) + } + + pub(crate) fn info(&self) -> &ModuleInfo { + &self.info + } +} + +impl crate::Module { + /// Consume [`self`] into a reference [`crate::rt::wasmi::module::Module`]. + pub fn into_wasmi(self) -> crate::rt::wasmi::module::Module { + match self.0 { + RuntimeModule::Wasmi(s) => s, + _ => panic!("Not a `wasmi` module!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::wasmi::module::Module`]. + pub fn as_wasmi(&self) -> &crate::rt::wasmi::module::Module { + match self.0 { + RuntimeModule::Wasmi(ref s) => s, + _ => panic!("Not a `wasmi` module!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wasmi::module::Module`]. + pub fn as_wasmi_mut(&mut self) -> &mut crate::rt::wasmi::module::Module { + match self.0 { + RuntimeModule::Wasmi(ref mut s) => s, + _ => panic!("Not a `wasmi` module!"), + } + } +} diff --git a/lib/api/src/rt/wasmi/entities/store/mod.rs b/lib/api/src/rt/wasmi/entities/store/mod.rs new file mode 100644 index 00000000000..71b2b10152b --- /dev/null +++ b/lib/api/src/rt/wasmi/entities/store/mod.rs @@ -0,0 +1,103 @@ +//! Data types, functions and traits for `wasmi`'s `Store` implementation. +use crate::{ + engine::{AsEngineRef, Engine, EngineRef}, + rt::wasmi::bindings::{wasm_store_delete, wasm_store_new, wasm_store_t}, + AsStoreRef, RuntimeStore, StoreRef, +}; + +mod obj; +pub use obj::*; + +/// A WebAssembly `store` in `wasmi`. +pub(crate) struct Store { + pub(crate) engine: Engine, + pub(crate) inner: *mut wasm_store_t, +} + +impl std::fmt::Debug for Store { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Store") + .field("engine", &self.engine) + .finish() + } +} + +impl Store { + pub(crate) fn new(engine: crate::engine::Engine) -> Self { + let inner: *mut wasm_store_t = unsafe { wasm_store_new(engine.as_wasmi().inner.engine) }; + Store { inner, engine } + } + + pub(crate) fn engine(&self) -> &Engine { + &self.engine + } + + pub(crate) fn engine_mut(&mut self) -> &mut Engine { + &mut self.engine + } +} + +impl Drop for Store { + fn drop(&mut self) { + unsafe { wasm_store_delete(self.inner) } + } +} + +impl AsEngineRef for Store { + fn as_engine_ref(&self) -> EngineRef<'_> { + EngineRef::new(&self.engine) + } +} + +impl crate::RuntimeStore { + /// Consume [`self`] into [`crate::rt::wasmi::store::Store`]. + pub fn into_wasmi(self) -> crate::rt::wasmi::store::Store { + match self { + Self::Wasmi(s) => s, + _ => panic!("Not a `wasmi` store!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::wasmi::store::Store`]. + pub fn as_wasmi(&self) -> &crate::rt::wasmi::store::Store { + match self { + Self::Wasmi(s) => s, + _ => panic!("Not a `wasmi` store!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wasmi::store::Store`]. + pub fn as_wasmi_mut(&mut self) -> &mut crate::rt::wasmi::store::Store { + match self { + Self::Wasmi(s) => s, + _ => panic!("Not a `wasmi` store!"), + } + } + + /// Return true if [`self`] is a store from the `wasmi` runtime. + pub fn is_wasmi(&self) -> bool { + matches!(self, Self::Wasmi(_)) + } +} + +impl crate::Store { + /// Consume [`self`] into [`crate::rt::wasmi::store::Store`]. + pub(crate) fn into_wasmi(self) -> crate::rt::wasmi::store::Store { + self.inner.store.into_wasmi() + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::wasmi::store::Store`]. + pub(crate) fn as_wasmi(&self) -> &crate::rt::wasmi::store::Store { + self.inner.store.as_wasmi() + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wasmi::store::Store`]. + pub(crate) fn as_wasmi_mut(&mut self) -> &mut crate::rt::wasmi::store::Store { + self.inner.store.as_wasmi_mut() + } + + /// Return true if [`self`] is a store from the `wasmi` runtime. + pub fn is_wasmi(&self) -> bool { + self.inner.store.is_wasmi() + } +} diff --git a/lib/api/src/rt/wasmi/entities/store/obj.rs b/lib/api/src/rt/wasmi/entities/store/obj.rs new file mode 100644 index 00000000000..8658b920514 --- /dev/null +++ b/lib/api/src/rt/wasmi/entities/store/obj.rs @@ -0,0 +1,288 @@ +use std::{fmt, marker::PhantomData, num::NonZeroUsize}; + +use crate::{ + rt::wasmi::vm::{VMFunctionEnvironment, VMGlobal}, + AsStoreMut, +}; + +pub use wasmer_types::StoreId; + +impl crate::StoreObjects { + /// Consume [`self`] into [`crate::rt::wasmi::store::StoreObjects`]. + pub fn into_wasmi(self) -> crate::rt::wasmi::store::StoreObjects { + match self { + crate::StoreObjects::Wasmi(s) => s, + _ => panic!("Not a `wasmi` store!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::wasmi::store::StoreObjects`]. + pub fn as_wasmi(&self) -> &crate::rt::wasmi::store::StoreObjects { + match self { + crate::StoreObjects::Wasmi(s) => s, + _ => panic!("Not a `wasmi` store!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wasmi::store::StoreObjects`]. + pub fn as_wasmi_mut(&mut self) -> &mut crate::rt::wasmi::store::StoreObjects { + match self { + crate::StoreObjects::Wasmi(s) => s, + _ => panic!("Not a `wasmi` store!"), + } + } +} + +/// Trait to represent an object managed by a context. This is implemented on +/// the VM types managed by the context. +pub trait StoreObject: Sized { + fn list(store: &StoreObjects) -> &Vec; + fn list_mut(store: &mut StoreObjects) -> &mut Vec; +} + +macro_rules! impl_store_object { + ($($field:ident => $ty:ty,)*) => { + $( + impl StoreObject for $ty { + fn list(store: &StoreObjects) -> &Vec { + &store.$field + } + fn list_mut(store: &mut StoreObjects) -> &mut Vec { + &mut store.$field + } + } + )* + }; + } + +impl_store_object! { + // Note: we store the globals in order to be able to access them later via + // `StoreObjects::iter_globals`. + globals => VMGlobal, + // functions => VMFunction, + // tables => VMTable, + // memories => VMMemory, + // The function environments are the only things attached to a store, + // since the other JS objects (table, globals, memory and functions) + // live in the JS VM Store by default + function_environments => VMFunctionEnvironment, +} + +/// Set of objects managed by a context. +#[derive(Default, Debug)] +pub struct StoreObjects { + id: StoreId, + globals: Vec, + function_environments: Vec, +} + +impl StoreObjects { + /// Returns the ID of this context. + pub fn id(&self) -> StoreId { + self.id + } + + /// Sets the ID of this store + pub fn set_id(&mut self, id: StoreId) { + self.id = id; + } + + /// Returns a pair of mutable references from two handles. + /// + /// Panics if both handles point to the same object. + pub fn get_2_mut( + &mut self, + a: InternalStoreHandle, + b: InternalStoreHandle, + ) -> (&mut T, &mut T) { + assert_ne!(a.index(), b.index()); + let list = T::list_mut(self); + if a.index() < b.index() { + let (low, high) = list.split_at_mut(b.index()); + (&mut low[a.index()], &mut high[0]) + } else { + let (low, high) = list.split_at_mut(a.index()); + (&mut high[0], &mut low[a.index()]) + } + } + + /// Return an immutable iterator over all globals + pub fn iter_globals(&self) -> core::slice::Iter { + self.globals.iter() + } + + /// Return an vector of all globals and converted to u128 + pub fn as_u128_globals(&self) -> Vec { + vec![] + // self.iter_globals() + // .map(|v| v.global.value().as_f64().unwrap() as u128) + // .collect() + } + + /// Set a global, at index idx. Will panic if idx is out of range + /// Safety: the caller should check taht the raw value is compatible + /// with destination VMGlobal type + pub fn set_global_unchecked(&self, idx: usize, new_val: u128) { + assert!(idx < self.globals.len()); + // let g = &self.globals[idx].global; + // let cur_val = g.value().as_f64().unwrap(); + // let new_val = new_val as f64; + // if cur_val != new_val { + // let new_value = JSValue::from(new_val); + // g.set_value(&new_value); + // } + } +} + +/// Handle to an object managed by a context. +/// +/// Internally this is just an integer index into a context. A reference to the +/// context must be passed in separately to access the actual object. +pub struct StoreHandle { + id: StoreId, + internal: InternalStoreHandle, +} + +impl core::cmp::PartialEq for StoreHandle { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl std::hash::Hash for StoreHandle { + fn hash(&self, state: &mut H) { + self.id.hash(state); + self.internal.idx.hash(state); + } +} + +impl Clone for StoreHandle { + fn clone(&self) -> Self { + Self { + id: self.id, + internal: self.internal, + } + } +} + +impl fmt::Debug for StoreHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StoreHandle") + .field("id", &self.id) + .field("internal", &self.internal.index()) + .finish() + } +} + +impl StoreHandle { + /// Moves the given object into a context and returns a handle to it. + pub fn new(store: &mut StoreObjects, val: T) -> Self { + Self { + id: store.id, + internal: InternalStoreHandle::new(store, val), + } + } + + /// Returns a reference to the object that this handle points to. + pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T { + assert_eq!(self.id, store.id, "object used with the wrong context"); + self.internal.get(store) + } + + /// Returns a mutable reference to the object that this handle points to. + pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T { + assert_eq!(self.id, store.id, "object used with the wrong context"); + self.internal.get_mut(store) + } + + /// Returns the internal handle contains within this handle. + pub fn internal_handle(&self) -> InternalStoreHandle { + self.internal + } + + /// Returns the ID of the context associated with the handle. + #[allow(unused)] + pub fn store_id(&self) -> StoreId { + self.id + } + + /// Overrides the store id with a new ID + #[allow(unused)] + pub fn set_store_id(&mut self, id: StoreId) { + self.id = id; + } + + /// Constructs a `StoreHandle` from a `StoreId` and an `InternalStoreHandle`. + /// + /// # Safety + /// Handling `InternalStoreHandle` values is unsafe because they do not track context ID. + pub unsafe fn from_internal(id: StoreId, internal: InternalStoreHandle) -> Self { + Self { id, internal } + } +} + +/// Internal handle to an object owned by the current context. +/// +/// Unlike `StoreHandle` this does not track the context ID: it is only +/// intended to be used within objects already owned by a context. +#[repr(transparent)] +pub struct InternalStoreHandle { + // Use a NonZero here to reduce the size of Option. + idx: NonZeroUsize, + marker: PhantomData T>, +} + +impl Clone for InternalStoreHandle { + fn clone(&self) -> Self { + *self + } +} +impl Copy for InternalStoreHandle {} + +impl fmt::Debug for InternalStoreHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("InternalStoreHandle") + .field("idx", &self.idx) + .finish() + } +} +impl PartialEq for InternalStoreHandle { + fn eq(&self, other: &Self) -> bool { + self.idx == other.idx + } +} +impl Eq for InternalStoreHandle {} + +impl InternalStoreHandle { + /// Moves the given object into a context and returns a handle to it. + pub fn new(store: &mut StoreObjects, val: T) -> Self { + let list = T::list_mut(store); + let idx = NonZeroUsize::new(list.len() + 1).unwrap(); + list.push(val); + Self { + idx, + marker: PhantomData, + } + } + + /// Returns a reference to the object that this handle points to. + pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T { + &T::list(store)[self.idx.get() - 1] + } + + /// Returns a mutable reference to the object that this handle points to. + pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T { + &mut T::list_mut(store)[self.idx.get() - 1] + } + + pub(crate) fn index(&self) -> usize { + self.idx.get() + } + + pub(crate) fn from_index(idx: usize) -> Option { + NonZeroUsize::new(idx).map(|idx| Self { + idx, + marker: PhantomData, + }) + } +} diff --git a/lib/api/src/rt/wasmi/entities/table.rs b/lib/api/src/rt/wasmi/entities/table.rs new file mode 100644 index 00000000000..dbb3286574a --- /dev/null +++ b/lib/api/src/rt/wasmi/entities/table.rs @@ -0,0 +1,233 @@ +//! Data types, functions and traits for `wasmi`'s `Table` implementation. +use wasmer_types::TableType; + +use crate::{ + vm::{VMExtern, VMExternTable}, + wasmi::{ + bindings::{self, *}, + utils::convert::{IntoCApiType, IntoCApiValue, IntoWasmerType, IntoWasmerValue}, + vm::VMTable, + }, + AsStoreMut, AsStoreRef, Runtime, RuntimeError, RuntimeTable, Value, +}; + +#[derive(Debug, Clone, PartialEq, Eq)] +/// A WebAssembly `table` in `wasmi`. +pub struct Table { + pub(crate) handle: VMTable, +} + +unsafe impl Send for Table {} +unsafe impl Sync for Table {} + +impl Table { + pub(crate) fn type_to_wasmi(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 { + min: ty.minimum, + max: match ty.maximum { + Some(v) => v, + None => 0, + }, + })); + + unsafe { wasm_tabletype_new(valtype, limits) } + } + + pub fn new( + store: &mut impl AsStoreMut, + ty: TableType, + init: Value, + ) -> Result { + let store_mut = store.as_store_mut(); + let engine = store_mut.engine(); + + let wasm_tablety = Self::type_to_wasmi(ty); + let init: wasm_val_t = init.into_cv(); + + Ok(Self { + handle: unsafe { + wasm_table_new( + store_mut.inner.store.as_wasmi().inner, + wasm_tablety, + init.of.ref_, + ) + }, + }) + } + + pub fn to_vm_extern(&self) -> VMExtern { + VMExtern::Wasmi(unsafe { wasm_table_as_extern(self.handle) }) + } + + pub fn ty(&self, _store: &impl AsStoreRef) -> TableType { + 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(), + minimum: unsafe { (*table_limits).min }, + maximum: unsafe { + if (*table_limits).max == 0 { + None + } else { + Some((*table_limits).max) + } + }, + } + } + + pub fn get(&self, store: &mut impl AsStoreMut, index: u32) -> Option { + unsafe { + let ref_ = wasm_table_get(self.handle, index); + + if ref_.is_null() { + return None; + } + + let kind = match self.ty(store).ty { + wasmer_types::Type::ExternRef => wasm_valkind_enum_WASM_EXTERNREF, + wasmer_types::Type::FuncRef => wasm_valkind_enum_WASM_FUNCREF, + ty => panic!("unsupported table type: {ty:?}"), + } as u8; + + let value = wasm_val_t { + kind, + of: bindings::wasm_val_t__bindgen_ty_1 { ref_ }, + }; + + Some(value.into_wv()) + } + } + + pub fn set( + &self, + store: &mut impl AsStoreMut, + index: u32, + val: Value, + ) -> Result<(), RuntimeError> { + unsafe { + let init = match val { + Value::ExternRef(None) | Value::FuncRef(None) => std::ptr::null_mut(), + Value::FuncRef(Some(ref r)) => wasm_func_as_ref(r.as_wasmi().handle), + _ => { + return Err(RuntimeError::new(format!( + "Could not grow table due to unsupported init value type: {val:?} " + ))) + } + }; + + if !wasm_table_set(self.handle, index, init) { + return Err(RuntimeError::new(format!( + "Could not set value {val:?} table at index {index}" + ))); + } + + Ok(()) + } + } + + pub fn size(&self, store: &impl AsStoreRef) -> u32 { + unsafe { wasm_table_size(self.handle) } + } + + pub fn grow( + &self, + store: &mut impl AsStoreMut, + delta: u32, + init: Value, + ) -> Result { + unsafe { + let size = wasm_table_size(self.handle); + let init = match init { + Value::ExternRef(None) | Value::FuncRef(None) => std::ptr::null_mut(), + Value::FuncRef(Some(r)) => wasm_func_as_ref(r.as_wasmi().handle), + _ => { + return Err(RuntimeError::new(format!( + "Could not grow table due to unsupported init value type: {init:?} " + ))) + } + }; + if !wasm_table_grow(self.handle, delta, init) { + return Err(RuntimeError::new("Could not grow table")); + } + + Ok(size) + } + } + + pub fn copy( + _store: &mut impl AsStoreMut, + _dst_table: &Self, + _dst_index: u32, + _src_table: &Self, + _src_index: u32, + _len: u32, + ) -> Result<(), RuntimeError> { + unimplemented!("Copying tables is currently not implemented!") + } + + pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, vm_extern: VMExternTable) -> Self { + Self { + handle: vm_extern.into_wasmi(), + } + } + + pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} + +impl crate::Table { + /// Consume [`self`] into [`crate::rt::wasmi::table::Table`]. + pub fn into_wasmi(self) -> crate::rt::wasmi::table::Table { + match self.0 { + RuntimeTable::Wasmi(s) => s, + _ => panic!("Not a `wasmi` table!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::wasmi::table::Table`]. + pub fn as_wasmi(&self) -> &crate::rt::wasmi::table::Table { + match self.0 { + RuntimeTable::Wasmi(ref s) => s, + _ => panic!("Not a `wasmi` table!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wasmi::table::Table`]. + pub fn as_wasmi_mut(&mut self) -> &mut crate::rt::wasmi::table::Table { + match self.0 { + RuntimeTable::Wasmi(ref mut s) => s, + _ => panic!("Not a `wasmi` table!"), + } + } +} + +impl crate::RuntimeTable { + /// Consume [`self`] into [`crate::rt::wasmi::table::Table`]. + pub fn into_wasmi(self) -> crate::rt::wasmi::table::Table { + match self { + Self::Wasmi(s) => s, + _ => panic!("Not a `wasmi` table!"), + } + } + + /// Convert a reference to [`self`] into a reference [`crate::rt::wasmi::table::Table`]. + pub fn as_wasmi(&self) -> &crate::rt::wasmi::table::Table { + match self { + Self::Wasmi(ref s) => s, + _ => panic!("Not a `wasmi` table!"), + } + } + + /// Convert a mutable reference to [`self`] into a mutable reference [`crate::rt::wasmi::table::Table`]. + pub fn as_wasmi_mut(&mut self) -> &mut crate::rt::wasmi::table::Table { + match self { + Self::Wasmi(ref mut s) => s, + _ => panic!("Not a `wasmi` table!"), + } + } +} diff --git a/lib/api/src/rt/wasmi/error.rs b/lib/api/src/rt/wasmi/error.rs new file mode 100644 index 00000000000..cbbb0dce82b --- /dev/null +++ b/lib/api/src/rt/wasmi/error.rs @@ -0,0 +1,172 @@ +use std::{ + error::Error, + ffi::{c_char, CStr}, +}; + +use crate::{wasmi::bindings::*, AsStoreMut}; + +#[derive(Debug)] +enum InnerTrap { + User(Box), + CApi(*mut wasm_trap_t), +} + +/// A struct representing a Trap +pub struct Trap { + inner: InnerTrap, +} + +unsafe impl Send for Trap {} +unsafe impl Sync for Trap {} + +impl Trap { + pub fn user(error: Box) -> Self { + Self { + inner: InnerTrap::User(error), + } + } + + /// Attempts to downcast the `Trap` to a concrete type. + pub fn downcast(self) -> Result { + match self.inner { + // We only try to downcast user errors + InnerTrap::User(err) if err.is::() => Ok(*err.downcast::().unwrap()), + _ => Err(self), + } + } + + /// Attempts to downcast the `Trap` to a concrete type. + pub fn downcast_ref(&self) -> Option<&T> { + match &self.inner { + // We only try to downcast user errors + InnerTrap::User(err) if err.is::() => err.downcast_ref::(), + _ => None, + } + } + + /// Returns true if the `Trap` is the same as T + pub fn is(&self) -> bool { + match &self.inner { + InnerTrap::User(err) => err.is::(), + _ => false, + } + } + + pub unsafe fn into_wasm_trap(self, store: &mut impl AsStoreMut) -> *mut wasm_trap_t { + match self.inner { + InnerTrap::CApi(t) => t, + InnerTrap::User(err) => { + let err_ptr = Box::leak(Box::new(err)); + let mut data = std::mem::zeroed(); + // let x = format!("") + let s1 = format!("🐛{:p}", err_ptr); + let _s = s1.into_bytes().into_boxed_slice(); + wasm_byte_vec_new(&mut data, _s.len(), _s.as_ptr() as _); + std::mem::forget(_s); + let store = store.as_store_mut(); + wasm_trap_new(store.inner.store.as_wasmi().inner, &mut data) + } + } + } + + // pub unsafe fn deserialize_from_wasm_trap(trap: *mut wasm_trap_t) -> Self { + // let mut data = std::mem::zeroed(); + // wasm_trap_message(trap, data); + // println!("data: {:p}", data); + + // std::ptr::read(data as *const _) + // } +} + +impl From<*mut wasm_trap_t> for Trap { + fn from(value: *mut wasm_trap_t) -> Self { + let message = unsafe { + let mut message = std::mem::zeroed(); + wasm_trap_message(value, &mut message); + + CStr::from_ptr(message.data as *const c_char) + .to_str() + .unwrap() + }; + + println!("{message}"); + + if message.starts_with("Exception: 🐛") { + let ptr_str = message.replace("Exception: 🐛", ""); + 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), + ) + }; + + Self { + inner: InnerTrap::User(ptr), + } + } else { + Self { + inner: InnerTrap::CApi(value), + } + } + } +} + +impl std::error::Error for Trap { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self.inner { + InnerTrap::User(err) => Some(&**err), + _ => None, + } + } +} + +impl std::fmt::Display for Trap { + 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()) + } + } + } +} + +impl std::fmt::Debug for Trap { + 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()) + } + } + } +} + +impl From for crate::RuntimeError { + fn from(trap: Trap) -> Self { + if trap.is::() { + return trap.downcast::().unwrap(); + } + + crate::RuntimeError::new_from_source(crate::RuntimeTrap::Wasmi(trap), vec![], None) + } +} diff --git a/lib/api/src/rt/wasmi/mod.rs b/lib/api/src/rt/wasmi/mod.rs new file mode 100644 index 00000000000..3e37e77ce43 --- /dev/null +++ b/lib/api/src/rt/wasmi/mod.rs @@ -0,0 +1,9 @@ +//! Data types, functions and traits for `wasmi`. + +pub(crate) mod bindings; +pub(crate) mod entities; +pub(crate) mod error; +pub(crate) mod utils; +pub(crate) mod vm; + +pub use entities::{engine::Engine as Wasmi, *}; diff --git a/lib/api/src/rt/wasmi/utils/convert.rs b/lib/api/src/rt/wasmi/utils/convert.rs new file mode 100644 index 00000000000..e346eda251b --- /dev/null +++ b/lib/api/src/rt/wasmi/utils/convert.rs @@ -0,0 +1,141 @@ +/// Utilities to convert between `wasmi` and `wasmer` values +use crate::{ + wasmi::{ + bindings::{ + self, wasm_func_as_ref, wasm_val_t, wasm_val_t__bindgen_ty_1, wasm_valkind_t, + wasm_valtype_kind, wasm_valtype_t, + }, + function, + }, + Function, RuntimeFunction, Value, +}; +use wasmer_types::Type; + +pub trait IntoCApiValue { + /// Consume [`self`] to produce a [`wasm_val_t`]. + fn into_cv(self) -> wasm_val_t; +} + +impl IntoCApiValue for Value { + fn into_cv(self) -> wasm_val_t { + match self { + Value::I32(val) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_I32 as _, + of: wasm_val_t__bindgen_ty_1 { i32_: val }, + }, + Value::I64(val) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_I64 as _, + of: wasm_val_t__bindgen_ty_1 { i64_: val }, + }, + Value::F32(val) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_F32 as _, + of: wasm_val_t__bindgen_ty_1 { f32_: val }, + }, + Value::F64(val) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_F64 as _, + of: wasm_val_t__bindgen_ty_1 { f64_: val }, + }, + Value::FuncRef(Some(val)) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_FUNCREF as _, + of: wasm_val_t__bindgen_ty_1 { + ref_: unsafe { wasm_func_as_ref(val.as_wasmi().handle) }, + }, + }, + Value::FuncRef(None) => wasm_val_t { + kind: bindings::wasm_valkind_enum_WASM_FUNCREF as _, + of: wasm_val_t__bindgen_ty_1 { + ref_: unsafe { wasm_func_as_ref(std::ptr::null_mut()) }, + }, + }, + Value::ExternRef(_) => panic!("Creating host values from guest ExternRefs is not currently supported through wasm_c_api.") , + Value::V128(_) => panic!("Creating host values from guest V128s is not currently supported through wasm_c_api."), + } + } +} + +pub trait IntoWasmerValue { + /// Consume [`self`] to produce a [`Value`]. + fn into_wv(self) -> Value; +} + +impl IntoWasmerValue for wasm_val_t { + fn into_wv(self) -> Value { + match self.kind as _ { + bindings::wasm_valkind_enum_WASM_I32 => Value::I32(unsafe { self.of.i32_ }), + bindings::wasm_valkind_enum_WASM_I64 => Value::I64(unsafe { self.of.i64_ }), + bindings::wasm_valkind_enum_WASM_F32 => Value::F32(unsafe { self.of.f32_ }), + bindings::wasm_valkind_enum_WASM_F64 => Value::F64(unsafe { self.of.f64_ }), + bindings::wasm_valkind_enum_WASM_FUNCREF => Value::FuncRef(Some(Function( + RuntimeFunction::Wasmi(crate::rt::wasmi::function::Function { + handle: unsafe { self.of.ref_ as _ }, + }), + ))), + bindings::wasm_valkind_enum_WASM_EXTERNREF => { + panic!("ExternRefs are not currently supported through wasm_c_api") + } + + _ => { + panic!("wasmi currently does not support V128 values") + } + } + } +} + +pub trait IntoWasmerType { + /// Consume [`self`] to produce a [`Type`]. + fn into_wt(self) -> Type; +} + +impl IntoWasmerType for wasm_valkind_t { + fn into_wt(self) -> Type { + match self as _ { + bindings::wasm_valkind_enum_WASM_I32 => Type::I32, + bindings::wasm_valkind_enum_WASM_I64 => Type::I64, + bindings::wasm_valkind_enum_WASM_F32 => Type::F32, + bindings::wasm_valkind_enum_WASM_F64 => Type::F64, + bindings::wasm_valkind_enum_WASM_EXTERNREF => Type::ExternRef, + bindings::wasm_valkind_enum_WASM_FUNCREF => Type::FuncRef, + _ => unreachable!("wasmi kind {self:?} has no matching wasmer_types::Type"), + } + } +} + +pub trait IntoCApiType { + /// Consume [`self`] to produce a [`wasm_valkind_t`]. + fn into_ct(self) -> wasm_valkind_t; +} + +impl IntoCApiType for Type { + fn into_ct(self) -> wasm_valkind_t { + match self as _ { + Type::I32 => bindings::wasm_valkind_enum_WASM_I32 as _, + Type::I64 => bindings::wasm_valkind_enum_WASM_I64 as _, + Type::F32 => bindings::wasm_valkind_enum_WASM_F32 as _, + Type::F64 => bindings::wasm_valkind_enum_WASM_F64 as _, + Type::FuncRef => bindings::wasm_valkind_enum_WASM_FUNCREF as _, + Type::ExternRef => bindings::wasm_valkind_enum_WASM_EXTERNREF as _, + Type::V128 => panic!("wasmi does not support V128!"), + } + } +} + +impl IntoWasmerType for wasm_valtype_t { + fn into_wt(self) -> Type { + let type_: wasm_valkind_t = unsafe { wasm_valtype_kind(&self as *const _) }; + type_.into_wt() + } +} + +impl IntoWasmerType for *const wasm_valtype_t { + fn into_wt(self) -> Type { + let type_: wasm_valkind_t = unsafe { wasm_valtype_kind(self as _) }; + type_.into_wt() + } +} + +impl IntoWasmerType for *mut wasm_valtype_t { + fn into_wt(self) -> Type { + let type_: wasm_valkind_t = unsafe { wasm_valtype_kind(self as _) }; + type_.into_wt() + } +} diff --git a/lib/api/src/rt/wasmi/utils/mod.rs b/lib/api/src/rt/wasmi/utils/mod.rs new file mode 100644 index 00000000000..02d63d4e141 --- /dev/null +++ b/lib/api/src/rt/wasmi/utils/mod.rs @@ -0,0 +1 @@ +pub(crate) mod convert; diff --git a/lib/api/src/rt/wasmi/vm/env.rs b/lib/api/src/rt/wasmi/vm/env.rs new file mode 100644 index 00000000000..d2a93d5f63d --- /dev/null +++ b/lib/api/src/rt/wasmi/vm/env.rs @@ -0,0 +1,28 @@ +use std::any::Any; + +/// Underlying FunctionEnvironment used by a `VMFunction`. +#[derive(Debug)] +pub struct VMFunctionEnvironment { + pub(crate) contents: Box, +} + +impl VMFunctionEnvironment { + /// Wraps the given value to expose it to Wasm code as a function context. + pub fn new(val: impl Any + Send + 'static) -> Self { + Self { + contents: Box::new(val), + } + } + + #[allow(clippy::should_implement_trait)] + /// Returns a reference to the underlying value. + pub fn as_ref(&self) -> &(dyn Any + Send + 'static) { + &*self.contents + } + + #[allow(clippy::should_implement_trait)] + /// Returns a mutable reference to the underlying value. + pub fn as_mut(&mut self) -> &mut (dyn Any + Send + 'static) { + &mut *self.contents + } +} diff --git a/lib/api/src/rt/wasmi/vm/mod.rs b/lib/api/src/rt/wasmi/vm/mod.rs new file mode 100644 index 00000000000..949ed3c9422 --- /dev/null +++ b/lib/api/src/rt/wasmi/vm/mod.rs @@ -0,0 +1,123 @@ +mod env; +pub use env::*; + +pub use super::bindings::{ + wasm_extern_as_func, wasm_extern_as_global, wasm_extern_as_memory, wasm_extern_as_table, + wasm_extern_kind, wasm_extern_t, wasm_func_t, wasm_global_t, wasm_instance_t, wasm_memory_t, + wasm_ref_t, wasm_table_t, +}; +use super::{ + entities::function::env::FunctionEnv, function::Function, global::Global, memory::Memory, + table::Table, +}; +use crate::{AsStoreMut, Extern, RuntimeFunction, RuntimeGlobal, RuntimeMemory, RuntimeTable}; +use wasmer_types::RawValue; + +pub use super::error::Trap; + +pub(crate) type VMExtern = *mut wasm_extern_t; + +pub(crate) type VMFunction = *mut wasm_func_t; +pub(crate) type VMFunctionBody = (); +pub(crate) type VMFunctionCallback = *mut ::std::os::raw::c_void; +pub(crate) type VMTrampoline = *mut ::std::os::raw::c_void; +pub(crate) type VMExternFunction = *mut wasm_func_t; + +pub(crate) type VMGlobal = *mut wasm_global_t; +pub(crate) type VMExternGlobal = *mut wasm_global_t; + +pub(crate) type VMMemory = *mut wasm_memory_t; +pub type VMSharedMemory = VMMemory; +pub(crate) type VMExternMemory = *mut wasm_memory_t; + +pub(crate) type VMTable = *mut wasm_table_t; +pub(crate) type VMExternTable = *mut wasm_table_t; + +pub(crate) type VMInstance = *mut wasm_instance_t; + +pub(crate) type VMExternObj = (); +pub(crate) type VMConfig = (); + +impl crate::VMExternToExtern for VMExtern { + fn to_extern(self, store: &mut impl AsStoreMut) -> Extern { + let kind = unsafe { wasm_extern_kind(&mut *self) }; + + match kind as u32 { + 0 => { + let func = unsafe { wasm_extern_as_func(&mut *self) }; + if func.is_null() { + panic!("The wasm-c-api reported extern as function, but is not"); + } + Extern::Function(crate::Function::from_vm_extern( + store, + crate::vm::VMExternFunction::Wasmi(func), + )) + } + 1 => { + let global = unsafe { wasm_extern_as_global(&mut *self) }; + if global.is_null() { + panic!("The wasm-c-api reported extern as a global, but is not"); + } + Extern::Global(crate::Global::from_vm_extern( + store, + crate::vm::VMExternGlobal::Wasmi(global), + )) + } + 2 => { + let table = unsafe { wasm_extern_as_table(&mut *self) }; + if table.is_null() { + panic!("The wasm-c-api reported extern as a table, but is not"); + } + Extern::Table(crate::Table::from_vm_extern( + store, + crate::vm::VMExternTable::Wasmi(table), + )) + } + 3 => { + let memory = unsafe { wasm_extern_as_memory(&mut *self) }; + if memory.is_null() { + panic!("The wasm-c-api reported extern as a table, but is not"); + } + Extern::Memory(crate::Memory::from_vm_extern( + store, + crate::vm::VMExternMemory::Wasmi(memory), + )) + } + _ => { + unimplemented!() + } + } + } +} + +pub(crate) struct VMExternRef(*mut wasm_ref_t); +impl VMExternRef { + /// Converts the `VMExternRef` into a `RawValue`. + pub fn into_raw(self) -> RawValue { + unimplemented!() + } + + /// Extracts a `VMExternRef` from a `RawValue`. + /// + /// # Safety + /// `raw` must be a valid `VMExternRef` instance. + pub unsafe fn from_raw(_raw: RawValue) -> Option { + unimplemented!(); + } +} + +pub(crate) struct VMFuncRef(*mut wasm_ref_t); +impl VMFuncRef { + /// Converts the `VMExternRef` into a `RawValue`. + pub fn into_raw(self) -> RawValue { + unimplemented!() + } + + /// Extracts a `VMExternRef` from a `RawValue`. + /// + /// # Safety + /// `raw` must be a valid `VMExternRef` instance. + pub unsafe fn from_raw(_raw: RawValue) -> Option { + unimplemented!(); + } +} diff --git a/lib/api/src/store.rs b/lib/api/src/store.rs deleted file mode 100644 index cdf37c897b5..00000000000 --- a/lib/api/src/store.rs +++ /dev/null @@ -1,309 +0,0 @@ -use crate::engine::{AsEngineRef, Engine, EngineRef}; -#[cfg(feature = "sys")] -use crate::sys::NativeStoreExt; -use std::{ - fmt, - ops::{Deref, DerefMut}, -}; -#[cfg(feature = "sys")] -pub use wasmer_compiler::Tunables; -pub use wasmer_types::{OnCalledAction, StoreId}; -#[cfg(feature = "sys")] -pub use wasmer_vm::TrapHandlerFn; - -#[cfg(feature = "sys")] -pub use wasmer_vm::{StoreHandle, StoreObjects}; - -#[cfg(feature = "js")] -pub use crate::js::store::{StoreHandle, StoreObjects}; - -#[cfg(feature = "jsc")] -pub use crate::jsc::store::{StoreHandle, StoreObjects}; - -#[cfg(feature = "wasm-c-api")] -pub use crate::c_api::store::{StoreHandle, StoreObjects}; - -#[cfg(feature = "sys")] -use crate::sys::store as store_imp; - -#[cfg(feature = "js")] -use crate::js::store as store_imp; - -#[cfg(feature = "jsc")] -use crate::jsc::store as store_imp; - -#[cfg(feature = "wasm-c-api")] -use crate::c_api::store as store_imp; - -/// Call handler for a store. -// TODO: better documentation! -pub type OnCalledHandler = Box< - dyn FnOnce(StoreMut<'_>) -> Result>, ->; - -/// We require the context to have a fixed memory address for its lifetime since -/// various bits of the VM have raw pointers that point back to it. Hence we -/// wrap the actual context in a box. -pub(crate) struct StoreInner { - pub(crate) objects: StoreObjects, - pub(crate) store: store_imp::Store, - pub(crate) on_called: Option, -} - -impl std::fmt::Debug for StoreInner { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.debug_struct("StoreInner") - .field("objects", &self.objects) - .field("store", &self.store) - .field("on_called", &"<...>") - .finish() - } -} - -/// The store represents all global state that can be manipulated by -/// WebAssembly programs. It consists of the runtime representation -/// of all instances of functions, tables, memories, and globals that -/// have been allocated during the lifetime of the abstract machine. -/// -/// The `Store` holds the engine (that is —amongst many things— used to compile -/// the Wasm bytes into a valid module artifact). -/// -/// Spec: -pub struct Store { - pub(crate) inner: Box, -} - -impl Store { - /// Creates a new `Store` with a specific [`Engine`]. - pub fn new(engine: impl Into) -> Self { - Self { - inner: Box::new(StoreInner { - objects: Default::default(), - store: store_imp::Store::new(engine.into()), - on_called: None, - }), - } - } - - #[cfg(feature = "sys")] - /// Set the trap handler in this store. - pub fn set_trap_handler(&mut self, handler: Option>>) { - self.inner.store.set_trap_handler(handler) - } - - /// Returns the [`Engine`]. - pub fn engine(&self) -> &Engine { - self.inner.store.engine() - } - - /// Returns mutable reference to [`Engine`] - pub fn engine_mut(&mut self) -> &mut Engine { - &mut self.inner.store.engine - } - - /// Checks whether two stores are identical. A store is considered - /// equal to another store if both have the same engine. - pub fn same(a: &Self, b: &Self) -> bool { - a.id() == b.id() - } - - /// Returns the ID of this store - pub fn id(&self) -> StoreId { - self.inner.objects.id() - } -} - -impl PartialEq for Store { - fn eq(&self, other: &Self) -> bool { - Self::same(self, other) - } -} - -// This is required to be able to set the trap_handler in the -// Store. -unsafe impl Send for Store {} -unsafe impl Sync for Store {} - -impl Default for Store { - fn default() -> Self { - Self::new(Engine::default()) - } -} - -impl AsStoreRef for Store { - fn as_store_ref(&self) -> StoreRef<'_> { - StoreRef { inner: &self.inner } - } -} -impl AsStoreMut for Store { - fn as_store_mut(&mut self) -> StoreMut<'_> { - StoreMut { - inner: &mut self.inner, - } - } - fn objects_mut(&mut self) -> &mut StoreObjects { - &mut self.inner.objects - } -} - -impl AsEngineRef for Store { - fn as_engine_ref(&self) -> EngineRef<'_> { - self.inner.store.as_engine_ref() - } - - fn maybe_as_store(&self) -> Option> { - Some(self.as_store_ref()) - } -} - -impl AsEngineRef for StoreRef<'_> { - fn as_engine_ref(&self) -> EngineRef<'_> { - self.inner.store.as_engine_ref() - } -} - -impl AsEngineRef for StoreMut<'_> { - fn as_engine_ref(&self) -> EngineRef<'_> { - self.inner.store.as_engine_ref() - } -} - -impl fmt::Debug for Store { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Store").finish() - } -} - -/// A temporary handle to a [`Store`]. -#[derive(Debug)] -pub struct StoreRef<'a> { - pub(crate) inner: &'a StoreInner, -} - -impl<'a> StoreRef<'a> { - pub(crate) fn objects(&self) -> &'a StoreObjects { - &self.inner.objects - } - - /// Returns the [`Engine`]. - pub fn engine(&self) -> &Engine { - &self.inner.store.engine - } - - /// Checks whether two stores are identical. A store is considered - /// equal to another store if both have the same engine. - pub fn same(a: &Self, b: &Self) -> bool { - a.inner.objects.id() == b.inner.objects.id() - } - - /// The signal handler - #[cfg(feature = "sys")] - #[inline] - pub fn signal_handler(&self) -> Option<*const TrapHandlerFn<'static>> { - self.inner.store.signal_handler() - } -} - -/// A temporary handle to a [`Store`]. -pub struct StoreMut<'a> { - pub(crate) inner: &'a mut StoreInner, -} - -impl<'a> StoreMut<'a> { - /// Returns the [`Engine`]. - pub fn engine(&self) -> &Engine { - &self.inner.store.engine - } - - /// Checks whether two stores are identical. A store is considered - /// equal to another store if both have the same engine. - pub fn same(a: &Self, b: &Self) -> bool { - a.inner.objects.id() == b.inner.objects.id() - } - - #[allow(unused)] - pub(crate) fn engine_and_objects_mut(&mut self) -> (&Engine, &mut StoreObjects) { - (&self.inner.store.engine, &mut self.inner.objects) - } - - #[allow(unused)] - pub(crate) fn as_raw(&self) -> *mut StoreInner { - self.inner as *const StoreInner as *mut StoreInner - } - - #[allow(unused)] - pub(crate) unsafe fn from_raw(raw: *mut StoreInner) -> Self { - Self { inner: &mut *raw } - } - - // TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 - /// Sets the unwind callback which will be invoked when the call finishes - pub fn on_called(&mut self, callback: F) - where - F: FnOnce(StoreMut<'_>) -> Result> - + Send - + Sync - + 'static, - { - self.inner.on_called.replace(Box::new(callback)); - } -} - -/// Helper trait for a value that is convertible to a [`StoreRef`]. -pub trait AsStoreRef { - /// Returns a `StoreRef` pointing to the underlying context. - fn as_store_ref(&self) -> StoreRef<'_>; -} - -/// Helper trait for a value that is convertible to a [`StoreMut`]. -pub trait AsStoreMut: AsStoreRef { - /// Returns a `StoreMut` pointing to the underlying context. - fn as_store_mut(&mut self) -> StoreMut<'_>; - - /// Returns the ObjectMutable - fn objects_mut(&mut self) -> &mut StoreObjects; -} - -impl AsStoreRef for StoreRef<'_> { - fn as_store_ref(&self) -> StoreRef<'_> { - StoreRef { inner: self.inner } - } -} - -impl AsStoreRef for StoreMut<'_> { - fn as_store_ref(&self) -> StoreRef<'_> { - StoreRef { inner: self.inner } - } -} -impl AsStoreMut for StoreMut<'_> { - fn as_store_mut(&mut self) -> StoreMut<'_> { - StoreMut { inner: self.inner } - } - fn objects_mut(&mut self) -> &mut StoreObjects { - &mut self.inner.objects - } -} - -impl

AsStoreRef for P -where - P: Deref, - P::Target: AsStoreRef, -{ - fn as_store_ref(&self) -> StoreRef<'_> { - (**self).as_store_ref() - } -} - -impl

AsStoreMut for P -where - P: DerefMut, - P::Target: AsStoreMut, -{ - fn as_store_mut(&mut self) -> StoreMut<'_> { - (**self).as_store_mut() - } - - fn objects_mut(&mut self) -> &mut StoreObjects { - (**self).objects_mut() - } -} diff --git a/lib/api/src/sys/extern_ref.rs b/lib/api/src/sys/extern_ref.rs deleted file mode 100644 index 2f50751b6bb..00000000000 --- a/lib/api/src/sys/extern_ref.rs +++ /dev/null @@ -1,49 +0,0 @@ -use std::any::Any; -use wasmer_vm::VMExternRef; -use wasmer_vm::{StoreHandle, VMExternObj}; - -use crate::store::{AsStoreMut, AsStoreRef}; - -#[derive(Debug, Clone)] -#[repr(transparent)] -pub struct ExternRef { - handle: StoreHandle, -} - -impl ExternRef { - pub fn new(store: &mut impl AsStoreMut, value: T) -> Self - where - T: Any + Send + Sync + 'static + Sized, - { - Self { - handle: StoreHandle::new(store.objects_mut(), VMExternObj::new(value)), - } - } - - pub fn downcast<'a, T>(&self, store: &'a impl AsStoreRef) -> Option<&'a T> - where - T: Any + Send + Sync + 'static + Sized, - { - self.handle - .get(store.as_store_ref().objects()) - .as_ref() - .downcast_ref::() - } - - pub(crate) fn vm_externref(&self) -> VMExternRef { - VMExternRef(self.handle.internal_handle()) - } - - pub(crate) unsafe fn from_vm_externref( - store: &mut impl AsStoreMut, - vm_externref: VMExternRef, - ) -> Self { - Self { - handle: StoreHandle::from_internal(store.objects_mut().id(), vm_externref.0), - } - } - - pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { - self.handle.store_id() == store.as_store_ref().objects().id() - } -} diff --git a/lib/api/src/sys/externals/mod.rs b/lib/api/src/sys/externals/mod.rs deleted file mode 100644 index 3575fe23bb5..00000000000 --- a/lib/api/src/sys/externals/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub(crate) mod function; -pub(crate) mod global; -pub(crate) mod memory; -pub(crate) mod memory_view; -pub(crate) mod table; diff --git a/lib/api/src/sys/externals/table.rs b/lib/api/src/sys/externals/table.rs deleted file mode 100644 index 285135dfd2a..00000000000 --- a/lib/api/src/sys/externals/table.rs +++ /dev/null @@ -1,162 +0,0 @@ -use crate::store::{AsStoreMut, AsStoreRef}; -use crate::sys::engine::NativeEngineExt; -use crate::TableType; -use crate::Value; -use crate::{vm::VMExternTable, ExternRef, Function, RuntimeError}; -use wasmer_vm::{StoreHandle, TableElement, Trap, VMExtern, VMTable}; - -#[derive(Debug, Clone)] -#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))] -pub struct Table { - handle: StoreHandle, -} - -fn set_table_item( - table: &mut VMTable, - item_index: u32, - item: TableElement, -) -> Result<(), RuntimeError> { - table.set(item_index, item).map_err(|e| e.into()) -} - -fn value_to_table_element( - store: &mut impl AsStoreMut, - val: Value, -) -> Result { - if !val.is_from_store(store) { - return Err(RuntimeError::new("cannot pass Value across contexts")); - } - Ok(match val { - Value::ExternRef(extern_ref) => { - wasmer_vm::TableElement::ExternRef(extern_ref.map(|e| e.vm_externref())) - } - Value::FuncRef(func_ref) => { - wasmer_vm::TableElement::FuncRef(func_ref.map(|f| f.vm_funcref(store))) - } - _ => return Err(RuntimeError::new("val is not reference")), - }) -} - -fn value_from_table_element(store: &mut impl AsStoreMut, item: wasmer_vm::TableElement) -> Value { - match item { - wasmer_vm::TableElement::FuncRef(funcref) => { - Value::FuncRef(funcref.map(|f| unsafe { Function::from_vm_funcref(store, f) })) - } - wasmer_vm::TableElement::ExternRef(extern_ref) => { - Value::ExternRef(extern_ref.map(|e| unsafe { ExternRef::from_vm_externref(store, e) })) - } - } -} - -impl Table { - pub fn new( - mut store: &mut impl AsStoreMut, - ty: TableType, - init: Value, - ) -> Result { - let item = value_to_table_element(&mut store, init)?; - let mut store = store.as_store_mut(); - let tunables = store.engine().tunables(); - let style = tunables.table_style(&ty); - let mut table = tunables - .create_host_table(&ty, &style) - .map_err(RuntimeError::new)?; - - let num_elements = table.size(); - for i in 0..num_elements { - set_table_item(&mut table, i, item.clone())?; - } - - Ok(Self { - handle: StoreHandle::new(store.objects_mut(), table), - }) - } - - pub fn ty(&self, store: &impl AsStoreRef) -> TableType { - *self.handle.get(store.as_store_ref().objects()).ty() - } - - pub fn get(&self, store: &mut impl AsStoreMut, index: u32) -> Option { - let item = self.handle.get(store.as_store_ref().objects()).get(index)?; - Some(value_from_table_element(store, item)) - } - - pub fn set( - &self, - store: &mut impl AsStoreMut, - index: u32, - val: Value, - ) -> Result<(), RuntimeError> { - let item = value_to_table_element(store, val)?; - set_table_item(self.handle.get_mut(store.objects_mut()), index, item) - } - - pub fn size(&self, store: &impl AsStoreRef) -> u32 { - self.handle.get(store.as_store_ref().objects()).size() - } - - pub fn grow( - &self, - store: &mut impl AsStoreMut, - delta: u32, - init: Value, - ) -> Result { - let item = value_to_table_element(store, init)?; - self.handle - .get_mut(store.objects_mut()) - .grow(delta, item) - .ok_or_else(|| RuntimeError::new(format!("failed to grow table by `{}`", delta))) - } - - pub fn copy( - store: &mut impl AsStoreMut, - dst_table: &Self, - dst_index: u32, - src_table: &Self, - src_index: u32, - len: u32, - ) -> Result<(), RuntimeError> { - if dst_table.handle.store_id() != src_table.handle.store_id() { - return Err(RuntimeError::new( - "cross-`Store` table copies are not supported", - )); - } - if dst_table.handle.internal_handle() == src_table.handle.internal_handle() { - let table = dst_table.handle.get_mut(store.objects_mut()); - table.copy_within(dst_index, src_index, len) - } else { - let (src_table, dst_table) = store.objects_mut().get_2_mut( - src_table.handle.internal_handle(), - dst_table.handle.internal_handle(), - ); - VMTable::copy(dst_table, src_table, dst_index, src_index, len) - } - .map_err(Into::::into)?; - Ok(()) - } - - pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternTable) -> Self { - Self { - handle: unsafe { - StoreHandle::from_internal(store.as_store_ref().objects().id(), vm_extern) - }, - } - } - - /// Checks whether this `Table` can be used with the given context. - pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { - self.handle.store_id() == store.as_store_ref().objects().id() - } - - pub(crate) fn to_vm_extern(&self) -> VMExtern { - VMExtern::Table(self.handle.internal_handle()) - } -} - -impl std::cmp::PartialEq for Table { - fn eq(&self, other: &Self) -> bool { - self.handle == other.handle - } -} - -impl std::cmp::Eq for Table {} diff --git a/lib/api/src/sys/mem_access.rs b/lib/api/src/sys/mem_access.rs deleted file mode 100644 index 376c7245446..00000000000 --- a/lib/api/src/sys/mem_access.rs +++ /dev/null @@ -1,95 +0,0 @@ -use std::mem; - -use crate::{ - access::{RefCow, SliceCow, WasmRefAccess, WasmSliceAccess}, - MemoryAccessError, WasmRef, WasmSlice, -}; - -impl<'a, T> WasmSliceAccess<'a, T> -where - T: wasmer_types::ValueType, -{ - pub(crate) fn new(slice: WasmSlice<'a, T>) -> Result { - let total_len = slice - .len - .checked_mul(mem::size_of::() as u64) - .ok_or(MemoryAccessError::Overflow)?; - let end = slice - .offset - .checked_add(total_len) - .ok_or(MemoryAccessError::Overflow)?; - if end > slice.buffer.0.len as u64 { - tracing::warn!( - "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", - total_len, - end, - slice.buffer.0.len - ); - return Err(MemoryAccessError::HeapOutOfBounds); - } - let buf = unsafe { - let buf_ptr: *mut u8 = slice.buffer.0.base.add(slice.offset as usize); - let buf_ptr: *mut T = std::mem::transmute(buf_ptr); - std::slice::from_raw_parts_mut(buf_ptr, slice.len as usize) - }; - Ok(Self { - slice, - buf: SliceCow::Borrowed(buf), - }) - } -} - -impl<'a, T> WasmRefAccess<'a, T> -where - T: wasmer_types::ValueType, -{ - pub(crate) fn new(ptr: WasmRef<'a, T>) -> Result { - let total_len = mem::size_of::() as u64; - let end = ptr - .offset - .checked_add(total_len) - .ok_or(MemoryAccessError::Overflow)?; - if end > ptr.buffer.0.len as u64 { - tracing::warn!( - "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", - total_len, - end, - ptr.buffer.0.len - ); - return Err(MemoryAccessError::HeapOutOfBounds); - } - let val = unsafe { - let val_ptr: *mut u8 = ptr.buffer.0.base.add(ptr.offset as usize); - let val_ptr: *mut T = std::mem::transmute(val_ptr); - &mut *val_ptr - }; - Ok(Self { - ptr, - buf: RefCow::Borrowed(val), - }) - } -} - -impl<'a, T> WasmRefAccess<'a, T> -where - T: wasmer_types::ValueType, -{ - /// Reads the address pointed to by this `WasmPtr` in a memory. - #[inline] - #[allow(clippy::clone_on_copy)] - pub fn read(&self) -> T - where - T: Clone, - { - self.as_ref().clone() - } - - /// Writes to the address pointed to by this `WasmPtr` in a memory. - #[inline] - pub fn write(&mut self, val: T) { - // Note: Zero padding is not required here as its a typed copy which does - // not leak the bytes into the memory - // https://stackoverflow.com/questions/61114026/does-stdptrwrite-transfer-the-uninitialized-ness-of-the-bytes-it-writes - *(self.as_mut()) = val; - } -} diff --git a/lib/api/src/sys/mod.rs b/lib/api/src/sys/mod.rs deleted file mode 100644 index f1a7ebc2503..00000000000 --- a/lib/api/src/sys/mod.rs +++ /dev/null @@ -1,28 +0,0 @@ -pub(crate) mod engine; -pub(crate) mod errors; -pub(crate) mod extern_ref; -pub(crate) mod externals; -pub(crate) mod instance; -pub(crate) mod mem_access; -pub(crate) mod module; -pub(crate) mod store; -pub(super) mod tunables; -pub(crate) mod typed_function; -pub(crate) mod vm; - -pub use crate::sys::engine::{get_default_compiler_config, NativeEngineExt}; -pub use crate::sys::store::NativeStoreExt; -pub use crate::sys::tunables::BaseTunables; -#[cfg(feature = "compiler")] -pub use wasmer_compiler::{ - wasmparser, CompilerConfig, FunctionMiddleware, MiddlewareReaderState, ModuleMiddleware, -}; -pub use wasmer_compiler::{Artifact, EngineBuilder, Features, Tunables}; -#[cfg(feature = "cranelift")] -pub use wasmer_compiler_cranelift::{Cranelift, CraneliftOptLevel}; -#[cfg(feature = "llvm")] -pub use wasmer_compiler_llvm::{LLVMOptLevel, LLVM}; -#[cfg(feature = "singlepass")] -pub use wasmer_compiler_singlepass::Singlepass; - -pub use wasmer_vm::VMConfig; diff --git a/lib/api/src/sys/store.rs b/lib/api/src/sys/store.rs deleted file mode 100644 index 39aa964899d..00000000000 --- a/lib/api/src/sys/store.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::engine::{AsEngineRef, Engine, EngineRef}; -use wasmer_vm::init_traps; -use wasmer_vm::TrapHandlerFn; - -pub(crate) struct Store { - pub(crate) engine: Engine, - pub(crate) trap_handler: Option>>, -} - -impl std::fmt::Debug for Store { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.debug_struct("Store") - .field("engine", &self.engine) - .finish() - } -} - -impl Store { - pub(crate) fn new(engine: Engine) -> Self { - init_traps(); - - Self { - engine, - trap_handler: None, - } - } - - pub(crate) fn engine(&self) -> &Engine { - &self.engine - } -} - -impl AsEngineRef for Store { - fn as_engine_ref(&self) -> EngineRef<'_> { - EngineRef::new(&self.engine) - } -} - -/// The custom trait to access to all the `sys` functions in the -/// Store. -pub trait NativeStoreExt { - /// Sets the trap handler - fn set_trap_handler(&mut self, handler: Option>>); - /// The signal handler - fn signal_handler(&self) -> Option<*const TrapHandlerFn<'static>>; -} - -impl NativeStoreExt for Store { - fn set_trap_handler(&mut self, handler: Option>>) { - self.trap_handler = handler; - } - - /// The signal handler - #[inline] - fn signal_handler(&self) -> Option<*const TrapHandlerFn<'static>> { - self.trap_handler - .as_ref() - .map(|handler| handler.as_ref() as *const _) - } -} - -impl NativeStoreExt for crate::Store { - fn set_trap_handler(&mut self, handler: Option>>) { - self.inner.store.set_trap_handler(handler) - } - - /// The signal handler - #[inline] - fn signal_handler(&self) -> Option<*const TrapHandlerFn<'static>> { - self.inner.store.signal_handler() - } -} diff --git a/lib/api/src/sys/vm.rs b/lib/api/src/sys/vm.rs deleted file mode 100644 index e15860fc252..00000000000 --- a/lib/api/src/sys/vm.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! The `vm` module re-exports wasmer-vm types. -use crate::externals::{Extern, Function, Global, Memory, Table, VMExternToExtern}; -use crate::store::AsStoreMut; -use wasmer_vm::InternalStoreHandle; -pub(crate) use wasmer_vm::{ - VMExtern, VMExternRef, VMFuncRef, VMFunction, VMFunctionBody, VMFunctionEnvironment, VMGlobal, - VMInstance, VMMemory, VMTable, VMTrampoline, -}; - -pub(crate) type VMExternTable = InternalStoreHandle; -pub(crate) type VMExternMemory = InternalStoreHandle; -pub(crate) type VMExternGlobal = InternalStoreHandle; -pub(crate) type VMExternFunction = InternalStoreHandle; - -pub type VMFunctionCallback = *const VMFunctionBody; - -impl VMExternToExtern for VMExtern { - fn to_extern(self, store: &mut impl AsStoreMut) -> Extern { - match self { - Self::Function(f) => Extern::Function(Function::from_vm_extern(store, f)), - Self::Memory(m) => Extern::Memory(Memory::from_vm_extern(store, m)), - Self::Global(g) => Extern::Global(Global::from_vm_extern(store, g)), - Self::Table(t) => Extern::Table(Table::from_vm_extern(store, t)), - } - } -} diff --git a/lib/api/src/typed_function.rs b/lib/api/src/typed_function.rs deleted file mode 100644 index 4f234f93625..00000000000 --- a/lib/api/src/typed_function.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! Native Functions. -//! -//! This module creates the helper `TypedFunction` that let us call WebAssembly -//! functions with the native ABI, that is: -//! -//! ```ignore -//! let add_one = instance.exports.get_function("function_name")?; -//! let add_one_native: TypedFunction = add_one.native().unwrap(); -//! ``` -use crate::{Function, WasmTypeList}; -use std::marker::PhantomData; - -use crate::store::AsStoreRef; - -/// A WebAssembly function that can be called natively -/// (using the Native ABI). -#[derive(Debug, Clone)] -pub struct TypedFunction { - pub(crate) func: Function, - _phantom: PhantomData Rets>, -} - -unsafe impl Send for TypedFunction {} -unsafe impl Sync for TypedFunction {} - -impl TypedFunction -where - Args: WasmTypeList, - Rets: WasmTypeList, -{ - #[allow(dead_code)] - pub(crate) fn new(_store: &impl AsStoreRef, func: Function) -> Self { - Self { - func, - _phantom: PhantomData, - } - } - - pub(crate) fn into_function(self) -> Function { - self.func - } -} diff --git a/lib/api/src/into_bytes.rs b/lib/api/src/utils/into_bytes.rs similarity index 100% rename from lib/api/src/into_bytes.rs rename to lib/api/src/utils/into_bytes.rs diff --git a/lib/api/src/utils/macros/mod.rs b/lib/api/src/utils/macros/mod.rs new file mode 100644 index 00000000000..272c0810317 --- /dev/null +++ b/lib/api/src/utils/macros/mod.rs @@ -0,0 +1 @@ +pub(crate) mod rt; diff --git a/lib/api/src/utils/macros/rt.rs b/lib/api/src/utils/macros/rt.rs new file mode 100644 index 00000000000..c95c948be93 --- /dev/null +++ b/lib/api/src/utils/macros/rt.rs @@ -0,0 +1,174 @@ +/// Automatically generate "runtime" types. +#[macro_use] +macro_rules! gen_rt_ty { + // In this case we automatically try to create the struct following the canonical path to the + // same entity in each runtime. + ($id:ident) => { + paste::paste! { + pub(crate) enum [] { + #[cfg(feature = "sys")] + /// The implementation from the `sys` runtime. + Sys(crate::rt::sys::entities::[<$id:lower>]::$id), + + #[cfg(feature = "v8")] + /// The implementation from the `v8` runtime. + V8(crate::rt::v8::entities::[<$id:lower>]::$id), + + #[cfg(feature = "wamr")] + /// The implementation from the `wamr` runtime. + Wamr(crate::rt::wamr::entities::[<$id:lower>]::$id), + + #[cfg(feature = "wasmi")] + /// The implementation from the `wasmi` runtime. + Wasmi(crate::rt::wasmi::entities::[<$id:lower>]::$id), + + #[cfg(feature = "js")] + /// The implementation from the `js` runtime. + Js(crate::rt::js::entities::[<$id:lower>]::$id), + + #[cfg(feature = "jsc")] + /// The implementation from the `jsc` runtime. + Jsc(crate::rt::jsc::entities::[<$id:lower>]::$id), + } + } + }; + + ($id:ident$(<$lt:lifetime>)? $(@cfg $($if:meta => $then: meta),*)? @derives $($derive:path),*) => { + paste::paste! { + $($(#[cfg_attr($if, $then)])*)? + #[derive($($derive,)*)] + pub(crate) enum []$(<$lt>)? { + #[cfg(feature = "sys")] + /// The implementation from the `sys` runtime. + Sys(crate::rt::sys::entities::[<$id:lower>]::$id$(<$lt>)?), + + #[cfg(feature = "v8")] + /// The implementation from the `v8` runtime. + V8(crate::rt::v8::entities::[<$id:lower>]::$id$(<$lt>)?), + + #[cfg(feature = "wamr")] + /// The implementation from the `wamr` runtime. + Wamr(crate::rt::wamr::entities::[<$id:lower>]::$id$(<$lt>)?), + + #[cfg(feature = "wasmi")] + /// The implementation from the `wasmi` runtime. + Wasmi(crate::rt::wasmi::entities::[<$id:lower>]::$id$(<$lt>)?), + + #[cfg(feature = "js")] + /// The implementation from the `js` runtime. + Js(crate::rt::js::entities::[<$id:lower>]::$id$(<$lt>)?), + + + #[cfg(feature = "jsc")] + /// The implementation from the `jsc` runtime. + Jsc(crate::rt::jsc::entities::[<$id:lower>]::$id$(<$lt>)?), + } + } + }; + + ($id:ident$(<$lt:lifetime>)? $(@cfg $($if:meta => $then: meta),*)? @derives $($derive:path),* ; @path $path:path ) => { + paste::paste! { + $($(#[cfg_attr($if, $then)])*)? + #[derive($($derive,)*)] + pub(crate) enum []$(<$lt>)? { + #[cfg(feature = "sys")] + /// The implementation from the `sys` runtime. + Sys(crate::rt::sys::entities::$path::$id$(<$lt>)?), + + #[cfg(feature = "v8")] + /// The implementation from the `v8` runtime. + V8(crate::rt::v8::entities::$path::$id$(<$lt>)?), + + #[cfg(feature = "wamr")] + /// The implementation from the `wamr` runtime. + Wamr(crate::rt::wamr::entities::$path::$id$(<$lt>)?), + + #[cfg(feature = "wasmi")] + /// The implementation from the `wasmi` runtime. + Wasmi(crate::rt::wasmi::entities::$path::$id$(<$lt>)?), + + #[cfg(feature = "js")] + /// The implementation from the `js` runtime. + Js(crate::rt::js::entities::$path::$id$(<$lt>)?), + + + #[cfg(feature = "jsc")] + /// The implementation from the `jsc` runtime. + Jsc(crate::rt::jsc::entities::$path::$id$(<$lt>)?), + } + } + }; + + + ($id:ident @derives $($derive:path),* ; @path $path:path) => { + paste::paste! { + #[derive($($derive,)*)] + pub(crate) enum [] { + #[cfg(feature = "sys")] + /// The implementation from the `sys` runtime. + Sys(crate::rt::sys::entities::$path::$id), + + #[cfg(feature = "v8")] + /// The implementation from the `v8` runtime. + V8(crate::rt::v8::entities::$path::$id), + + #[cfg(feature = "wamr")] + /// The implementation from the `wamr` runtime. + Wamr(crate::rt::wamr::entities::$path::$id), + + #[cfg(feature = "wasmi")] + /// The implementation from the `wasmi` runtime. + Wasmi(crate::rt::wasmi::entities::$path::$id), + + #[cfg(feature = "js")] + /// The implementation from the `js` runtime. + Js(crate::rt::js::entities::$path::$id), + + + #[cfg(feature = "jsc")] + /// The implementation from the `jsc` runtime. + Jsc(crate::rt::jsc::entities::$path::$id), + } + } + }; +} + +/// Automatically create a match statement, repeating the expression for each runtime. +#[macro_use] +macro_rules! match_rt { + (on $self:expr => $var:ident { $stmt:expr }) => { + match $self { + #[cfg(feature = "sys")] + Self::Sys($var) => $stmt, + #[cfg(feature = "wamr")] + Self::Wamr($var) => $stmt, + #[cfg(feature = "wasmi")] + Self::Wasmi($var) => $stmt, + #[cfg(feature = "v8")] + Self::V8($var) => $stmt, + #[cfg(feature = "js")] + Self::Js($var) => $stmt, + #[cfg(feature = "jsc")] + Self::Jsc($var) => $stmt, + } + }; + + (on $value:expr ; $match:expr => $var:ident { $stmt:expr }) => { + match $self { + #[cfg(feature = "sys")] + Self::Sys($var) => $stmt, + #[cfg(feature = "wamr")] + Self::Wamr($var) => $stmt, + #[cfg(feature = "wasmi")] + Self::Wasmi($var) => $stmt, + #[cfg(feature = "v8")] + Self::V8($var) => $stmt, + #[cfg(feature = "js")] + Self::Js($var) => $stmt, + #[cfg(feature = "jsc")] + Self::Jsc($var) => $stmt, + } + }; +} + +pub(crate) use {gen_rt_ty, match_rt}; diff --git a/lib/api/src/access.rs b/lib/api/src/utils/mem/access.rs similarity index 62% rename from lib/api/src/access.rs rename to lib/api/src/utils/mem/access.rs index 32ec42b5cec..6d8b739a429 100644 --- a/lib/api/src/access.rs +++ b/lib/api/src/utils/mem/access.rs @@ -1,8 +1,11 @@ use std::mem::MaybeUninit; -use crate::mem_access::{WasmRef, WasmSlice}; +use crate::{ + utils::mem::{WasmRef, WasmSlice}, + MemoryAccessError, +}; -pub(super) enum SliceCow<'a, T> { +pub(crate) enum SliceCow<'a, T> { #[allow(dead_code)] Borrowed(&'a mut [T]), #[allow(dead_code)] @@ -39,8 +42,8 @@ pub struct WasmSliceAccess<'a, T> where T: wasmer_types::ValueType, { - pub(super) slice: WasmSlice<'a, T>, - pub(super) buf: SliceCow<'a, T>, + pub(crate) slice: WasmSlice<'a, T>, + pub(crate) buf: SliceCow<'a, T>, } impl<'a, T> AsRef<[T]> for WasmSliceAccess<'a, T> @@ -122,7 +125,7 @@ where } } -pub(super) enum RefCow<'a, T> { +pub(crate) enum RefCow<'a, T> { #[allow(dead_code)] Borrowed(&'a mut T), #[allow(dead_code)] @@ -159,8 +162,8 @@ pub struct WasmRefAccess<'a, T> where T: wasmer_types::ValueType, { - pub(super) ptr: WasmRef<'a, T>, - pub(super) buf: RefCow<'a, T>, + pub(crate) ptr: WasmRef<'a, T>, + pub(crate) buf: RefCow<'a, T>, } impl<'a, T> AsRef for WasmRefAccess<'a, T> @@ -217,3 +220,92 @@ where ret } } + +impl<'a, T> WasmSliceAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + pub(crate) fn new(slice: WasmSlice<'a, T>) -> Result { + let total_len = slice + .len + .checked_mul(std::mem::size_of::() as u64) + .ok_or(MemoryAccessError::Overflow)?; + let end = slice + .offset + .checked_add(total_len) + .ok_or(MemoryAccessError::Overflow)?; + if end > slice.buffer.len() as u64 { + tracing::warn!( + "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", + total_len, + end, + slice.buffer.len() + ); + return Err(MemoryAccessError::HeapOutOfBounds); + } + let buf = unsafe { + let buf_ptr: *mut u8 = slice.buffer.base().add(slice.offset as usize); + let buf_ptr: *mut T = std::mem::transmute(buf_ptr); + std::slice::from_raw_parts_mut(buf_ptr, slice.len as usize) + }; + Ok(Self { + slice, + buf: SliceCow::Borrowed(buf), + }) + } +} + +impl<'a, T> WasmRefAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + pub(crate) fn new(ptr: WasmRef<'a, T>) -> Result { + let total_len = std::mem::size_of::() as u64; + let end = ptr + .offset + .checked_add(total_len) + .ok_or(MemoryAccessError::Overflow)?; + if end > ptr.buffer.len() as u64 { + tracing::warn!( + "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", + total_len, + end, + ptr.buffer.len() + ); + return Err(MemoryAccessError::HeapOutOfBounds); + } + let val = unsafe { + let val_ptr: *mut u8 = ptr.buffer.base().add(ptr.offset as usize); + let val_ptr: *mut T = std::mem::transmute(val_ptr); + &mut *val_ptr + }; + Ok(Self { + ptr, + buf: RefCow::Borrowed(val), + }) + } +} + +impl<'a, T> WasmRefAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + /// Reads the address pointed to by this `WasmPtr` in a memory. + #[inline] + #[allow(clippy::clone_on_copy)] + pub fn read(&self) -> T + where + T: Clone, + { + self.as_ref().clone() + } + + /// Writes to the address pointed to by this `WasmPtr` in a memory. + #[inline] + pub fn write(&mut self, val: T) { + // Note: Zero padding is not required here as its a typed copy which does + // not leak the bytes into the memory + // https://stackoverflow.com/questions/61114026/does-stdptrwrite-transfer-the-uninitialized-ness-of-the-bytes-it-writes + *(self.as_mut()) = val; + } +} diff --git a/lib/api/src/mem_access.rs b/lib/api/src/utils/mem/mod.rs similarity index 94% rename from lib/api/src/mem_access.rs rename to lib/api/src/utils/mem/mod.rs index 0acc2b2850d..3ac3228b2d3 100644 --- a/lib/api/src/mem_access.rs +++ b/lib/api/src/utils/mem/mod.rs @@ -1,18 +1,21 @@ -use crate::access::WasmRefAccess; -use crate::externals::memory::MemoryBuffer; -use crate::{Memory32, Memory64, MemorySize, MemoryView, WasmPtr}; -use crate::{RuntimeError, WasmSliceAccess}; -use std::convert::TryInto; -use std::fmt; -use std::marker::PhantomData; -use std::mem::{self, MaybeUninit}; -use std::ops::Range; -use std::slice; -use std::string::FromUtf8Error; +pub(crate) mod access; +pub(crate) mod ptr; +pub use ptr::*; + +use std::{ + marker::PhantomData, + mem::{self, MaybeUninit}, + ops::Range, + slice, + string::FromUtf8Error, +}; + +use crate::{buffer::MemoryBuffer, error::RuntimeError, view::MemoryView}; +use access::{WasmRefAccess, WasmSliceAccess}; use thiserror::Error; -use wasmer_types::ValueType; +pub use wasmer_types::{Memory32, Memory64, MemorySize, ValueType}; -/// Error for invalid [`Memory`][super::Memory] access. +/// Error for invalid [`Memory`][crate::Memory] access. #[derive(Clone, Copy, Debug, Error)] #[non_exhaustive] pub enum MemoryAccessError { @@ -121,8 +124,8 @@ impl<'a, T: ValueType> WasmRef<'a, T> { } } -impl<'a, T: ValueType> fmt::Debug for WasmRef<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl<'a, T: ValueType> std::fmt::Debug for WasmRef<'a, T> { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, "WasmRef(offset: {}, pointer: {:#x})", @@ -356,8 +359,8 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { } } -impl<'a, T: ValueType> fmt::Debug for WasmSlice<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl<'a, T: ValueType> std::fmt::Debug for WasmSlice<'a, T> { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, "WasmSlice(offset: {}, len: {}, pointer: {:#x})", diff --git a/lib/api/src/ptr.rs b/lib/api/src/utils/mem/ptr.rs similarity index 92% rename from lib/api/src/ptr.rs rename to lib/api/src/utils/mem/ptr.rs index b1443a24cba..257e2a1cb29 100644 --- a/lib/api/src/ptr.rs +++ b/lib/api/src/utils/mem/ptr.rs @@ -1,12 +1,10 @@ -use crate::access::WasmRefAccess; -use crate::mem_access::{MemoryAccessError, WasmRef, WasmSlice}; -use crate::{AsStoreRef, FromToNativeWasmType, MemoryView, NativeWasmTypeInto}; -use std::convert::TryFrom; -use std::{fmt, marker::PhantomData, mem}; -pub use wasmer_types::Memory32; -pub use wasmer_types::Memory64; -pub use wasmer_types::MemorySize; -use wasmer_types::ValueType; +use std::marker::PhantomData; +use wasmer_types::{Memory32, Memory64, MemorySize, ValueType}; + +use crate::{ + access::WasmRefAccess, view::MemoryView, AsStoreRef, FromToNativeWasmType, MemoryAccessError, + NativeWasmTypeInto, WasmRef, WasmSlice, +}; /// Alias for `WasmPtr. pub type WasmPtr64 = WasmPtr; @@ -109,7 +107,7 @@ impl WasmPtr { let base = self.offset.into(); let index = offset.into(); let offset = index - .checked_mul(mem::size_of::() as u64) + .checked_mul(std::mem::size_of::() as u64) .ok_or(MemoryAccessError::Overflow)?; let address = base .checked_add(offset) @@ -127,7 +125,7 @@ impl WasmPtr { let base = self.offset.into(); let index = offset.into(); let offset = index - .checked_mul(mem::size_of::() as u64) + .checked_mul(std::mem::size_of::() as u64) .ok_or(MemoryAccessError::Overflow)?; let address = base .checked_sub(offset) @@ -254,7 +252,7 @@ where } unsafe impl ValueType for WasmPtr { - fn zero_padding_bytes(&self, _bytes: &mut [mem::MaybeUninit]) {} + fn zero_padding_bytes(&self, _bytes: &mut [std::mem::MaybeUninit]) {} } impl Clone for WasmPtr { @@ -273,8 +271,8 @@ impl PartialEq for WasmPtr { impl Eq for WasmPtr {} -impl fmt::Debug for WasmPtr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl std::fmt::Debug for WasmPtr { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}(@{})", std::any::type_name::(), self.offset.into()) } } diff --git a/lib/api/src/utils/mod.rs b/lib/api/src/utils/mod.rs new file mode 100644 index 00000000000..1016dcb4a1e --- /dev/null +++ b/lib/api/src/utils/mod.rs @@ -0,0 +1,22 @@ +//! Useful data types, functions and traits used throughout the crate to interact with WebAssembly +//! entities such as [`Memory`] and [`Value`]. + +/// Convert bynary data into [`bytes::Bytes`]. +mod into_bytes; +pub use into_bytes::IntoBytes; + +/// Useful data types, functions and traits for the interaction between host types and WebAssembly. +pub(crate) mod native; +pub use native::*; + +/// Useful data types, functions and traits for interacting with the memory of a [`crate::Instance`]. +pub(crate) mod mem; +pub use mem::*; + +/// Useful macros to generate enums to represent `Runtime`-types. +pub(crate) mod rt_macros; + +#[cfg(any(feature = "wasm-types-polyfill", feature = "jsc"))] +pub(crate) mod polyfill; + +pub(crate) mod macros; diff --git a/lib/api/src/native_type.rs b/lib/api/src/utils/native/convert.rs similarity index 84% rename from lib/api/src/native_type.rs rename to lib/api/src/utils/native/convert.rs index 5879ff3da7c..afd2040a940 100644 --- a/lib/api/src/native_type.rs +++ b/lib/api/src/utils/native/convert.rs @@ -1,16 +1,17 @@ -//! This module permits to create native functions -//! easily in Rust, thanks to its advanced typing system. - -use wasmer_types::{NativeWasmType, RawValue, Type}; +pub use wasmer_types::NativeWasmType; +use wasmer_types::{RawValue, Type}; use crate::store::AsStoreRef; -use crate::vm::{VMExternRef, VMFuncRef}; +use crate::{ + vm::{VMExternRef, VMFuncRef}, + ExternRef, Function, TypedFunction, +}; -use crate::{ExternRef, Function, TypedFunction}; -use std::array::TryFromSliceError; -use std::convert::Infallible; -use std::convert::TryInto; use std::error::Error; +use std::{ + array::TryFromSliceError, + convert::{Infallible, TryInto}, +}; use crate::store::AsStoreMut; @@ -195,8 +196,38 @@ impl NativeWasmType for ExternRef { impl NativeWasmTypeInto for Option { #[inline] unsafe fn from_abi(store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { - VMExternRef::from_raw(RawValue { externref: abi }) - .map(|e| ExternRef::from_vm_externref(store, e)) + match store.as_store_ref().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(_) => { + wasmer_vm::VMExternRef::from_raw(RawValue { externref: abi }).map(VMExternRef::Sys) + } + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(_) => { + crate::rt::wamr::vm::VMExternRef::from_raw(RawValue { externref: abi }) + .map(VMExternRef::Wamr) + } + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(_) => { + crate::rt::wasmi::vm::VMExternRef::from_raw(RawValue { externref: abi }) + .map(VMExternRef::Wasmi) + } + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(_) => { + crate::rt::v8::vm::VMExternRef::from_raw(RawValue { externref: abi }) + .map(VMExternRef::V8) + } + #[cfg(feature = "js")] + crate::RuntimeStore::Js(_) => { + crate::rt::js::vm::VMExternRef::from_raw(RawValue { externref: abi }) + .map(VMExternRef::Js) + } + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(_) => { + crate::rt::jsc::vm::VMExternRef::from_raw(RawValue { externref: abi }) + .map(VMExternRef::Jsc) + } + } + .map(|e| ExternRef::from_vm_externref(store, e)) } #[inline] @@ -211,7 +242,33 @@ impl NativeWasmTypeInto for Option { #[inline] unsafe fn from_raw(store: &mut impl AsStoreMut, raw: RawValue) -> Self { - VMExternRef::from_raw(raw).map(|e| ExternRef::from_vm_externref(store, e)) + match store.as_store_ref().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(_) => { + wasmer_vm::VMExternRef::from_raw(raw).map(VMExternRef::Sys) + } + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(_) => { + crate::rt::wamr::vm::VMExternRef::from_raw(raw).map(VMExternRef::Wamr) + } + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(_) => { + crate::rt::wasmi::vm::VMExternRef::from_raw(raw).map(VMExternRef::Wasmi) + } + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(_) => { + crate::rt::v8::vm::VMExternRef::from_raw(raw).map(VMExternRef::V8) + } + #[cfg(feature = "js")] + crate::RuntimeStore::Js(_) => { + crate::rt::js::vm::VMExternRef::from_raw(raw).map(VMExternRef::Js) + } + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(_) => { + crate::rt::jsc::vm::VMExternRef::from_raw(raw).map(VMExternRef::Jsc) + } + } + .map(|e| ExternRef::from_vm_externref(store, e)) } } @@ -233,7 +290,36 @@ impl NativeWasmType for Function { impl NativeWasmTypeInto for Option { #[inline] unsafe fn from_abi(store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { - VMFuncRef::from_raw(RawValue { funcref: abi }).map(|f| Function::from_vm_funcref(store, f)) + match store.as_store_ref().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(_) => { + wasmer_vm::VMFuncRef::from_raw(RawValue { funcref: abi }).map(VMFuncRef::Sys) + } + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(_) => { + crate::rt::wamr::vm::VMFuncRef::from_raw(RawValue { funcref: abi }) + .map(VMFuncRef::Wamr) + } + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(_) => { + crate::rt::wasmi::vm::VMFuncRef::from_raw(RawValue { funcref: abi }) + .map(VMFuncRef::Wasmi) + } + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(_) => { + crate::rt::v8::vm::VMFuncRef::from_raw(RawValue { funcref: abi }).map(VMFuncRef::V8) + } + #[cfg(feature = "js")] + crate::RuntimeStore::Js(_) => { + crate::rt::js::vm::VMFuncRef::from_raw(RawValue { funcref: abi }).map(VMFuncRef::Js) + } + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(_) => { + crate::rt::jsc::vm::VMFuncRef::from_raw(RawValue { funcref: abi }) + .map(VMFuncRef::Jsc) + } + } + .map(|f| Function::from_vm_funcref(store, f)) } #[inline] @@ -250,7 +336,31 @@ impl NativeWasmTypeInto for Option { #[inline] unsafe fn from_raw(store: &mut impl AsStoreMut, raw: RawValue) -> Self { - VMFuncRef::from_raw(raw).map(|f| Function::from_vm_funcref(store, f)) + match store.as_store_ref().inner.store { + #[cfg(feature = "sys")] + crate::RuntimeStore::Sys(_) => wasmer_vm::VMFuncRef::from_raw(raw).map(VMFuncRef::Sys), + #[cfg(feature = "wamr")] + crate::RuntimeStore::Wamr(_) => { + crate::rt::wamr::vm::VMFuncRef::from_raw(raw).map(VMFuncRef::Wamr) + } + #[cfg(feature = "wasmi")] + crate::RuntimeStore::Wasmi(_) => { + crate::rt::wasmi::vm::VMFuncRef::from_raw(raw).map(VMFuncRef::Wasmi) + } + #[cfg(feature = "v8")] + crate::RuntimeStore::V8(_) => { + crate::rt::v8::vm::VMFuncRef::from_raw(raw).map(VMFuncRef::V8) + } + #[cfg(feature = "js")] + crate::RuntimeStore::Js(_) => { + crate::rt::js::vm::VMFuncRef::from_raw(raw).map(VMFuncRef::Js) + } + #[cfg(feature = "jsc")] + crate::RuntimeStore::Jsc(_) => { + crate::rt::jsc::vm::VMFuncRef::from_raw(raw).map(VMFuncRef::Jsc) + } + } + .map(|f| Function::from_vm_funcref(store, f)) } } diff --git a/lib/api/src/utils/native/mod.rs b/lib/api/src/utils/native/mod.rs new file mode 100644 index 00000000000..65eb3152159 --- /dev/null +++ b/lib/api/src/utils/native/mod.rs @@ -0,0 +1,7 @@ +/// Traits and implementations used to convert between native values and WebAssembly ones. +pub(crate) mod convert; +pub use convert::*; + +/// Trait to interact with native functions. +pub(crate) mod typed_func; +pub use typed_func::*; diff --git a/lib/api/src/utils/native/typed_func.rs b/lib/api/src/utils/native/typed_func.rs new file mode 100644 index 00000000000..e96fc5db720 --- /dev/null +++ b/lib/api/src/utils/native/typed_func.rs @@ -0,0 +1,132 @@ +//! Native Functions. +//! +//! This module creates the helper [`TypedFunction`] that let us call WebAssembly +//! functions with the native ABI, that is: +//! +//! ```ignore +//! let add_one = instance.exports.get_function("function_name")?; +//! let add_one_native: TypedFunction = add_one.native().unwrap(); +//! ``` +use crate::{ + store::AsStoreRef, AsStoreMut, FromToNativeWasmType, Function, NativeWasmTypeInto, + RuntimeError, RuntimeStore, WasmTypeList, +}; +use std::marker::PhantomData; +use wasmer_types::RawValue; + +/// A WebAssembly function that can be called natively +/// (using the Native ABI). +#[derive(Clone, Debug)] +pub struct TypedFunction { + pub(crate) func: Function, + _phantom: PhantomData Rets>, +} + +unsafe impl Send for TypedFunction {} +unsafe impl Sync for TypedFunction {} + +impl TypedFunction +where + Args: WasmTypeList, + Rets: WasmTypeList, +{ + #[allow(dead_code)] + pub(crate) fn new(_store: &impl AsStoreRef, func: Function) -> Self { + Self { + func, + _phantom: PhantomData, + } + } + + pub(crate) fn into_function(self) -> Function { + self.func + } +} + +macro_rules! impl_native_traits { + ( $( $x:ident ),* ) => { + paste::paste!{ + #[allow(unused_parens, non_snake_case)] + impl<$( $x , )* Rets> TypedFunction<( $( $x ),* ), Rets> + where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + { + /// Call the typed func and return results. + #[allow(unused_mut)] + #[allow(clippy::too_many_arguments)] + pub fn call(&self, store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result where $( $x: FromToNativeWasmType, )* + + { + $( + let [] = $x; + )* + match store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + RuntimeStore::Sys(_) => self.call_sys(store, $([]),*), + #[cfg(feature = "wamr")] + RuntimeStore::Wamr(_) => self.call_wamr(store, $([]),*), + #[cfg(feature = "wasmi")] + RuntimeStore::Wasmi(_) => self.call_wasmi(store, $([]),*), + #[cfg(feature = "v8")] + RuntimeStore::V8(_) => self.call_v8(store, $([]),*), + #[cfg(feature = "js")] + RuntimeStore::Js(_) => self.call_js(store, $([]),*), + #[cfg(feature = "jsc")] + RuntimeStore::Jsc(_) => self.call_jsc(store, $([]),*), + + } + } + + #[doc(hidden)] + #[allow(missing_docs)] + #[allow(unused_mut)] + #[allow(clippy::too_many_arguments)] + pub fn call_raw(&self, store: &mut impl AsStoreMut, mut params_list: Vec ) -> Result { + match store.as_store_mut().inner.store { + #[cfg(feature = "sys")] + RuntimeStore::Sys(_) => self.call_raw_sys(store, params_list), + #[cfg(feature = "wamr")] + RuntimeStore::Wamr(_) => self.call_raw_wamr(store, params_list), + #[cfg(feature = "wasmi")] + RuntimeStore::Wasmi(_) => self.call_raw_wasmi(store, params_list), + #[cfg(feature = "v8")] + RuntimeStore::V8(_) => self.call_raw_v8(store, params_list), + #[cfg(feature = "js")] + RuntimeStore::Js(_) => self.call_raw_js(store, params_list), + #[cfg(feature = "jsc")] + RuntimeStore::Jsc(_) => self.call_raw_jsc(store, params_list), + } + } + } + } + }; +} + +impl_native_traits!(); +impl_native_traits!(A1); +impl_native_traits!(A1, A2); +impl_native_traits!(A1, A2, A3); +impl_native_traits!(A1, A2, A3, A4); +impl_native_traits!(A1, A2, A3, A4, A5); +impl_native_traits!(A1, A2, A3, A4, A5, A6); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17); +impl_native_traits!( + A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18 +); +impl_native_traits!( + A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19 +); +impl_native_traits!( + A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20 +); diff --git a/lib/api/src/module_info_polyfill.rs b/lib/api/src/utils/polyfill.rs similarity index 98% rename from lib/api/src/module_info_polyfill.rs rename to lib/api/src/utils/polyfill.rs index 0da88084444..09ea47b1057 100644 --- a/lib/api/src/module_info_polyfill.rs +++ b/lib/api/src/utils/polyfill.rs @@ -2,8 +2,7 @@ //! creates the corresponding import and export types. //! //! This shall not be needed once the JS type reflection API is available -//! for the Wasm imports and exports. -//! +//! for the Wasm imports and exports.//! //! use core::convert::TryFrom; use std::vec::Vec; @@ -250,7 +249,7 @@ fn transform_err(err: BinaryReaderError) -> String { /// Translate a sequence of bytes forming a valid Wasm binary into a /// parsed ModuleInfo `ModuleInfoPolyfill`. -pub fn translate_module<'data>(data: &'data [u8]) -> WasmResult { +pub fn translate_module(data: &[u8]) -> WasmResult { let mut module_info: ModuleInfoPolyfill = Default::default(); for payload in Parser::new(0).parse_all(data) { @@ -371,8 +370,8 @@ pub fn parse_type_section( } /// Parses the Import section of the wasm module. -pub fn parse_import_section<'data>( - imports: ImportSectionReader<'data>, +pub fn parse_import_section( + imports: ImportSectionReader<'_>, module_info: &mut ModuleInfoPolyfill, ) -> WasmResult<()> { module_info.reserve_imports(imports.count())?; @@ -526,8 +525,8 @@ pub fn parse_global_section( } /// Parses the Export section of the wasm module. -pub fn parse_export_section<'data>( - exports: ExportSectionReader<'data>, +pub fn parse_export_section( + exports: ExportSectionReader<'_>, module_info: &mut ModuleInfoPolyfill, ) -> WasmResult<()> { module_info.reserve_exports(exports.count())?; @@ -571,8 +570,8 @@ pub fn parse_export_section<'data>( // } /// Parses the Name section of the wasm module. -pub fn parse_name_section<'data>( - mut names: NameSectionReader<'data>, +pub fn parse_name_section( + mut names: NameSectionReader<'_>, module_info: &mut ModuleInfoPolyfill, ) -> WasmResult<()> { while let Some(Ok(subsection)) = names.next() { diff --git a/lib/api/src/utils/rt_macros.rs b/lib/api/src/utils/rt_macros.rs new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/lib/api/src/utils/rt_macros.rs @@ -0,0 +1 @@ + diff --git a/lib/api/src/vm.rs b/lib/api/src/vm.rs deleted file mode 100644 index a10dd901f4c..00000000000 --- a/lib/api/src/vm.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! The `vm` module re-exports wasmer-vm types. - -#[cfg(feature = "js")] -pub(crate) use crate::js::vm::{ - VMExtern, VMExternFunction, VMExternGlobal, VMExternMemory, VMExternRef, VMExternTable, - VMFuncRef, VMFunctionCallback, VMFunctionEnvironment, VMInstance, VMTrampoline, -}; - -#[cfg(feature = "jsc")] -pub(crate) use crate::jsc::vm::{ - VMExtern, VMExternFunction, VMExternGlobal, VMExternMemory, VMExternRef, VMExternTable, - VMFuncRef, VMFunctionCallback, VMFunctionEnvironment, VMInstance, VMTrampoline, -}; - -#[cfg(feature = "wasm-c-api")] -pub(crate) use crate::c_api::vm::{ - VMExtern, VMExternFunction, VMExternGlobal, VMExternMemory, VMExternRef, VMExternTable, - VMFuncRef, VMFunctionCallback, VMFunctionEnvironment, VMInstance, VMTrampoline, -}; - -#[cfg(feature = "sys")] -pub(crate) use crate::sys::vm::{ - VMExtern, VMExternFunction, VMExternGlobal, VMExternMemory, VMExternRef, VMExternTable, - VMFuncRef, VMFunctionCallback, VMFunctionEnvironment, VMInstance, VMTrampoline, -}; - -#[cfg(feature = "js")] -pub use crate::js::vm::{VMFunction, VMGlobal, VMMemory, VMSharedMemory, VMTable}; - -#[cfg(feature = "sys")] -pub use wasmer_vm::{VMConfig, VMFunction, VMGlobal, VMMemory, VMSharedMemory, VMTable}; - -#[cfg(feature = "jsc")] -pub use crate::jsc::vm::{VMFunction, VMGlobal, VMMemory, VMSharedMemory, VMTable}; - -#[cfg(feature = "wasm-c-api")] -pub use crate::c_api::vm::{VMFunction, VMGlobal, VMMemory, VMSharedMemory, VMTable}; - -// Needed for tunables customization (those are public types now) -#[cfg(feature = "sys")] -pub use wasmer_vm::{ - // An extra one for VMMemory implementors - LinearMemory, - VMMemoryDefinition, - VMTableDefinition, -}; - -// Deprecated exports -pub use wasmer_types::{MemoryError, MemoryStyle, TableStyle}; diff --git a/lib/api/src/vm/impls.rs b/lib/api/src/vm/impls.rs new file mode 100644 index 00000000000..323520a756e --- /dev/null +++ b/lib/api/src/vm/impls.rs @@ -0,0 +1,53 @@ +use crate::macros::rt::match_rt; + +use super::*; + +impl VMExternToExtern for VMExtern { + fn to_extern(self, store: &mut impl crate::AsStoreMut) -> crate::Extern { + match_rt!(on self => s { + s.to_extern(store) + }) + } +} + +impl VMFunctionEnvironment { + #[allow(clippy::should_implement_trait)] + /// Returns a reference to the underlying value. + pub fn as_ref(&self) -> &(dyn std::any::Any + Send + 'static) { + match_rt!(on self => s { + s.as_ref() + }) + } + + #[allow(clippy::should_implement_trait)] + /// Returns a mutable reference to the underlying value. + pub fn as_mut(&mut self) -> &mut (dyn std::any::Any + Send + 'static) { + match_rt!(on self => s { + s.as_mut() + }) + } + + pub fn contents(self) -> Box<(dyn std::any::Any + Send + 'static)> { + match_rt!(on self => s { + s.contents + }) + } +} + +impl VMFuncRef { + /// Converts the `VMFuncRef` into a `RawValue`. + pub fn into_raw(self) -> RawValue { + match_rt!(on self => s { + s.into_raw() + }) + } +} + +impl VMExternRef { + /// Converts the `VMExternRef` into a `RawValue`. + pub fn into_raw(self) -> RawValue { + match_rt!(on self => s { + s.into_raw() + }) + } +} diff --git a/lib/api/src/vm/mod.rs b/lib/api/src/vm/mod.rs new file mode 100644 index 00000000000..c4265af2765 --- /dev/null +++ b/lib/api/src/vm/mod.rs @@ -0,0 +1,411 @@ +//! This module defines traits to handle abstractions created by the runtimes. + +mod impls; + +use crate::VMExternToExtern; +use wasmer_types::RawValue; + +macro_rules! define_vm_like { + ($name: ident) => { + paste::paste! { + /// The enum for all those VM values of this kind. + #[repr(C)] + pub enum [] { + #[cfg(feature = "sys")] + Sys(crate::rt::sys::vm::[]), + + #[cfg(feature = "wamr")] + Wamr(crate::rt::wamr::vm::[]), + + + #[cfg(feature = "wasmi")] + Wasmi(crate::rt::wasmi::vm::[]), + + + #[cfg(feature = "v8")] + V8(crate::rt::v8::vm::[]), + + #[cfg(feature = "js")] + Js(crate::rt::js::vm::[]), + + #[cfg(feature = "jsc")] + Jsc(crate::rt::jsc::vm::[]), + + } + + impl [] { + #[cfg(feature = "sys")] + /// Consume `self` into a `sys` VM kind. + pub fn into_sys(self) -> crate::rt::sys::vm::[] { + match self { + []::Sys(s) => s, + _ => panic!("Not a `sys` value!") + } + } + + #[cfg(feature = "sys")] + /// Convert a reference to [`self`] into a reference to the same `sys` VM kind. + pub fn as_sys(&self) -> &crate::rt::sys::vm::[] { + match self { + []::Sys(s) => s, + _ => panic!("Not a `sys` value!") + } + } + + #[cfg(feature = "sys")] + /// Convert a mutable reference to [`self`] into a mutable reference to the same `sys` VM kind. + pub fn as_sys_mut(&mut self) -> &mut crate::rt::sys::vm::[] { + match self { + []::Sys(s) => s, + _ => panic!("Not a `sys` value!") + } + } + + #[cfg(feature = "wamr")] + /// Consume `self` into a `wamr` VM kind. + pub fn into_wamr(self) -> crate::rt::wamr::vm::[] { + match self { + []::Wamr(s) => s, + _ => panic!("Not a `wamr` value!") + } + } + + #[cfg(feature = "wamr")] + /// Convert a reference to [`self`] into a reference to the same `wamr` VM kind. + pub fn as_wamr(&self) -> &crate::rt::wamr::vm::[] { + match self { + []::Wamr(s) => s, + _ => panic!("Not a `wamr` value!") + } + } + + #[cfg(feature = "wamr")] + /// Convert a mutable reference to [`self`] into a mutable reference to the same `wamr` VM kind. + pub fn as_wamr_mut(&mut self) -> &mut crate::rt::wamr::vm::[] { + match self { + []::Wamr(s) => s, + _ => panic!("Not a `wamr` value!") + } + } + + #[cfg(feature = "wasmi")] + /// Consume `self` into a `wasmi` VM kind. + pub fn into_wasmi(self) -> crate::rt::wasmi::vm::[] { + match self { + []::Wasmi(s) => s, + _ => panic!("Not a `wasmi` value!") + } + } + + #[cfg(feature = "wasmi")] + /// Convert a reference to [`self`] into a reference to the same `wasmi` VM kind. + pub fn as_wasmi(&self) -> &crate::rt::wasmi::vm::[] { + match self { + []::Wasmi(s) => s, + _ => panic!("Not a `wasmi` value!") + } + } + + #[cfg(feature = "wasmi")] + /// Convert a mutable reference to [`self`] into a mutable reference to the same `wasmi` VM kind. + pub fn as_wasmi_mut(&mut self) -> &mut crate::rt::wasmi::vm::[] { + match self { + []::Wasmi(s) => s, + _ => panic!("Not a `wasmi` value!") + } + } + + #[cfg(feature = "v8")] + /// Consume `self` into a `v8` VM kind. + pub fn into_v8(self) -> crate::rt::v8::vm::[] { + match self { + []::V8(s) => s, + _ => panic!("Not a `v8` value!") + } + } + + #[cfg(feature = "v8")] + /// Convert a reference to [`self`] into a reference to the same `v8` VM kind. + pub fn as_v8(&self) -> &crate::rt::v8::vm::[] { + match self { + []::V8(s) => s, + _ => panic!("Not a `v8` value!") + } + } + + #[cfg(feature = "v8")] + /// Convert a mutable reference to [`self`] into a mutable reference to the same `v8` VM kind. + pub fn as_v8_mut(&mut self) -> &mut crate::rt::v8::vm::[] { + match self { + []::V8(s) => s, + _ => panic!("Not a `v8` value!") + } + } + + #[cfg(feature = "js")] + /// Consume `self` into a `js` VM kind. + pub fn into_js(self) -> crate::rt::js::vm::[] { + match self { + []::Js(s) => s, + _ => panic!("Not a `js` value!") + } + } + + #[cfg(feature = "js")] + /// Convert a reference to [`self`] into a reference to the same `js` VM kind. + pub fn as_js(&self) -> &crate::rt::js::vm::[] { + match self { + []::Js(s) => s, + _ => panic!("Not a `js` value!") + } + } + + #[cfg(feature = "js")] + /// Convert a mutable reference to [`self`] into a mutable reference to the same `js` VM kind. + pub fn as_js_mut(&mut self) -> &mut crate::rt::js::vm::[] { + match self { + []::Js(s) => s, + _ => panic!("Not a `js` value!") + } + } + + #[cfg(feature = "jsc")] + /// Consume `self` into a `jsc` VM kind. + pub fn into_jsc(self) -> crate::rt::jsc::vm::[] { + match self { + []::Jsc(s) => s, + _ => panic!("Not a `jsc` value!") + } + } + + #[cfg(feature = "jsc")] + /// Convert a reference to [`self`] into a reference to the same `jsc` VM kind. + pub fn as_jsc(&self) -> &crate::rt::jsc::vm::[] { + match self { + []::Jsc(s) => s, + _ => panic!("Not a `jsc` value!") + } + } + + #[cfg(feature = "jsc")] + /// Convert a mutable reference to [`self`] into a mutable reference to the same `jsc` VM kind. + pub fn as_jsc_mut(&mut self) -> &mut crate::rt::jsc::vm::[] { + match self { + []::Jsc(s) => s, + _ => panic!("Not a `jsc` value!") + } + } + } + } + }; + + ($name: ident $(, $derive:ident)*) => { + paste::paste! { + /// The enum for all those VM values of this kind. + $(#[derive($derive)])* + #[repr(C)] + pub enum [] { + #[cfg(feature = "sys")] + Sys(crate::rt::sys::vm::[]), + #[cfg(feature = "wamr")] + Wamr(crate::rt::wamr::vm::[]), + #[cfg(feature = "wasmi")] + Wasmi(crate::rt::wasmi::vm::[]), + #[cfg(feature = "v8")] + V8(crate::rt::v8::vm::[]), + #[cfg(feature = "js")] + Js(crate::rt::js::vm::[]), + #[cfg(feature = "jsc")] + Jsc(crate::rt::jsc::vm::[]), + } + + impl [] { + #[cfg(feature = "sys")] + /// Consume `self` into a `sys` VM kind. + pub fn into_sys(self) -> crate::rt::sys::vm::[] { + match self { + []::Sys(s) => s, + _ => panic!("Not a `sys` value!") + } + } + + #[cfg(feature = "sys")] + /// Convert a reference to [`self`] into a reference to the same `sys` VM kind. + pub fn as_sys(&self) -> &crate::rt::sys::vm::[] { + match self { + []::Sys(s) => s, + _ => panic!("Not a `sys` value!") + } + } + + #[cfg(feature = "sys")] + /// Convert a mutable reference to [`self`] into a mutable reference to the same `sys` VM kind. + pub fn as_sys_mut(&mut self) -> &mut crate::rt::sys::vm::[] { + match self { + []::Sys(s) => s, + _ => panic!("Not a `sys` value!") + } + } + + #[cfg(feature = "wamr")] + /// Consume `self` into a `wamr` VM kind. + pub fn into_wamr(self) -> crate::rt::wamr::vm::[] { + match self { + []::Wamr(s) => s, + _ => panic!("Not a `wamr` value!") + } + } + + #[cfg(feature = "wamr")] + /// Convert a reference to [`self`] into a reference to the same `wamr` VM kind. + pub fn as_wamr(&self) -> &crate::rt::wamr::vm::[] { + match self { + []::Wamr(s) => s, + _ => panic!("Not a `wamr` value!") + } + } + + #[cfg(feature = "wamr")] + /// Convert a mutable reference to [`self`] into a mutable reference to the same `wamr` VM kind. + pub fn as_wamr_mut(&mut self) -> &mut crate::rt::wamr::vm::[] { + match self { + []::Wamr(s) => s, + _ => panic!("Not a `wamr` value!") + } + } + + #[cfg(feature = "wasmi")] + /// Consume `self` into a `wasmi` VM kind. + pub fn into_wasmi(self) -> crate::rt::wasmi::vm::[] { + match self { + []::Wasmi(s) => s, + _ => panic!("Not a `wasmi` value!") + } + } + + #[cfg(feature = "wasmi")] + /// Convert a reference to [`self`] into a reference to the same `wasmi` VM kind. + pub fn as_wasmi(&self) -> &crate::rt::wasmi::vm::[] { + match self { + []::Wasmi(s) => s, + _ => panic!("Not a `wasmi` value!") + } + } + + #[cfg(feature = "wasmi")] + /// Convert a mutable reference to [`self`] into a mutable reference to the same `wasmi` VM kind. + pub fn as_wasmi_mut(&mut self) -> &mut crate::rt::wasmi::vm::[] { + match self { + []::Wasmi(s) => s, + _ => panic!("Not a `wasmi` value!") + } + } + + + #[cfg(feature = "js")] + /// Consume `self` into a `js` VM kind. + pub fn into_js(self) -> crate::rt::js::vm::[] { + match self { + []::Js(s) => s, + _ => panic!("Not a `js` value!") + } + } + + #[cfg(feature = "js")] + /// Convert a reference to [`self`] into a reference to the same `js` VM kind. + pub fn as_js(&self) -> &crate::rt::js::vm::[] { + match self { + []::Js(s) => s, + _ => panic!("Not a `js` value!") + } + } + + #[cfg(feature = "js")] + /// Convert a mutable reference to [`self`] into a mutable reference to the same `js` VM kind. + pub fn as_js_mut(&mut self) -> &mut crate::rt::js::vm::[] { + match self { + []::Js(s) => s, + _ => panic!("Not a `js` value!") + } + } + + #[cfg(feature = "jsc")] + /// Consume `self` into a `jsc` VM kind. + pub fn into_jsc(self) -> crate::rt::jsc::vm::[] { + match self { + []::Jsc(s) => s, + _ => panic!("Not a `jsc` value!") + } + } + + #[cfg(feature = "jsc")] + /// Convert a reference to [`self`] into a reference to the same `jsc` VM kind. + pub fn as_jsc(&self) -> &crate::rt::jsc::vm::[] { + match self { + []::Jsc(s) => s, + _ => panic!("Not a `jsc` value!") + } + } + + #[cfg(feature = "jsc")] + /// Convert a mutable reference to [`self`] into a mutable reference to the same `jsc` VM kind. + pub fn as_jsc_mut(&mut self) -> &mut crate::rt::jsc::vm::[] { + match self { + []::Jsc(s) => s, + _ => panic!("Not a `jsc` value!") + } + } + + + #[cfg(feature = "v8")] + /// Consume `self` into a `v8` VM kind. + pub fn into_v8(self) -> crate::rt::v8::vm::[] { + match self { + []::V8(s) => s, + _ => panic!("Not a `v8` value!") + } + } + + #[cfg(feature = "v8")] + /// Convert a reference to [`self`] into a reference to the same `v8` VM kind. + pub fn as_v8(&self) -> &crate::rt::v8::vm::[] { + match self { + []::V8(s) => s, + _ => panic!("Not a `v8` value!") + } + } + + #[cfg(feature = "v8")] + /// Convert a mutable reference to [`self`] into a mutable reference to the same `v8` VM kind. + pub fn as_v8_mut(&mut self) -> &mut crate::rt::v8::vm::[] { + match self { + []::V8(s) => s, + _ => panic!("Not a `v8` value!") + } + } + } + } + }; +} + +define_vm_like!(Extern); +define_vm_like!(ExternFunction); +define_vm_like!(ExternGlobal); +define_vm_like!(ExternMemory); +define_vm_like!(ExternTable); +//define_vm_like!(ExternObj, Debug); +define_vm_like!(FunctionCallback); +define_vm_like!(FunctionBody); +define_vm_like!(FunctionEnvironment, Debug); +define_vm_like!(Instance, Debug); +define_vm_like!(Trampoline); + +//define_vm_like!(Config); +define_vm_like!(Function, Debug); +define_vm_like!(Global, Debug); +define_vm_like!(Memory, Debug); +define_vm_like!(SharedMemory); +define_vm_like!(Table, Debug); + +define_vm_like!(ExternRef); +define_vm_like!(FuncRef); diff --git a/lib/api/tests/memory.rs b/lib/api/tests/memory.rs index 23881e7b781..f65246ecf13 100644 --- a/lib/api/tests/memory.rs +++ b/lib/api/tests/memory.rs @@ -5,6 +5,7 @@ use std::sync::{ use wasmer::{imports, Instance, Memory, MemoryLocation, MemoryType, Module, Store}; #[test] +#[cfg_attr(feature = "wamr", ignore = "wamr ignores import memories")] #[cfg_attr(feature = "wasmi", ignore = "wasmi does not support threads")] #[cfg_attr( feature = "v8", diff --git a/lib/api/tests/rt.rs b/lib/api/tests/rt.rs new file mode 100644 index 00000000000..72af57f298c --- /dev/null +++ b/lib/api/tests/rt.rs @@ -0,0 +1,74 @@ +#[test] +#[cfg(all(feature = "sys", feature = "wamr", feature = "v8"))] +fn can_create_multiple_engines() { + use wasmer::{sys::Cranelift, v8::V8, wamr::Wamr, *}; + let _: Engine = Cranelift::new().into(); + + #[cfg(feature = "v8")] + { + let _: Engine = V8::new().into(); + } + + #[cfg(feature = "wamr")] + { + let _: Engine = Wamr::new().into(); + } +} + +#[test] +#[cfg(all(feature = "v8", feature = "sys"))] +fn multiple_engines_can_run_together() { + use std::u8; + use wasmer::{sys::Cranelift, v8::V8, *}; + + let clift: Engine = Cranelift::new().into(); + let mut clift_store = Store::new(clift); + let c_hello = Function::new_typed(&mut clift_store, move || { + println!("hello from cranelift!"); + }); + + #[cfg(feature = "v8")] + { + let v8: Engine = V8::new().into(); + let mut v8_store = Store::new(v8); + let v8_hello = Function::new_typed(&mut v8_store, move || { + println!("hello from v8!"); + }); + c_hello.call(&mut clift_store, &[]).unwrap(); + v8_hello.call(&mut v8_store, &[]).unwrap(); + } +} + +#[test] +#[cfg(all(feature = "sys", feature = "wamr", feature = "v8"))] +fn engine_unique_id() { + use std::collections::HashSet; + + use wasmer::{sys::Cranelift, v8::V8, wamr::Wamr, *}; + + let mut table = HashSet::new(); + + for _ in 0..100_000 { + let e: Engine = Cranelift::new().into(); + + let id = e.id(); + + assert!(!table.contains(&id)); + table.insert(e.id()); + assert!(table.contains(&id)); + + let e: Engine = V8::new().into(); + let id = e.id(); + + assert!(!table.contains(&id)); + table.insert(e.id()); + assert!(table.contains(&id)); + + let e: Engine = Wamr::new().into(); + let id = e.id(); + + assert!(!table.contains(&id)); + table.insert(e.id()); + assert!(table.contains(&id)); + } +} diff --git a/lib/api/third-party/wee8/wasm.h b/lib/api/third-party/wee8/wasm.h new file mode 100644 index 00000000000..e343c55fae4 --- /dev/null +++ b/lib/api/third-party/wee8/wasm.h @@ -0,0 +1,697 @@ +// WebAssembly C API + +#ifndef __WASM_H +#define __WASM_H + +#include +#include +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Auxiliaries + +// Machine types + +inline void assertions() { + static_assert(sizeof(float) == sizeof(uint32_t), "incompatible float type"); + static_assert(sizeof(double) == sizeof(uint64_t), "incompatible double type"); + static_assert(sizeof(intptr_t) == sizeof(uint32_t) || + sizeof(intptr_t) == sizeof(uint64_t), + "incompatible pointer type"); +} + +typedef char byte_t; +typedef float float32_t; +typedef double float64_t; + + +// Ownership + +#define own + +// The qualifier `own` is used to indicate ownership of data in this API. +// It is intended to be interpreted similar to a `const` qualifier: +// +// - `own wasm_xxx_t*` owns the pointed-to data +// - `own wasm_xxx_t` distributes to all fields of a struct or union `xxx` +// - `own wasm_xxx_vec_t` owns the vector as well as its elements(!) +// - an `own` function parameter passes ownership from caller to callee +// - an `own` function result passes ownership from callee to caller +// - an exception are `own` pointer parameters named `out`, which are copy-back +// output parameters passing back ownership from callee to caller +// +// Own data is created by `wasm_xxx_new` functions and some others. +// It must be released with the corresponding `wasm_xxx_delete` function. +// +// Deleting a reference does not necessarily delete the underlying object, +// it merely indicates that this owner no longer uses it. +// +// For vectors, `const wasm_xxx_vec_t` is used informally to indicate that +// neither the vector nor its elements should be modified. +// TODO: introduce proper `wasm_xxx_const_vec_t`? + + +#define WASM_DECLARE_OWN(name) \ + typedef struct wasm_##name##_t wasm_##name##_t; \ + \ + void wasm_##name##_delete(own wasm_##name##_t*); + + +// Vectors + +#define WASM_DECLARE_VEC(name, ptr_or_none) \ + typedef struct wasm_##name##_vec_t { \ + size_t size; \ + wasm_##name##_t ptr_or_none* data; \ + } wasm_##name##_vec_t; \ + \ + void wasm_##name##_vec_new_empty(own wasm_##name##_vec_t* out); \ + void wasm_##name##_vec_new_uninitialized( \ + own wasm_##name##_vec_t* out, size_t); \ + void wasm_##name##_vec_new( \ + own wasm_##name##_vec_t* out, \ + size_t, own wasm_##name##_t ptr_or_none const[]); \ + void wasm_##name##_vec_copy( \ + own wasm_##name##_vec_t* out, wasm_##name##_vec_t*); \ + void wasm_##name##_vec_delete(own wasm_##name##_vec_t*); + + +// Byte vectors + +typedef byte_t wasm_byte_t; +WASM_DECLARE_VEC(byte, ) + +typedef wasm_byte_vec_t wasm_name_t; + +#define wasm_name wasm_byte_vec +#define wasm_name_new wasm_byte_vec_new +#define wasm_name_new_empty wasm_byte_vec_new_empty +#define wasm_name_new_new_uninitialized wasm_byte_vec_new_uninitialized +#define wasm_name_copy wasm_byte_vec_copy +#define wasm_name_delete wasm_byte_vec_delete + +static inline void wasm_name_new_from_string( + own wasm_name_t* out, const char* s +) { + wasm_name_new(out, strlen(s) + 1, s); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Runtime Environment + +// Configuration + +WASM_DECLARE_OWN(config) + +own wasm_config_t* wasm_config_new(); + +// Embedders may provide custom functions for manipulating configs. + + +// Engine + +WASM_DECLARE_OWN(engine) + +own wasm_engine_t* wasm_engine_new(); +own wasm_engine_t* wasm_engine_new_with_config(own wasm_config_t*); + + +// Store + +WASM_DECLARE_OWN(store) + +own wasm_store_t* wasm_store_new(wasm_engine_t*); + + +/////////////////////////////////////////////////////////////////////////////// +// Type Representations + +// Type attributes + +typedef uint8_t wasm_mutability_t; +enum wasm_mutability_enum { + WASM_CONST, + WASM_VAR, +}; + +typedef struct wasm_limits_t { + uint32_t min; + uint32_t max; +} wasm_limits_t; + +static const uint32_t wasm_limits_max_default = 0xffffffff; + + +// Generic + +#define WASM_DECLARE_TYPE(name) \ + WASM_DECLARE_OWN(name) \ + WASM_DECLARE_VEC(name, *) \ + \ + own wasm_##name##_t* wasm_##name##_copy(wasm_##name##_t*); + + +// Value Types + +WASM_DECLARE_TYPE(valtype) + +typedef uint8_t wasm_valkind_t; +enum wasm_valkind_enum { + WASM_I32, + WASM_I64, + WASM_F32, + WASM_F64, + WASM_ANYREF = 128, + WASM_FUNCREF, +}; + +own wasm_valtype_t* wasm_valtype_new(wasm_valkind_t); + +wasm_valkind_t wasm_valtype_kind(const wasm_valtype_t*); + +static inline bool wasm_valkind_is_num(wasm_valkind_t k) { + return k < WASM_ANYREF; +} +static inline bool wasm_valkind_is_ref(wasm_valkind_t k) { + return k >= WASM_ANYREF; +} + +static inline bool wasm_valtype_is_num(const wasm_valtype_t* t) { + return wasm_valkind_is_num(wasm_valtype_kind(t)); +} +static inline bool wasm_valtype_is_ref(const wasm_valtype_t* t) { + return wasm_valkind_is_ref(wasm_valtype_kind(t)); +} + + +// Function Types + +WASM_DECLARE_TYPE(functype) + +own wasm_functype_t* wasm_functype_new( + own wasm_valtype_vec_t* params, own wasm_valtype_vec_t* results); + +const wasm_valtype_vec_t* wasm_functype_params(const wasm_functype_t*); +const wasm_valtype_vec_t* wasm_functype_results(const wasm_functype_t*); + + +// Global Types + +WASM_DECLARE_TYPE(globaltype) + +own wasm_globaltype_t* wasm_globaltype_new( + own wasm_valtype_t*, wasm_mutability_t); + +const wasm_valtype_t* wasm_globaltype_content(const wasm_globaltype_t*); +wasm_mutability_t wasm_globaltype_mutability(const wasm_globaltype_t*); + + +// Table Types + +WASM_DECLARE_TYPE(tabletype) + +own wasm_tabletype_t* wasm_tabletype_new( + own wasm_valtype_t*, const wasm_limits_t*); + +const wasm_valtype_t* wasm_tabletype_element(const wasm_tabletype_t*); +const wasm_limits_t* wasm_tabletype_limits(const wasm_tabletype_t*); + + +// Memory Types + +WASM_DECLARE_TYPE(memorytype) + +own wasm_memorytype_t* wasm_memorytype_new(const wasm_limits_t*); + +const wasm_limits_t* wasm_memorytype_limits(const wasm_memorytype_t*); + + +// Extern Types + +WASM_DECLARE_TYPE(externtype) + +typedef uint8_t wasm_externkind_t; +enum wasm_externkind_enum { + WASM_EXTERN_FUNC, + WASM_EXTERN_GLOBAL, + WASM_EXTERN_TABLE, + WASM_EXTERN_MEMORY, +}; + +wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t*); + +wasm_externtype_t* wasm_functype_as_externtype(wasm_functype_t*); +wasm_externtype_t* wasm_globaltype_as_externtype(wasm_globaltype_t*); +wasm_externtype_t* wasm_tabletype_as_externtype(wasm_tabletype_t*); +wasm_externtype_t* wasm_memorytype_as_externtype(wasm_memorytype_t*); + +wasm_functype_t* wasm_externtype_as_functype(wasm_externtype_t*); +wasm_globaltype_t* wasm_externtype_as_globaltype(wasm_externtype_t*); +wasm_tabletype_t* wasm_externtype_as_tabletype(wasm_externtype_t*); +wasm_memorytype_t* wasm_externtype_as_memorytype(wasm_externtype_t*); + +const wasm_externtype_t* wasm_functype_as_externtype_const(const wasm_functype_t*); +const wasm_externtype_t* wasm_globaltype_as_externtype_const(const wasm_globaltype_t*); +const wasm_externtype_t* wasm_tabletype_as_externtype_const(const wasm_tabletype_t*); +const wasm_externtype_t* wasm_memorytype_as_externtype_const(const wasm_memorytype_t*); + +const wasm_functype_t* wasm_externtype_as_functype_const(const wasm_externtype_t*); +const wasm_globaltype_t* wasm_externtype_as_globaltype_const(const wasm_externtype_t*); +const wasm_tabletype_t* wasm_externtype_as_tabletype_const(const wasm_externtype_t*); +const wasm_memorytype_t* wasm_externtype_as_memorytype_const(const wasm_externtype_t*); + + +// Import Types + +WASM_DECLARE_TYPE(importtype) + +own wasm_importtype_t* wasm_importtype_new( + own wasm_name_t* module, own wasm_name_t* name, own wasm_externtype_t*); + +const wasm_name_t* wasm_importtype_module(const wasm_importtype_t*); +const wasm_name_t* wasm_importtype_name(const wasm_importtype_t*); +const wasm_externtype_t* wasm_importtype_type(const wasm_importtype_t*); + + +// Export Types + +WASM_DECLARE_TYPE(exporttype) + +own wasm_exporttype_t* wasm_exporttype_new( + own wasm_name_t*, own wasm_externtype_t*); + +const wasm_name_t* wasm_exporttype_name(const wasm_exporttype_t*); +const wasm_externtype_t* wasm_exporttype_type(const wasm_exporttype_t*); + + +/////////////////////////////////////////////////////////////////////////////// +// Runtime Objects + +// Values + +struct wasm_ref_t; + +typedef struct wasm_val_t { + wasm_valkind_t kind; + union { + int32_t i32; + int64_t i64; + float32_t f32; + float64_t f64; + struct wasm_ref_t* ref; + } of; +} wasm_val_t; + +void wasm_val_delete(own wasm_val_t* v); +void wasm_val_copy(own wasm_val_t* out, const wasm_val_t*); + +WASM_DECLARE_VEC(val, ) + + +// References + +#define WASM_DECLARE_REF_BASE(name) \ + WASM_DECLARE_OWN(name) \ + \ + own wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t*); \ + bool wasm_##name##_same(const wasm_##name##_t*, const wasm_##name##_t*); \ + \ + void* wasm_##name##_get_host_info(const wasm_##name##_t*); \ + void wasm_##name##_set_host_info(wasm_##name##_t*, void*); \ + void wasm_##name##_set_host_info_with_finalizer(wasm_##name##_t*, void*, \ + void (*)(void*)); + +#define WASM_DECLARE_REF(name) \ + WASM_DECLARE_REF_BASE(name) \ + \ + wasm_ref_t* wasm_##name##_as_ref(wasm_##name##_t*); \ + wasm_##name##_t* wasm_ref_as_##name(wasm_ref_t*); \ + const wasm_ref_t* wasm_##name##_as_ref_const(const wasm_##name##_t*); \ + const wasm_##name##_t* wasm_ref_as_##name##_const(const wasm_ref_t*); + +#define WASM_DECLARE_SHARABLE_REF(name) \ + WASM_DECLARE_REF(name) \ + WASM_DECLARE_OWN(shared_##name) \ + \ + own wasm_shared_##name##_t* wasm_##name##_share(const wasm_##name##_t*); \ + own wasm_##name##_t* wasm_##name##_obtain(wasm_store_t*, const wasm_shared_##name##_t*); + + +WASM_DECLARE_REF_BASE(ref) + + +// Frames + +WASM_DECLARE_OWN(frame) +WASM_DECLARE_VEC(frame, *) +own wasm_frame_t* wasm_frame_copy(const wasm_frame_t*); + +struct wasm_instance_t* wasm_frame_instance(const wasm_frame_t*); +uint32_t wasm_frame_func_index(const wasm_frame_t*); +size_t wasm_frame_func_offset(const wasm_frame_t*); +size_t wasm_frame_module_offset(const wasm_frame_t*); + + +// Traps + +typedef wasm_name_t wasm_message_t; // null terminated + +WASM_DECLARE_REF(trap) + +own wasm_trap_t* wasm_trap_new(wasm_store_t* store, const wasm_message_t*); + +void wasm_trap_message(const wasm_trap_t*, own wasm_message_t* out); +own wasm_frame_t* wasm_trap_origin(const wasm_trap_t*); +void wasm_trap_trace(const wasm_trap_t*, own wasm_frame_vec_t* out); + + +// Foreign Objects + +WASM_DECLARE_REF(foreign) + +own wasm_foreign_t* wasm_foreign_new(wasm_store_t*); + + +// Modules + +WASM_DECLARE_SHARABLE_REF(module) + +own wasm_module_t* wasm_module_new( + wasm_store_t*, const wasm_byte_vec_t* binary); + +bool wasm_module_validate(wasm_store_t*, const wasm_byte_vec_t* binary); + +void wasm_module_imports(const wasm_module_t*, own wasm_importtype_vec_t* out); +void wasm_module_exports(const wasm_module_t*, own wasm_exporttype_vec_t* out); + +void wasm_module_serialize(const wasm_module_t*, own wasm_byte_vec_t* out); +own wasm_module_t* wasm_module_deserialize(wasm_store_t*, const wasm_byte_vec_t*); + + +// Function Instances + +WASM_DECLARE_REF(func) + +typedef own wasm_trap_t* (*wasm_func_callback_t)( + const wasm_val_t args[], wasm_val_t results[]); +typedef own wasm_trap_t* (*wasm_func_callback_with_env_t)( + void* env, const wasm_val_t args[], wasm_val_t results[]); + +own wasm_func_t* wasm_func_new( + wasm_store_t*, const wasm_functype_t*, wasm_func_callback_t); +own wasm_func_t* wasm_func_new_with_env( + wasm_store_t*, const wasm_functype_t* type, wasm_func_callback_with_env_t, + void* env, void (*finalizer)(void*)); + +own wasm_functype_t* wasm_func_type(const wasm_func_t*); +size_t wasm_func_param_arity(const wasm_func_t*); +size_t wasm_func_result_arity(const wasm_func_t*); + +own wasm_trap_t* wasm_func_call( + const wasm_func_t*, const wasm_val_t args[], wasm_val_t results[]); + + +// Global Instances + +WASM_DECLARE_REF(global) + +own wasm_global_t* wasm_global_new( + wasm_store_t*, const wasm_globaltype_t*, const wasm_val_t*); + +own wasm_globaltype_t* wasm_global_type(const wasm_global_t*); + +void wasm_global_get(const wasm_global_t*, own wasm_val_t* out); +void wasm_global_set(wasm_global_t*, const wasm_val_t*); + + +// Table Instances + +WASM_DECLARE_REF(table) + +typedef uint32_t wasm_table_size_t; + +own wasm_table_t* wasm_table_new( + wasm_store_t*, const wasm_tabletype_t*, wasm_ref_t* init); + +own wasm_tabletype_t* wasm_table_type(const wasm_table_t*); + +own wasm_ref_t* wasm_table_get(const wasm_table_t*, wasm_table_size_t index); +bool wasm_table_set(wasm_table_t*, wasm_table_size_t index, wasm_ref_t*); + +wasm_table_size_t wasm_table_size(const wasm_table_t*); +bool wasm_table_grow(wasm_table_t*, wasm_table_size_t delta, wasm_ref_t* init); + + +// Memory Instances + +WASM_DECLARE_REF(memory) + +typedef uint32_t wasm_memory_pages_t; + +static const size_t MEMORY_PAGE_SIZE = 0x10000; + +own wasm_memory_t* wasm_memory_new(wasm_store_t*, const wasm_memorytype_t*); + +own wasm_memorytype_t* wasm_memory_type(const wasm_memory_t*); + +byte_t* wasm_memory_data(wasm_memory_t*); +size_t wasm_memory_data_size(const wasm_memory_t*); + +wasm_memory_pages_t wasm_memory_size(const wasm_memory_t*); +bool wasm_memory_grow(wasm_memory_t*, wasm_memory_pages_t delta); + + +// Externals + +WASM_DECLARE_REF(extern) +WASM_DECLARE_VEC(extern, *) + +wasm_externkind_t wasm_extern_kind(const wasm_extern_t*); +own wasm_externtype_t* wasm_extern_type(const wasm_extern_t*); + +wasm_extern_t* wasm_func_as_extern(wasm_func_t*); +wasm_extern_t* wasm_global_as_extern(wasm_global_t*); +wasm_extern_t* wasm_table_as_extern(wasm_table_t*); +wasm_extern_t* wasm_memory_as_extern(wasm_memory_t*); + +wasm_func_t* wasm_extern_as_func(wasm_extern_t*); +wasm_global_t* wasm_extern_as_global(wasm_extern_t*); +wasm_table_t* wasm_extern_as_table(wasm_extern_t*); +wasm_memory_t* wasm_extern_as_memory(wasm_extern_t*); + +const wasm_extern_t* wasm_func_as_extern_const(const wasm_func_t*); +const wasm_extern_t* wasm_global_as_extern_const(const wasm_global_t*); +const wasm_extern_t* wasm_table_as_extern_const(const wasm_table_t*); +const wasm_extern_t* wasm_memory_as_extern_const(const wasm_memory_t*); + +const wasm_func_t* wasm_extern_as_func_const(const wasm_extern_t*); +const wasm_global_t* wasm_extern_as_global_const(const wasm_extern_t*); +const wasm_table_t* wasm_extern_as_table_const(const wasm_extern_t*); +const wasm_memory_t* wasm_extern_as_memory_const(const wasm_extern_t*); + + +// Module Instances + +WASM_DECLARE_REF(instance) + +own wasm_instance_t* wasm_instance_new( + wasm_store_t*, const wasm_module_t*, const wasm_extern_t* const imports[], + own wasm_trap_t** +); + +void wasm_instance_exports(const wasm_instance_t*, own wasm_extern_vec_t* out); + + +/////////////////////////////////////////////////////////////////////////////// +// Convenience + +// Value Type construction short-hands + +static inline own wasm_valtype_t* wasm_valtype_new_i32() { + return wasm_valtype_new(WASM_I32); +} +static inline own wasm_valtype_t* wasm_valtype_new_i64() { + return wasm_valtype_new(WASM_I64); +} +static inline own wasm_valtype_t* wasm_valtype_new_f32() { + return wasm_valtype_new(WASM_F32); +} +static inline own wasm_valtype_t* wasm_valtype_new_f64() { + return wasm_valtype_new(WASM_F64); +} + +static inline own wasm_valtype_t* wasm_valtype_new_anyref() { + return wasm_valtype_new(WASM_ANYREF); +} +static inline own wasm_valtype_t* wasm_valtype_new_funcref() { + return wasm_valtype_new(WASM_FUNCREF); +} + + +// Function Types construction short-hands + +static inline own wasm_functype_t* wasm_functype_new_0_0() { + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new_empty(¶ms); + wasm_valtype_vec_new_empty(&results); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_1_0( + own wasm_valtype_t* p +) { + wasm_valtype_t* ps[1] = {p}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 1, ps); + wasm_valtype_vec_new_empty(&results); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_2_0( + own wasm_valtype_t* p1, own wasm_valtype_t* p2 +) { + wasm_valtype_t* ps[2] = {p1, p2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 2, ps); + wasm_valtype_vec_new_empty(&results); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_3_0( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3 +) { + wasm_valtype_t* ps[3] = {p1, p2, p3}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 3, ps); + wasm_valtype_vec_new_empty(&results); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_0_1( + own wasm_valtype_t* r +) { + wasm_valtype_t* rs[1] = {r}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new_empty(¶ms); + wasm_valtype_vec_new(&results, 1, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_1_1( + own wasm_valtype_t* p, own wasm_valtype_t* r +) { + wasm_valtype_t* ps[1] = {p}; + wasm_valtype_t* rs[1] = {r}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 1, ps); + wasm_valtype_vec_new(&results, 1, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_2_1( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* r +) { + wasm_valtype_t* ps[2] = {p1, p2}; + wasm_valtype_t* rs[1] = {r}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 2, ps); + wasm_valtype_vec_new(&results, 1, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_3_1( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3, + own wasm_valtype_t* r +) { + wasm_valtype_t* ps[3] = {p1, p2, p3}; + wasm_valtype_t* rs[1] = {r}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 3, ps); + wasm_valtype_vec_new(&results, 1, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_0_2( + own wasm_valtype_t* r1, own wasm_valtype_t* r2 +) { + wasm_valtype_t* rs[2] = {r1, r2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new_empty(¶ms); + wasm_valtype_vec_new(&results, 2, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_1_2( + own wasm_valtype_t* p, own wasm_valtype_t* r1, own wasm_valtype_t* r2 +) { + wasm_valtype_t* ps[1] = {p}; + wasm_valtype_t* rs[2] = {r1, r2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 1, ps); + wasm_valtype_vec_new(&results, 2, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_2_2( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, + own wasm_valtype_t* r1, own wasm_valtype_t* r2 +) { + wasm_valtype_t* ps[2] = {p1, p2}; + wasm_valtype_t* rs[2] = {r1, r2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 2, ps); + wasm_valtype_vec_new(&results, 2, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_3_2( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3, + own wasm_valtype_t* r1, own wasm_valtype_t* r2 +) { + wasm_valtype_t* ps[3] = {p1, p2, p3}; + wasm_valtype_t* rs[2] = {r1, r2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 3, ps); + wasm_valtype_vec_new(&results, 2, rs); + return wasm_functype_new(¶ms, &results); +} + + +// Value construction short-hands + +static inline void wasm_val_init_ptr(own wasm_val_t* out, void* p) { +#if UINTPTR_MAX == UINT32_MAX + out->kind = WASM_I32; + out->of.i32 = (intptr_t)p; +#elif UINTPTR_MAX == UINT64_MAX + out->kind = WASM_I64; + out->of.i64 = (intptr_t)p; +#endif +} + +static inline void* wasm_val_ptr(const wasm_val_t* val) { +#if UINTPTR_MAX == UINT32_MAX + return (void*)(intptr_t)val->of.i32; +#elif UINTPTR_MAX == UINT64_MAX + return (void*)(intptr_t)val->of.i64; +#endif +} + + +/////////////////////////////////////////////////////////////////////////////// + +#undef own + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // #ifdef __WASM_H diff --git a/lib/api/third_party/.gitignore b/lib/api/third_party/.gitignore deleted file mode 100644 index b2e7e790c55..00000000000 --- a/lib/api/third_party/.gitignore +++ /dev/null @@ -1 +0,0 @@ -wamr diff --git a/lib/api/third_party/v8/wasm.h b/lib/api/third_party/v8/wasm.h deleted file mode 100644 index 971d413bc78..00000000000 --- a/lib/api/third_party/v8/wasm.h +++ /dev/null @@ -1,664 +0,0 @@ -// WebAssembly C API - -#ifndef __WASM_H -#define __WASM_H - -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Auxiliaries - -// Machine types - -inline void assertions() { - static_assert(sizeof(float) == sizeof(uint32_t), "incompatible float type"); - static_assert(sizeof(double) == sizeof(uint64_t), "incompatible double type"); - static_assert(sizeof(intptr_t) == sizeof(uint32_t) || - sizeof(intptr_t) == sizeof(uint64_t), - "incompatible pointer type"); -} - -typedef char byte_t; -typedef float float32_t; -typedef double float64_t; - -// Ownership - -#define own - -// The qualifier `own` is used to indicate ownership of data in this API. -// It is intended to be interpreted similar to a `const` qualifier: -// -// - `own wasm_xxx_t*` owns the pointed-to data -// - `own wasm_xxx_t` distributes to all fields of a struct or union `xxx` -// - `own wasm_xxx_vec_t` owns the vector as well as its elements(!) -// - an `own` function parameter passes ownership from caller to callee -// - an `own` function result passes ownership from callee to caller -// - an exception are `own` pointer parameters named `out`, which are copy-back -// output parameters passing back ownership from callee to caller -// -// Own data is created by `wasm_xxx_new` functions and some others. -// It must be released with the corresponding `wasm_xxx_delete` function. -// -// Deleting a reference does not necessarily delete the underlying object, -// it merely indicates that this owner no longer uses it. -// -// For vectors, `const wasm_xxx_vec_t` is used informally to indicate that -// neither the vector nor its elements should be modified. -// TODO: introduce proper `wasm_xxx_const_vec_t`? - -#define WASM_DECLARE_OWN(name) \ - typedef struct wasm_##name##_t wasm_##name##_t; \ - \ - void wasm_##name##_delete(own wasm_##name##_t *); - -// Vectors - -#define WASM_DECLARE_VEC(name, ptr_or_none) \ - typedef struct wasm_##name##_vec_t { \ - size_t size; \ - wasm_##name##_t ptr_or_none *data; \ - } wasm_##name##_vec_t; \ - \ - void wasm_##name##_vec_new_empty(own wasm_##name##_vec_t *out); \ - void wasm_##name##_vec_new_uninitialized(own wasm_##name##_vec_t *out, \ - size_t); \ - void wasm_##name##_vec_new(own wasm_##name##_vec_t *out, size_t, \ - own wasm_##name##_t ptr_or_none const[]); \ - void wasm_##name##_vec_copy(own wasm_##name##_vec_t *out, \ - wasm_##name##_vec_t *); \ - void wasm_##name##_vec_delete(own wasm_##name##_vec_t *); - -// Byte vectors - -typedef byte_t wasm_byte_t; -WASM_DECLARE_VEC(byte, ) - -typedef wasm_byte_vec_t wasm_name_t; - -#define wasm_name wasm_byte_vec -#define wasm_name_new wasm_byte_vec_new -#define wasm_name_new_empty wasm_byte_vec_new_empty -#define wasm_name_new_new_uninitialized wasm_byte_vec_new_uninitialized -#define wasm_name_copy wasm_byte_vec_copy -#define wasm_name_delete wasm_byte_vec_delete - -static inline void wasm_name_new_from_string(own wasm_name_t *out, - const char *s) { - wasm_name_new(out, strlen(s) + 1, s); -} - -/////////////////////////////////////////////////////////////////////////////// -// Runtime Environment - -// Configuration - -WASM_DECLARE_OWN(config) - -own wasm_config_t *wasm_config_new(); - -// Embedders may provide custom functions for manipulating configs. - -// Engine - -WASM_DECLARE_OWN(engine) - -own wasm_engine_t *wasm_engine_new(); -own wasm_engine_t *wasm_engine_new_with_config(own wasm_config_t *); - -// Store - -WASM_DECLARE_OWN(store) - -own wasm_store_t *wasm_store_new(wasm_engine_t *); - -/////////////////////////////////////////////////////////////////////////////// -// Type Representations - -// Type attributes - -typedef uint8_t wasm_mutability_t; -enum wasm_mutability_enum { - WASM_CONST, - WASM_VAR, -}; - -typedef struct wasm_limits_t { - uint32_t min; - uint32_t max; -} wasm_limits_t; - -static const uint32_t wasm_limits_max_default = 0xffffffff; - -// Generic - -#define WASM_DECLARE_TYPE(name) \ - WASM_DECLARE_OWN(name) \ - WASM_DECLARE_VEC(name, *) \ - \ - own wasm_##name##_t *wasm_##name##_copy(wasm_##name##_t *); - -// Value Types - -WASM_DECLARE_TYPE(valtype) - -typedef uint8_t wasm_valkind_t; -enum wasm_valkind_enum { - WASM_I32, - WASM_I64, - WASM_F32, - WASM_F64, - WASM_ANYREF = 128, - WASM_FUNCREF, -}; - -own wasm_valtype_t *wasm_valtype_new(wasm_valkind_t); - -wasm_valkind_t wasm_valtype_kind(const wasm_valtype_t *); - -static inline bool wasm_valkind_is_num(wasm_valkind_t k) { - return k < WASM_ANYREF; -} -static inline bool wasm_valkind_is_ref(wasm_valkind_t k) { - return k >= WASM_ANYREF; -} - -static inline bool wasm_valtype_is_num(const wasm_valtype_t *t) { - return wasm_valkind_is_num(wasm_valtype_kind(t)); -} -static inline bool wasm_valtype_is_ref(const wasm_valtype_t *t) { - return wasm_valkind_is_ref(wasm_valtype_kind(t)); -} - -// Function Types - -WASM_DECLARE_TYPE(functype) - -own wasm_functype_t *wasm_functype_new(own wasm_valtype_vec_t *params, - own wasm_valtype_vec_t *results); - -const wasm_valtype_vec_t *wasm_functype_params(const wasm_functype_t *); -const wasm_valtype_vec_t *wasm_functype_results(const wasm_functype_t *); - -// Global Types - -WASM_DECLARE_TYPE(globaltype) - -own wasm_globaltype_t *wasm_globaltype_new(own wasm_valtype_t *, - wasm_mutability_t); - -const wasm_valtype_t *wasm_globaltype_content(const wasm_globaltype_t *); -wasm_mutability_t wasm_globaltype_mutability(const wasm_globaltype_t *); - -// Table Types - -WASM_DECLARE_TYPE(tabletype) - -own wasm_tabletype_t *wasm_tabletype_new(own wasm_valtype_t *, - const wasm_limits_t *); - -const wasm_valtype_t *wasm_tabletype_element(const wasm_tabletype_t *); -const wasm_limits_t *wasm_tabletype_limits(const wasm_tabletype_t *); - -// Memory Types - -WASM_DECLARE_TYPE(memorytype) - -own wasm_memorytype_t *wasm_memorytype_new(const wasm_limits_t *); - -const wasm_limits_t *wasm_memorytype_limits(const wasm_memorytype_t *); - -// Extern Types - -WASM_DECLARE_TYPE(externtype) - -typedef uint8_t wasm_externkind_t; -enum wasm_externkind_enum { - WASM_EXTERN_FUNC, - WASM_EXTERN_GLOBAL, - WASM_EXTERN_TABLE, - WASM_EXTERN_MEMORY, -}; - -wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t *); - -wasm_externtype_t *wasm_functype_as_externtype(wasm_functype_t *); -wasm_externtype_t *wasm_globaltype_as_externtype(wasm_globaltype_t *); -wasm_externtype_t *wasm_tabletype_as_externtype(wasm_tabletype_t *); -wasm_externtype_t *wasm_memorytype_as_externtype(wasm_memorytype_t *); - -wasm_functype_t *wasm_externtype_as_functype(wasm_externtype_t *); -wasm_globaltype_t *wasm_externtype_as_globaltype(wasm_externtype_t *); -wasm_tabletype_t *wasm_externtype_as_tabletype(wasm_externtype_t *); -wasm_memorytype_t *wasm_externtype_as_memorytype(wasm_externtype_t *); - -const wasm_externtype_t * -wasm_functype_as_externtype_const(const wasm_functype_t *); -const wasm_externtype_t * -wasm_globaltype_as_externtype_const(const wasm_globaltype_t *); -const wasm_externtype_t * -wasm_tabletype_as_externtype_const(const wasm_tabletype_t *); -const wasm_externtype_t * -wasm_memorytype_as_externtype_const(const wasm_memorytype_t *); - -const wasm_functype_t * -wasm_externtype_as_functype_const(const wasm_externtype_t *); -const wasm_globaltype_t * -wasm_externtype_as_globaltype_const(const wasm_externtype_t *); -const wasm_tabletype_t * -wasm_externtype_as_tabletype_const(const wasm_externtype_t *); -const wasm_memorytype_t * -wasm_externtype_as_memorytype_const(const wasm_externtype_t *); - -// Import Types - -WASM_DECLARE_TYPE(importtype) - -own wasm_importtype_t *wasm_importtype_new(own wasm_name_t *module, - own wasm_name_t *name, - own wasm_externtype_t *); - -const wasm_name_t *wasm_importtype_module(const wasm_importtype_t *); -const wasm_name_t *wasm_importtype_name(const wasm_importtype_t *); -const wasm_externtype_t *wasm_importtype_type(const wasm_importtype_t *); - -// Export Types - -WASM_DECLARE_TYPE(exporttype) - -own wasm_exporttype_t *wasm_exporttype_new(own wasm_name_t *, - own wasm_externtype_t *); - -const wasm_name_t *wasm_exporttype_name(const wasm_exporttype_t *); -const wasm_externtype_t *wasm_exporttype_type(const wasm_exporttype_t *); - -/////////////////////////////////////////////////////////////////////////////// -// Runtime Objects - -// Values - -struct wasm_ref_t; - -typedef struct wasm_val_t { - wasm_valkind_t kind; - union { - int32_t i32; - int64_t i64; - float32_t f32; - float64_t f64; - struct wasm_ref_t *ref; - } of; -} wasm_val_t; - -void wasm_val_delete(own wasm_val_t *v); -void wasm_val_copy(own wasm_val_t *out, const wasm_val_t *); - -WASM_DECLARE_VEC(val, ) - -// References - -#define WASM_DECLARE_REF_BASE(name) \ - WASM_DECLARE_OWN(name) \ - \ - own wasm_##name##_t *wasm_##name##_copy(const wasm_##name##_t *); \ - bool wasm_##name##_same(const wasm_##name##_t *, const wasm_##name##_t *); \ - \ - void *wasm_##name##_get_host_info(const wasm_##name##_t *); \ - void wasm_##name##_set_host_info(wasm_##name##_t *, void *); \ - void wasm_##name##_set_host_info_with_finalizer(wasm_##name##_t *, void *, \ - void (*)(void *)); - -#define WASM_DECLARE_REF(name) \ - WASM_DECLARE_REF_BASE(name) \ - \ - wasm_ref_t *wasm_##name##_as_ref(wasm_##name##_t *); \ - wasm_##name##_t *wasm_ref_as_##name(wasm_ref_t *); \ - const wasm_ref_t *wasm_##name##_as_ref_const(const wasm_##name##_t *); \ - const wasm_##name##_t *wasm_ref_as_##name##_const(const wasm_ref_t *); - -#define WASM_DECLARE_SHARABLE_REF(name) \ - WASM_DECLARE_REF(name) \ - WASM_DECLARE_OWN(shared_##name) \ - \ - own wasm_shared_##name##_t *wasm_##name##_share(const wasm_##name##_t *); \ - own wasm_##name##_t *wasm_##name##_obtain(wasm_store_t *, \ - const wasm_shared_##name##_t *); - -WASM_DECLARE_REF_BASE(ref) - -// Frames - -WASM_DECLARE_OWN(frame) -WASM_DECLARE_VEC(frame, *) -own wasm_frame_t *wasm_frame_copy(const wasm_frame_t *); - -struct wasm_instance_t *wasm_frame_instance(const wasm_frame_t *); -uint32_t wasm_frame_func_index(const wasm_frame_t *); -size_t wasm_frame_func_offset(const wasm_frame_t *); -size_t wasm_frame_module_offset(const wasm_frame_t *); - -// Traps - -typedef wasm_name_t wasm_message_t; // null terminated - -WASM_DECLARE_REF(trap) - -own wasm_trap_t *wasm_trap_new(wasm_store_t *store, const wasm_message_t *); - -void wasm_trap_message(const wasm_trap_t *, own wasm_message_t *out); -own wasm_frame_t *wasm_trap_origin(const wasm_trap_t *); -void wasm_trap_trace(const wasm_trap_t *, own wasm_frame_vec_t *out); - -// Foreign Objects - -WASM_DECLARE_REF(foreign) - -own wasm_foreign_t *wasm_foreign_new(wasm_store_t *); - -// Modules - -WASM_DECLARE_SHARABLE_REF(module) - -own wasm_module_t *wasm_module_new(wasm_store_t *, - const wasm_byte_vec_t *binary); - -bool wasm_module_validate(wasm_store_t *, const wasm_byte_vec_t *binary); - -void wasm_module_imports(const wasm_module_t *, own wasm_importtype_vec_t *out); -void wasm_module_exports(const wasm_module_t *, own wasm_exporttype_vec_t *out); - -void wasm_module_serialize(const wasm_module_t *, own wasm_byte_vec_t *out); -own wasm_module_t *wasm_module_deserialize(wasm_store_t *, - const wasm_byte_vec_t *); - -// Function Instances - -WASM_DECLARE_REF(func) - -typedef own wasm_trap_t *(*wasm_func_callback_t)(const wasm_val_t args[], - wasm_val_t results[]); -typedef own wasm_trap_t *(*wasm_func_callback_with_env_t)( - void *env, const wasm_val_t args[], wasm_val_t results[]); - -own wasm_func_t *wasm_func_new(wasm_store_t *, const wasm_functype_t *, - wasm_func_callback_t); -own wasm_func_t *wasm_func_new_with_env(wasm_store_t *, - const wasm_functype_t *type, - wasm_func_callback_with_env_t, - void *env, void (*finalizer)(void *)); - -own wasm_functype_t *wasm_func_type(const wasm_func_t *); -size_t wasm_func_param_arity(const wasm_func_t *); -size_t wasm_func_result_arity(const wasm_func_t *); - -own wasm_trap_t *wasm_func_call(const wasm_func_t *, const wasm_val_t args[], - wasm_val_t results[]); - -// Global Instances - -WASM_DECLARE_REF(global) - -own wasm_global_t *wasm_global_new(wasm_store_t *, const wasm_globaltype_t *, - const wasm_val_t *); - -own wasm_globaltype_t *wasm_global_type(const wasm_global_t *); - -void wasm_global_get(const wasm_global_t *, own wasm_val_t *out); -void wasm_global_set(wasm_global_t *, const wasm_val_t *); - -// Table Instances - -WASM_DECLARE_REF(table) - -typedef uint32_t wasm_table_size_t; - -own wasm_table_t *wasm_table_new(wasm_store_t *, const wasm_tabletype_t *, - wasm_ref_t *init); - -own wasm_tabletype_t *wasm_table_type(const wasm_table_t *); - -own wasm_ref_t *wasm_table_get(const wasm_table_t *, wasm_table_size_t index); -bool wasm_table_set(wasm_table_t *, wasm_table_size_t index, wasm_ref_t *); - -wasm_table_size_t wasm_table_size(const wasm_table_t *); -bool wasm_table_grow(wasm_table_t *, wasm_table_size_t delta, wasm_ref_t *init); - -// Memory Instances - -WASM_DECLARE_REF(memory) - -typedef uint32_t wasm_memory_pages_t; - -static const size_t MEMORY_PAGE_SIZE = 0x10000; - -own wasm_memory_t *wasm_memory_new(wasm_store_t *, const wasm_memorytype_t *); - -own wasm_memorytype_t *wasm_memory_type(const wasm_memory_t *); - -byte_t *wasm_memory_data(wasm_memory_t *); -size_t wasm_memory_data_size(const wasm_memory_t *); - -wasm_memory_pages_t wasm_memory_size(const wasm_memory_t *); -bool wasm_memory_grow(wasm_memory_t *, wasm_memory_pages_t delta); - -// Externals - -WASM_DECLARE_REF(extern) -WASM_DECLARE_VEC(extern, *) - -wasm_externkind_t wasm_extern_kind(const wasm_extern_t *); -own wasm_externtype_t *wasm_extern_type(const wasm_extern_t *); - -wasm_extern_t *wasm_func_as_extern(wasm_func_t *); -wasm_extern_t *wasm_global_as_extern(wasm_global_t *); -wasm_extern_t *wasm_table_as_extern(wasm_table_t *); -wasm_extern_t *wasm_memory_as_extern(wasm_memory_t *); - -wasm_func_t *wasm_extern_as_func(wasm_extern_t *); -wasm_global_t *wasm_extern_as_global(wasm_extern_t *); -wasm_table_t *wasm_extern_as_table(wasm_extern_t *); -wasm_memory_t *wasm_extern_as_memory(wasm_extern_t *); - -const wasm_extern_t *wasm_func_as_extern_const(const wasm_func_t *); -const wasm_extern_t *wasm_global_as_extern_const(const wasm_global_t *); -const wasm_extern_t *wasm_table_as_extern_const(const wasm_table_t *); -const wasm_extern_t *wasm_memory_as_extern_const(const wasm_memory_t *); - -const wasm_func_t *wasm_extern_as_func_const(const wasm_extern_t *); -const wasm_global_t *wasm_extern_as_global_const(const wasm_extern_t *); -const wasm_table_t *wasm_extern_as_table_const(const wasm_extern_t *); -const wasm_memory_t *wasm_extern_as_memory_const(const wasm_extern_t *); - -// Module Instances - -WASM_DECLARE_REF(instance) - -own wasm_instance_t *wasm_instance_new(wasm_store_t *, const wasm_module_t *, - const wasm_extern_t *const imports[], - own wasm_trap_t **); - -void wasm_instance_exports(const wasm_instance_t *, own wasm_extern_vec_t *out); - -/////////////////////////////////////////////////////////////////////////////// -// Convenience - -// Value Type construction short-hands - -static inline own wasm_valtype_t *wasm_valtype_new_i32() { - return wasm_valtype_new(WASM_I32); -} -static inline own wasm_valtype_t *wasm_valtype_new_i64() { - return wasm_valtype_new(WASM_I64); -} -static inline own wasm_valtype_t *wasm_valtype_new_f32() { - return wasm_valtype_new(WASM_F32); -} -static inline own wasm_valtype_t *wasm_valtype_new_f64() { - return wasm_valtype_new(WASM_F64); -} - -static inline own wasm_valtype_t *wasm_valtype_new_anyref() { - return wasm_valtype_new(WASM_ANYREF); -} -static inline own wasm_valtype_t *wasm_valtype_new_funcref() { - return wasm_valtype_new(WASM_FUNCREF); -} - -// Function Types construction short-hands - -static inline own wasm_functype_t *wasm_functype_new_0_0() { - wasm_valtype_vec_t params, results; - wasm_valtype_vec_new_empty(¶ms); - wasm_valtype_vec_new_empty(&results); - return wasm_functype_new(¶ms, &results); -} - -static inline own wasm_functype_t * -wasm_functype_new_1_0(own wasm_valtype_t *p) { - wasm_valtype_t *ps[1] = {p}; - wasm_valtype_vec_t params, results; - wasm_valtype_vec_new(¶ms, 1, ps); - wasm_valtype_vec_new_empty(&results); - return wasm_functype_new(¶ms, &results); -} - -static inline own wasm_functype_t * -wasm_functype_new_2_0(own wasm_valtype_t *p1, own wasm_valtype_t *p2) { - wasm_valtype_t *ps[2] = {p1, p2}; - wasm_valtype_vec_t params, results; - wasm_valtype_vec_new(¶ms, 2, ps); - wasm_valtype_vec_new_empty(&results); - return wasm_functype_new(¶ms, &results); -} - -static inline own wasm_functype_t * -wasm_functype_new_3_0(own wasm_valtype_t *p1, own wasm_valtype_t *p2, - own wasm_valtype_t *p3) { - wasm_valtype_t *ps[3] = {p1, p2, p3}; - wasm_valtype_vec_t params, results; - wasm_valtype_vec_new(¶ms, 3, ps); - wasm_valtype_vec_new_empty(&results); - return wasm_functype_new(¶ms, &results); -} - -static inline own wasm_functype_t * -wasm_functype_new_0_1(own wasm_valtype_t *r) { - wasm_valtype_t *rs[1] = {r}; - wasm_valtype_vec_t params, results; - wasm_valtype_vec_new_empty(¶ms); - wasm_valtype_vec_new(&results, 1, rs); - return wasm_functype_new(¶ms, &results); -} - -static inline own wasm_functype_t * -wasm_functype_new_1_1(own wasm_valtype_t *p, own wasm_valtype_t *r) { - wasm_valtype_t *ps[1] = {p}; - wasm_valtype_t *rs[1] = {r}; - wasm_valtype_vec_t params, results; - wasm_valtype_vec_new(¶ms, 1, ps); - wasm_valtype_vec_new(&results, 1, rs); - return wasm_functype_new(¶ms, &results); -} - -static inline own wasm_functype_t * -wasm_functype_new_2_1(own wasm_valtype_t *p1, own wasm_valtype_t *p2, - own wasm_valtype_t *r) { - wasm_valtype_t *ps[2] = {p1, p2}; - wasm_valtype_t *rs[1] = {r}; - wasm_valtype_vec_t params, results; - wasm_valtype_vec_new(¶ms, 2, ps); - wasm_valtype_vec_new(&results, 1, rs); - return wasm_functype_new(¶ms, &results); -} - -static inline own wasm_functype_t * -wasm_functype_new_3_1(own wasm_valtype_t *p1, own wasm_valtype_t *p2, - own wasm_valtype_t *p3, own wasm_valtype_t *r) { - wasm_valtype_t *ps[3] = {p1, p2, p3}; - wasm_valtype_t *rs[1] = {r}; - wasm_valtype_vec_t params, results; - wasm_valtype_vec_new(¶ms, 3, ps); - wasm_valtype_vec_new(&results, 1, rs); - return wasm_functype_new(¶ms, &results); -} - -static inline own wasm_functype_t * -wasm_functype_new_0_2(own wasm_valtype_t *r1, own wasm_valtype_t *r2) { - wasm_valtype_t *rs[2] = {r1, r2}; - wasm_valtype_vec_t params, results; - wasm_valtype_vec_new_empty(¶ms); - wasm_valtype_vec_new(&results, 2, rs); - return wasm_functype_new(¶ms, &results); -} - -static inline own wasm_functype_t * -wasm_functype_new_1_2(own wasm_valtype_t *p, own wasm_valtype_t *r1, - own wasm_valtype_t *r2) { - wasm_valtype_t *ps[1] = {p}; - wasm_valtype_t *rs[2] = {r1, r2}; - wasm_valtype_vec_t params, results; - wasm_valtype_vec_new(¶ms, 1, ps); - wasm_valtype_vec_new(&results, 2, rs); - return wasm_functype_new(¶ms, &results); -} - -static inline own wasm_functype_t * -wasm_functype_new_2_2(own wasm_valtype_t *p1, own wasm_valtype_t *p2, - own wasm_valtype_t *r1, own wasm_valtype_t *r2) { - wasm_valtype_t *ps[2] = {p1, p2}; - wasm_valtype_t *rs[2] = {r1, r2}; - wasm_valtype_vec_t params, results; - wasm_valtype_vec_new(¶ms, 2, ps); - wasm_valtype_vec_new(&results, 2, rs); - return wasm_functype_new(¶ms, &results); -} - -static inline own wasm_functype_t * -wasm_functype_new_3_2(own wasm_valtype_t *p1, own wasm_valtype_t *p2, - own wasm_valtype_t *p3, own wasm_valtype_t *r1, - own wasm_valtype_t *r2) { - wasm_valtype_t *ps[3] = {p1, p2, p3}; - wasm_valtype_t *rs[2] = {r1, r2}; - wasm_valtype_vec_t params, results; - wasm_valtype_vec_new(¶ms, 3, ps); - wasm_valtype_vec_new(&results, 2, rs); - return wasm_functype_new(¶ms, &results); -} - -// Value construction short-hands - -static inline void wasm_val_init_ptr(own wasm_val_t *out, void *p) { -#if UINTPTR_MAX == UINT32_MAX - out->kind = WASM_I32; - out->of.i32 = (intptr_t)p; -#elif UINTPTR_MAX == UINT64_MAX - out->kind = WASM_I64; - out->of.i64 = (intptr_t)p; -#endif -} - -static inline void *wasm_val_ptr(const wasm_val_t *val) { -#if UINTPTR_MAX == UINT32_MAX - return (void *)(intptr_t)val->of.i32; -#elif UINTPTR_MAX == UINT64_MAX - return (void *)(intptr_t)val->of.i64; -#endif -} - -/////////////////////////////////////////////////////////////////////////////// - -#undef own - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // #ifdef __WASM_H diff --git a/lib/c-api/Cargo.toml b/lib/c-api/Cargo.toml index c8252b12a9b..abda696ce2e 100644 --- a/lib/c-api/Cargo.toml +++ b/lib/c-api/Cargo.toml @@ -31,9 +31,9 @@ wasmer-compiler-llvm = { version = "=5.1.0", path = "../compiler-llvm", optional wasmer-compiler-singlepass = { version = "=5.1.0", path = "../compiler-singlepass", optional = true } wasmer-middlewares = { version = "=5.1.0", path = "../middlewares", optional = true } wasmer-types = { version = "=5.1.0", path = "../types" } -wasmer-wasix = { path = "../wasix", version="=0.31.0", features = ["host-fs", "host-vnet"], optional = true } +wasmer-wasix = { path = "../wasix", version="=0.32.0", features = ["host-fs", "host-vnet"], optional = true } webc = { workspace = true, optional = true } -virtual-fs = { version = "0.19.0", path = "../virtual-fs", optional = true, default-features = false, features = ["static-fs"] } +virtual-fs = { version = "0.20.0", path = "../virtual-fs", optional = true, default-features = false, features = ["static-fs"] } enumset.workspace = true cfg-if = "1.0" lazy_static = "1.4" diff --git a/lib/c-api/examples/wasmer-capi-examples-runner/Cargo.toml b/lib/c-api/examples/wasmer-capi-examples-runner/Cargo.toml index 29c49b177f6..2fd9f77c19f 100644 --- a/lib/c-api/examples/wasmer-capi-examples-runner/Cargo.toml +++ b/lib/c-api/examples/wasmer-capi-examples-runner/Cargo.toml @@ -9,4 +9,4 @@ description = "wasmer-capi-examples-runner" cc = "1.0" target-lexicon = "0.11" regex = "1.6" -walkdir = "2.3.2" \ No newline at end of file +walkdir = "2.3.2" diff --git a/lib/c-api/src/wasm_c_api/engine.rs b/lib/c-api/src/wasm_c_api/engine.rs index f5705d8bf54..5a43d0eec61 100644 --- a/lib/c-api/src/wasm_c_api/engine.rs +++ b/lib/c-api/src/wasm_c_api/engine.rs @@ -253,7 +253,7 @@ pub struct wasm_engine_t { } #[cfg(feature = "compiler")] -use wasmer_api::CompilerConfig; +use wasmer_api::sys::CompilerConfig; #[cfg(all(feature = "compiler", any(feature = "compiler", feature = "dylib")))] fn get_default_compiler_config() -> Box { diff --git a/lib/c-api/src/wasm_c_api/unstable/middlewares/mod.rs b/lib/c-api/src/wasm_c_api/unstable/middlewares/mod.rs index 238154beed6..43b660aa386 100644 --- a/lib/c-api/src/wasm_c_api/unstable/middlewares/mod.rs +++ b/lib/c-api/src/wasm_c_api/unstable/middlewares/mod.rs @@ -5,7 +5,7 @@ pub mod metering; use super::super::engine::wasm_config_t; use std::sync::Arc; -use wasmer_api::ModuleMiddleware; +use wasmer_api::sys::ModuleMiddleware; #[cfg(all(feature = "middlewares", not(feature = "compiler")))] compile_error!("The `middlewares` feature requires the `compiler` feature to be turned on"); diff --git a/lib/c-api/src/wasm_c_api/unstable/target_lexicon.rs b/lib/c-api/src/wasm_c_api/unstable/target_lexicon.rs index b24c3da6475..32ef2c8253e 100644 --- a/lib/c-api/src/wasm_c_api/unstable/target_lexicon.rs +++ b/lib/c-api/src/wasm_c_api/unstable/target_lexicon.rs @@ -57,7 +57,7 @@ use super::super::types::wasm_name_t; use enumset::EnumSet; use std::slice; use std::str::{self, FromStr}; -use wasmer_api::{CpuFeature, Target, Triple}; +use wasmer_api::sys::{CpuFeature, Target, Triple}; /// Unstable non-standard Wasmer-specific API to represent a triple + /// CPU features pair. diff --git a/lib/c-api/tests/wasmer-c-api-test-runner/Cargo.toml b/lib/c-api/tests/wasmer-c-api-test-runner/Cargo.toml index f3714f312b3..caa3d20ef70 100644 --- a/lib/c-api/tests/wasmer-c-api-test-runner/Cargo.toml +++ b/lib/c-api/tests/wasmer-c-api-test-runner/Cargo.toml @@ -9,4 +9,4 @@ description = "wasmer-c-api-test-runner" cc = "1.0" target-lexicon = "0.11" regex = "1.6" -walkdir = "2.3.2" \ No newline at end of file +walkdir = "2.3.2" diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index c8ffe17f914..e21c0d14880 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -109,7 +109,6 @@ enable-serde = [ [dependencies] # Repo-local dependencies. - wasmer = { version = "=5.1.0", path = "../api", default-features = false } wasmer-compiler = { version = "=5.1.0", path = "../compiler", features = [ "compiler", @@ -120,7 +119,7 @@ wasmer-compiler-llvm = { version = "=5.1.0", path = "../compiler-llvm", optional wasmer-package.workspace = true wasmer-vm = { version = "=5.1.0", path = "../vm", optional = true } -wasmer-wasix = { path = "../wasix", version = "=0.31.0", features = [ +wasmer-wasix = { path = "../wasix", version = "=0.32.0", features = [ "logging", "webc_runner_rt_wcgi", "webc_runner_rt_dcgi", @@ -132,11 +131,11 @@ wasmer-wast = { version = "=5.1.0", path = "../../tests/lib/wast", optional = tr wasmer-types = { version = "=5.1.0", path = "../types", features = [ "enable-serde", ] } -virtual-fs = { version = "0.19.0", path = "../virtual-fs", default-features = false, features = [ +virtual-fs = { version = "0.20.0", path = "../virtual-fs", default-features = false, features = [ "host-fs", ] } -virtual-net = { version = "0.11.0", path = "../virtual-net" } -virtual-mio = { version = "0.5.0", path = "../virtual-io" } +virtual-net = { version = "0.12.0", path = "../virtual-net" } +virtual-mio = { version = "0.6.0", path = "../virtual-io" } # Wasmer-owned dependencies. diff --git a/lib/cli/src/commands/compile.rs b/lib/cli/src/commands/compile.rs index 77f330153a7..a70ffa56a38 100644 --- a/lib/cli/src/commands/compile.rs +++ b/lib/cli/src/commands/compile.rs @@ -2,7 +2,10 @@ use std::path::PathBuf; use anyhow::{Context, Result}; use clap::Parser; -use wasmer::*; +use wasmer::{ + sys::{engine::NativeEngineExt, *}, + *, +}; use crate::{common::HashAlgorithm, store::StoreOptions, warning}; diff --git a/lib/cli/src/commands/create_exe.rs b/lib/cli/src/commands/create_exe.rs index 4fbd187b0bd..c72287ae961 100644 --- a/lib/cli/src/commands/create_exe.rs +++ b/lib/cli/src/commands/create_exe.rs @@ -5,7 +5,7 @@ use super::CliCommand; use crate::{ common::{normalize_path, HashAlgorithm}, config::WasmerEnv, - store::CompilerOptions, + store::RuntimeOptions, }; use anyhow::{anyhow, bail, Context, Result}; use clap::Parser; @@ -17,7 +17,10 @@ use std::{ process::{Command, Stdio}, }; use tar::Archive; -use wasmer::{sys::Artifact, *}; +use wasmer::{ + sys::{engine::NativeEngineExt, *}, + *, +}; use wasmer_compiler::{ object::{emit_serialized, get_object_for_target}, types::symbols::ModuleMetadataSymbolRegistry, @@ -96,7 +99,7 @@ pub struct CreateExe { cross_compile: CrossCompile, #[clap(flatten)] - compiler: CompilerOptions, + compiler: RuntimeOptions, /// Hashing algorithm to be used for module hash #[clap(long, value_enum)] @@ -362,7 +365,7 @@ pub enum AllowMultiWasm { pub(super) fn compile_pirita_into_directory( pirita: &Container, target_dir: &Path, - compiler: &CompilerOptions, + compiler: &RuntimeOptions, cpu_features: &[CpuFeature], triple: &Triple, prefixes: &[String], @@ -804,7 +807,7 @@ fn test_split_prefix() { fn compile_atoms( atoms: &[(String, Vec)], output_dir: &Path, - compiler: &CompilerOptions, + compiler: &RuntimeOptions, target: &Target, prefixes: &PrefixMapCompilation, debug: bool, @@ -829,8 +832,8 @@ fn compile_atoms( } continue; } - let (engine, _) = compiler.get_engine_for_target(target.clone())?; - let engine_inner = engine.inner(); + let engine = compiler.get_compiler_engine_for_target(target.clone())?; + let engine_inner = engine.as_sys().inner(); let compiler = engine_inner.compiler()?; let features = engine_inner.features(); let tunables = engine.tunables(); @@ -948,7 +951,7 @@ fn write_volume_obj( pub(super) fn prepare_directory_from_single_wasm_file( wasm_file: &Path, target_dir: &Path, - compiler: &CompilerOptions, + compiler: &RuntimeOptions, triple: &Triple, cpu_features: &[CpuFeature], prefix: &[String], @@ -2185,7 +2188,7 @@ mod http_fetch { pub(super) fn download_release( env: &WasmerEnv, mut release: serde_json::Value, - target_triple: wasmer::Triple, + target_triple: wasmer::sys::Triple, ) -> Result { // Test if file has been already downloaded if let Ok(mut cache_path) = super::utils::get_libwasmer_cache_path(env) { diff --git a/lib/cli/src/commands/create_obj.rs b/lib/cli/src/commands/create_obj.rs index 444f224c26c..515e320c029 100644 --- a/lib/cli/src/commands/create_obj.rs +++ b/lib/cli/src/commands/create_obj.rs @@ -5,10 +5,10 @@ use std::{env, path::PathBuf}; use anyhow::{Context, Result}; use clap::Parser; -use wasmer::*; +use wasmer::sys::*; use wasmer_package::utils::from_disk; -use crate::store::CompilerOptions; +use crate::store::RuntimeOptions; #[derive(Debug, Parser)] /// The options for the `wasmer create-exe` subcommand @@ -55,7 +55,7 @@ pub struct CreateObj { cpu_features: Vec, #[clap(flatten)] - compiler: CompilerOptions, + rt: RuntimeOptions, } impl CreateObj { @@ -80,7 +80,14 @@ impl CreateObj { &target_triple, &self.cpu_features, ); - let (_, compiler_type) = self.compiler.get_store_for_target(target.clone())?; + let (_, compiler_type) = self.rt.get_store_for_target(target.clone())?; + match compiler_type { + crate::store::RuntimeType::V8 | crate::store::RuntimeType::Wamr => { + anyhow::bail!("Cannot produce objects with {compiler_type}!") + } + crate::store::RuntimeType::Headless => todo!(), + _ => {} + } println!("Compiler: {}", compiler_type); println!("Target: {}", target.triple()); @@ -88,7 +95,7 @@ impl CreateObj { crate::commands::create_exe::compile_pirita_into_directory( &webc, &output_directory_path, - &self.compiler, + &self.rt, &self.cpu_features, &target_triple, &prefix, @@ -99,7 +106,7 @@ impl CreateObj { crate::commands::create_exe::prepare_directory_from_single_wasm_file( &input_path, &output_directory_path, - &self.compiler, + &self.rt, &target_triple, &self.cpu_features, &prefix, diff --git a/lib/cli/src/commands/gen_c_header.rs b/lib/cli/src/commands/gen_c_header.rs index aa523f28517..895f3be376a 100644 --- a/lib/cli/src/commands/gen_c_header.rs +++ b/lib/cli/src/commands/gen_c_header.rs @@ -14,7 +14,7 @@ use wasmer_package::{package::WasmerPackageError, utils::from_bytes}; use wasmer_types::MetadataHeader; use webc::{compat::SharedBytes, Container, ContainerError, DetectError}; -use crate::store::CompilerOptions; +use crate::store::RuntimeOptions; #[derive(Debug, Parser)] /// The options for the `wasmer gen-c-header` subcommand @@ -83,7 +83,12 @@ impl GenCHeader { &target_triple, &self.cpu_features, ); - let (engine, _) = CompilerOptions::default().get_engine_for_target(target.clone())?; + let engine = RuntimeOptions::default().get_compiler_engine_for_target(target.clone())?; + if !engine.is_sys() { + anyhow::bail!("Cannot use this engine to generate c-headers! Please, use one of `cranelift`, `llvm` or `singlepass`."); + } + let engine = engine.into_sys(); + let engine_inner = engine.inner(); let compiler = engine_inner.compiler()?; let features = engine_inner.features(); diff --git a/lib/cli/src/commands/init.rs b/lib/cli/src/commands/init.rs index 645a2207904..2892684efad 100644 --- a/lib/cli/src/commands/init.rs +++ b/lib/cli/src/commands/init.rs @@ -569,6 +569,6 @@ fn parse_cargo_toml(manifest_path: &PathBuf) -> Result Result<(), anyhow::Error> { println!("commit-date: {}", env!("WASMER_BUILD_DATE")); println!("host: {}", target_lexicon::HOST); - let mut compilers = Vec::<&'static str>::new(); + let mut runtimes = Vec::<&'static str>::new(); if cfg!(feature = "singlepass") { - compilers.push("singlepass"); + runtimes.push("singlepass"); } if cfg!(feature = "cranelift") { - compilers.push("cranelift"); + runtimes.push("cranelift"); } if cfg!(feature = "llvm") { - compilers.push("llvm"); + runtimes.push("llvm"); } - println!("compiler: {}", compilers.join(",")); - - let mut interpreters = Vec::<&'static str>::new(); if cfg!(feature = "wamr") { - interpreters.push("wamr"); + runtimes.push("wamr"); } if cfg!(feature = "wasmi") { - // Can't use two different c_api backends together as of now, but maybe we'll support more - // interepreters. - interpreters.push("wasmi"); + runtimes.push("wasmi"); } if cfg!(feature = "v8") { - // Can't use c_api backends together as of now, but maybe we'll support more - // interepreters. - interpreters.push("v8"); + runtimes.push("v8"); } - println!("c_api backend: {}", interpreters.join(",")); + println!("runtimes: {}", runtimes.join(", ")); Ok(()) } diff --git a/lib/cli/src/commands/run/mod.rs b/lib/cli/src/commands/run/mod.rs index a7bd678e76c..25b40257588 100644 --- a/lib/cli/src/commands/run/mod.rs +++ b/lib/cli/src/commands/run/mod.rs @@ -23,7 +23,7 @@ use once_cell::sync::Lazy; use tempfile::NamedTempFile; use url::Url; #[cfg(feature = "sys")] -use wasmer::NativeEngineExt; +use wasmer::sys::NativeEngineExt; use wasmer::{ DeserializeError, Engine, Function, Imports, Instance, Module, Store, Type, TypedFunction, Value, @@ -115,11 +115,6 @@ impl Run { .build()?; let handle = runtime.handle().clone(); - #[cfg(feature = "sys")] - if self.stack_size.is_some() { - wasmer_vm::set_stack_size(self.stack_size.unwrap()); - } - // Check for the preferred webc version. // Default to v3. let webc_version_var = std::env::var("WASMER_WEBC_VERSION"); @@ -132,18 +127,23 @@ impl Run { }; let _guard = handle.enter(); - let (store, _) = self.store.get_store()?; + + let (mut store, _) = self.store.get_store()?; + + #[cfg(feature = "sys")] + if self.stack_size.is_some() { + wasmer_vm::set_stack_size(self.stack_size.unwrap()); + } + + let engine = store.engine_mut(); #[cfg(feature = "sys")] - let engine = { - let mut engine = store.engine().clone(); + if engine.is_sys() { let hash_algorithm = self.hash_algorithm.unwrap_or_default().into(); engine.set_hash_algorithm(Some(hash_algorithm)); + } - engine - }; - #[cfg(not(feature = "sys"))] - let engine = store.engine().clone(); + let engine = engine.clone(); let runtime = self.wasi.prepare_runtime( engine, diff --git a/lib/cli/src/commands/wast.rs b/lib/cli/src/commands/wast.rs index a2ce63b7891..63a78d60572 100644 --- a/lib/cli/src/commands/wast.rs +++ b/lib/cli/src/commands/wast.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use anyhow::{Context, Result}; use clap::Parser; -use wasmer::NativeEngineExt; +use wasmer::sys::engine::NativeEngineExt; use wasmer_wast::Wast as WastSpectest; use crate::{common::HashAlgorithm, store::StoreOptions}; diff --git a/lib/cli/src/store.rs b/lib/cli/src/store.rs index 47f9baff755..4ee1adc5458 100644 --- a/lib/cli/src/store.rs +++ b/lib/cli/src/store.rs @@ -12,19 +12,19 @@ use std::sync::Arc; use anyhow::{bail, Result}; #[cfg(feature = "sys")] -use wasmer::sys::Features; +use wasmer::sys::*; use wasmer::*; + #[cfg(feature = "compiler")] use wasmer_compiler::CompilerConfig; -#[cfg(feature = "compiler")] -use wasmer_compiler::Engine; + +use wasmer::Engine; #[derive(Debug, Clone, clap::Parser, Default)] /// The compiler options pub struct StoreOptions { - #[cfg(feature = "compiler")] #[clap(flatten)] - compiler: CompilerOptions, + rt: RuntimeOptions, } #[derive(Debug, clap::Parser, Clone, Default)] @@ -60,22 +60,39 @@ pub struct WasmFeatures { pub all: bool, } -#[cfg(feature = "compiler")] #[derive(Debug, Clone, clap::Parser, Default)] /// The compiler options -pub struct CompilerOptions { +pub struct RuntimeOptions { /// Use Singlepass compiler. - #[clap(long, conflicts_with_all = &["cranelift", "llvm"])] + #[cfg(feature = "singlepass")] + #[clap(long, conflicts_with_all = &["cranelift", "llvm", "v8", "wamr", "wasmi"])] singlepass: bool, /// Use Cranelift compiler. - #[clap(long, conflicts_with_all = &["singlepass", "llvm"])] + #[cfg(feature = "cranelift")] + #[clap(long, conflicts_with_all = &["singlepass", "llvm", "v8", "wamr", "wasmi"])] cranelift: bool, /// Use LLVM compiler. - #[clap(long, conflicts_with_all = &["singlepass", "cranelift"])] + #[cfg(feature = "llvm")] + #[clap(long, conflicts_with_all = &["singlepass", "cranelift", "v8", "wamr", "wasmi"])] llvm: bool, + /// Use the V8 runtime. + #[cfg(feature = "v8")] + #[clap(long, conflicts_with_all = &["singlepass", "cranelift", "llvm", "wamr", "wasmi"])] + v8: bool, + + /// Use WAMR. + #[cfg(feature = "wamr")] + #[clap(long, conflicts_with_all = &["singlepass", "cranelift", "llvm", "v8", "wasmi"])] + wamr: bool, + + /// Use the wasmi runtime. + #[cfg(feature = "wasmi")] + #[clap(long, conflicts_with_all = &["singlepass", "cranelift", "llvm", "v8", "wamr"])] + wasmi: bool, + /// Enable compiler internal verification. /// /// Available for cranelift, LLVM and singlepass. @@ -92,33 +109,71 @@ pub struct CompilerOptions { features: WasmFeatures, } -#[cfg(feature = "compiler")] -impl CompilerOptions { - fn get_compiler(&self) -> Result { - if self.cranelift { - Ok(CompilerType::Cranelift) - } else if self.llvm { - Ok(CompilerType::LLVM) - } else if self.singlepass { - Ok(CompilerType::Singlepass) - } else { - // Auto mode, we choose the best compiler for that platform - cfg_if::cfg_if! { - if #[cfg(all(feature = "cranelift", any(target_arch = "x86_64", target_arch = "aarch64")))] { - Ok(CompilerType::Cranelift) - } - else if #[cfg(all(feature = "singlepass", any(target_arch = "x86_64", target_arch = "aarch64")))] { - Ok(CompilerType::Singlepass) - } - else if #[cfg(feature = "llvm")] { - Ok(CompilerType::LLVM) - } else { - bail!("There are no available compilers for your architecture"); - } +impl RuntimeOptions { + fn get_rt(&self) -> Result { + #[cfg(feature = "cranelift")] + { + if self.cranelift { + return Ok(RuntimeType::Cranelift); + } + } + + #[cfg(feature = "llvm")] + { + if self.llvm { + return Ok(RuntimeType::LLVM); + } + } + + #[cfg(feature = "singlepass")] + { + if self.singlepass { + return Ok(RuntimeType::Singlepass); + } + } + + #[cfg(feature = "wamr")] + { + if self.wamr { + return Ok(RuntimeType::Wamr); + } + } + + #[cfg(feature = "v8")] + { + if self.v8 { + return Ok(RuntimeType::V8); + } + } + + #[cfg(feature = "wasmi")] + { + if self.wasmi { + return Ok(RuntimeType::Wasmi); + } + } + + // Auto mode, we choose the best compiler for that platform + cfg_if::cfg_if! { + if #[cfg(all(feature = "cranelift", any(target_arch = "x86_64", target_arch = "aarch64")))] { + Ok(RuntimeType::Cranelift) + } else if #[cfg(all(feature = "singlepass", any(target_arch = "x86_64", target_arch = "aarch64")))] { + Ok(RuntimeType::Singlepass) + } else if #[cfg(feature = "llvm")] { + Ok(RuntimeType::LLVM) + } else if #[cfg(feature = "v8")] { + Ok(RuntimeType::V8) + } else if #[cfg(feature = "wamr")] { + Ok(RuntimeType::Wamr) + } else if #[cfg(feature = "wasmi")] { + Ok(RuntimeType::Wasmi) + } else { + bail!("There are no available compilers for your architecture"); } } } + #[cfg(feature = "compiler")] /// Get the enaled Wasm features. pub fn get_features(&self, mut features: Features) -> Result { if !self.features.disable_threads || self.features.all { @@ -143,43 +198,71 @@ impl CompilerOptions { } /// Gets the Store for a given target. - pub fn get_store_for_target(&self, target: Target) -> Result<(Store, CompilerType)> { - let (compiler_config, compiler_type) = self.get_compiler_config()?; - let engine = self.get_engine(target, compiler_config)?; + #[cfg(feature = "compiler")] + pub fn get_store_for_target(&self, target: Target) -> Result<(Store, RuntimeType)> { + let rt = self.get_rt()?; + let engine = self.get_engine(target, &rt)?; let store = Store::new(engine); - Ok((store, compiler_type)) + Ok((store, rt)) } - /// Gets the Engine for a given target. - pub fn get_engine_for_target(&self, target: Target) -> Result<(Engine, CompilerType)> { - let (compiler_config, compiler_type) = self.get_compiler_config()?; - let engine = self.get_engine(target, compiler_config)?; - Ok((engine, compiler_type)) + #[cfg(feature = "compiler")] + fn get_engine(&self, target: Target, rt: &RuntimeType) -> Result { + match rt { + RuntimeType::V8 => { + #[cfg(feature = "v8")] + return Ok(wasmer::v8::V8::new().into()); + #[allow(unreachable_code)] + { + anyhow::bail!("The `v8` engine is not enabled in this build.") + } + } + RuntimeType::Wamr => { + #[cfg(feature = "wamr")] + return Ok(wasmer::wamr::Wamr::new().into()); + #[allow(unreachable_code)] + { + anyhow::bail!("The `wamr` engine is not enabled in this build.") + } + } + RuntimeType::Wasmi => { + #[cfg(feature = "wasmi")] + return Ok(wasmer::wasmi::Wasmi::new().into()); + #[allow(unreachable_code)] + { + anyhow::bail!("The `wasmi` engine is not enabled in this build.") + } + } + #[cfg(feature = "compiler")] + _ => self.get_compiler_engine_for_target(target), + + #[cfg(not(feature = "compiler"))] + _ => anyhow::bail!("No engine selected!"), + } } #[cfg(feature = "compiler")] - fn get_engine( + pub fn get_compiler_engine_for_target( &self, target: Target, - compiler_config: Box, - ) -> Result { + ) -> std::result::Result { + let rt = self.get_rt()?; + let compiler_config = self.get_compiler_config(&rt)?; let features = self.get_features(compiler_config.default_features_for_target(&target))?; - let engine: Engine = wasmer_compiler::EngineBuilder::new(compiler_config) + Ok(wasmer_compiler::EngineBuilder::new(compiler_config) .set_features(Some(features)) .set_target(Some(target)) - .engine(); - - Ok(engine) + .engine() + .into()) } - /// Get the Compiler Config for the current options #[allow(unused_variables)] - pub(crate) fn get_compiler_config(&self) -> Result<(Box, CompilerType)> { - let compiler = self.get_compiler()?; - let compiler_config: Box = match compiler { - CompilerType::Headless => bail!("The headless engine can't be chosen"), + #[cfg(feature = "compiler")] + pub(crate) fn get_compiler_config(&self, rt: &RuntimeType) -> Result> { + let compiler_config: Box = match rt { + RuntimeType::Headless => bail!("The headless engine can't be chosen"), #[cfg(feature = "singlepass")] - CompilerType::Singlepass => { + RuntimeType::Singlepass => { let mut config = wasmer_compiler_singlepass::Singlepass::new(); if self.enable_verifier { config.enable_verifier(); @@ -187,7 +270,7 @@ impl CompilerOptions { Box::new(config) } #[cfg(feature = "cranelift")] - CompilerType::Cranelift => { + RuntimeType::Cranelift => { let mut config = wasmer_compiler_cranelift::Cranelift::new(); if self.enable_verifier { config.enable_verifier(); @@ -195,7 +278,7 @@ impl CompilerOptions { Box::new(config) } #[cfg(feature = "llvm")] - CompilerType::LLVM => { + RuntimeType::LLVM => { use std::{fmt, fs::File, io::Write}; use wasmer_compiler_llvm::{ @@ -296,7 +379,8 @@ impl CompilerOptions { } Box::new(config) } - #[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm",)))] + RuntimeType::V8 | RuntimeType::Wamr | RuntimeType::Wasmi => unreachable!(), + #[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm")))] compiler => { bail!( "The `{}` compiler is not included in this binary.", @@ -306,29 +390,41 @@ impl CompilerOptions { }; #[allow(unreachable_code)] - Ok((compiler_config, compiler)) + Ok(compiler_config) } } /// The compiler used for the store #[derive(Debug, PartialEq, Eq)] #[allow(clippy::upper_case_acronyms, dead_code)] -pub enum CompilerType { +pub enum RuntimeType { /// Singlepass compiler Singlepass, + /// Cranelift compiler Cranelift, + /// LLVM compiler LLVM, + + /// V8 runtime + V8, + + /// Wamr runtime + Wamr, + + /// Wasmi runtime + Wasmi, + /// Headless compiler #[allow(dead_code)] Headless, } -impl CompilerType { +impl RuntimeType { /// Return all enabled compilers #[allow(dead_code)] - pub fn enabled() -> Vec { + pub fn enabled() -> Vec { vec![ #[cfg(feature = "singlepass")] Self::Singlepass, @@ -336,11 +432,15 @@ impl CompilerType { Self::Cranelift, #[cfg(feature = "llvm")] Self::LLVM, + #[cfg(feature = "v8")] + Self::V8, + #[cfg(feature = "wamr")] + Self::Wamr, ] } } -impl std::fmt::Display for CompilerType { +impl std::fmt::Display for RuntimeType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, @@ -349,6 +449,9 @@ impl std::fmt::Display for CompilerType { Self::Singlepass => "singlepass", Self::Cranelift => "cranelift", Self::LLVM => "llvm", + Self::V8 => "v8", + Self::Wamr => "wamr", + Self::Wasmi => "wasmi", Self::Headless => "headless", } ) @@ -358,27 +461,17 @@ impl std::fmt::Display for CompilerType { #[cfg(feature = "compiler")] impl StoreOptions { /// Gets the store for the host target, with the compiler name selected - pub fn get_store(&self) -> Result<(Store, CompilerType)> { + pub fn get_store(&self) -> Result<(Store, RuntimeType)> { let target = Target::default(); self.get_store_for_target(target) } /// Gets the store for a given target, with the compiler name selected. - pub fn get_store_for_target(&self, target: Target) -> Result<(Store, CompilerType)> { - let (compiler_config, compiler_type) = self.compiler.get_compiler_config()?; - let engine = self.get_engine_with_compiler(target, compiler_config)?; + pub fn get_store_for_target(&self, target: Target) -> Result<(Store, RuntimeType)> { + let rt = self.rt.get_rt()?; + let engine = self.rt.get_engine(target, &rt)?; let store = Store::new(engine); - Ok((store, compiler_type)) - } - - #[cfg(feature = "compiler")] - fn get_engine_with_compiler( - &self, - target: Target, - compiler_config: Box, - ) -> Result { - let engine = self.compiler.get_engine(target, compiler_config)?; - Ok(engine) + Ok((store, rt)) } } @@ -397,10 +490,10 @@ impl StoreOptions { } /// Get the store (headless engine) - pub fn get_store(&self) -> Result<(Store, CompilerType)> { + pub fn get_store(&self) -> Result<(Store, RuntimeType)> { let engine = self.get_engine_headless()?; let store = Store::new(engine); - Ok((store, CompilerType::Headless)) + Ok((store, RuntimeType::Headless)) } } @@ -410,8 +503,8 @@ impl StoreOptions { ))] impl StoreOptions { /// Get the store (headless engine) - pub fn get_store(&self) -> Result<(Store, CompilerType)> { + pub fn get_store(&self) -> Result<(Store, RuntimeType)> { let store = Store::default(); - Ok((store, CompilerType::Headless)) + Ok((store, RuntimeType::Headless)) } } diff --git a/lib/config/src/package/mod.rs b/lib/config/src/package/mod.rs index 5f2d61f7564..2fe0611c4c5 100644 --- a/lib/config/src/package/mod.rs +++ b/lib/config/src/package/mod.rs @@ -1031,12 +1031,12 @@ license = "MIT" [[module]] name = "mod" -source = "target/wasm32-wasi/release/mod.wasm" +source = "target/wasm32-wasip1/release/mod.wasm" interfaces = {"wasi" = "0.0.0-unstable"} [[module]] name = "mod-with-exports" -source = "target/wasm32-wasi/release/mod-with-exports.wasm" +source = "target/wasm32-wasip1/release/mod-with-exports.wasm" bindings = { wit-exports = "exports.wit", wit-bindgen = "0.0.0" } [[command]] @@ -1054,7 +1054,7 @@ module = "mod" modules[1], Module { name: "mod-with-exports".to_string(), - source: PathBuf::from("target/wasm32-wasi/release/mod-with-exports.wasm"), + source: PathBuf::from("target/wasm32-wasip1/release/mod-with-exports.wasm"), abi: Abi::None, kind: None, interfaces: None, diff --git a/lib/config/src/package/named_package_ident.rs b/lib/config/src/package/named_package_ident.rs index d1c9fa6e2d2..151ec1faa4a 100644 --- a/lib/config/src/package/named_package_ident.rs +++ b/lib/config/src/package/named_package_ident.rs @@ -172,6 +172,24 @@ impl NamedPackageIdent { out } + + /// Returns true if this ident matches the given package id. + /// + /// Semver constraints are matched against the package id's version. + pub fn matches_id(&self, id: &NamedPackageId) -> bool { + if self.full_name() == id.full_name { + if let Some(tag) = &self.tag { + match tag { + Tag::Named(n) => n == &id.version.to_string(), + Tag::VersionReq(v) => v.matches(&id.version), + } + } else { + true + } + } else { + false + } + } } impl From for NamedPackageIdent { @@ -443,4 +461,23 @@ mod tests { let ident2 = serde_json::from_str::(&raw).unwrap(); assert_eq!(ident, ident2); } + + #[test] + fn test_named_package_ident_matches_id() { + assert!(NamedPackageIdent::from_str("ns/name") + .unwrap() + .matches_id(&NamedPackageId::try_new("ns/name", "0.1.0").unwrap())); + + assert!(NamedPackageIdent::from_str("ns/name") + .unwrap() + .matches_id(&NamedPackageId::try_new("ns/name", "1.0.1").unwrap())); + + assert!(NamedPackageIdent::from_str("ns/name@1") + .unwrap() + .matches_id(&NamedPackageId::try_new("ns/name", "1.0.1").unwrap())); + + assert!(!NamedPackageIdent::from_str("ns/name@2") + .unwrap() + .matches_id(&NamedPackageId::try_new("ns/name", "1.0.1").unwrap())); + } } diff --git a/lib/config/src/package/package_ident.rs b/lib/config/src/package/package_ident.rs index 5b03d6ef812..0e6a5544a78 100644 --- a/lib/config/src/package/package_ident.rs +++ b/lib/config/src/package/package_ident.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use super::{NamedPackageIdent, PackageHash, PackageParseError}; +use super::{NamedPackageIdent, PackageHash, PackageId, PackageParseError}; #[derive(PartialEq, Eq, Clone, Debug, Hash)] pub enum PackageIdent { @@ -24,6 +24,17 @@ impl PackageIdent { None } } + + /// Returns true if this ident matches the given package id. + /// + /// Semver constraints are matched against the package id's version. + pub fn matches_id(&self, id: &PackageId) -> bool { + match (self, id) { + (Self::Named(a), PackageId::Named(b)) => a.matches_id(b), + (Self::Hash(a), PackageId::Hash(b)) => a == b, + _ => false, + } + } } impl From for PackageIdent { @@ -92,3 +103,23 @@ impl schemars::JsonSchema for PackageIdent { String::json_schema(gen) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_package_ident_matches_id() { + assert!(PackageIdent::from_str("ns/pkg") + .unwrap() + .matches_id(&PackageId::new_named("ns/pkg", "1.0.0".parse().unwrap()))); + + assert!(PackageIdent::from_str("ns/pkg@2") + .unwrap() + .matches_id(&PackageId::new_named("ns/pkg", "2.3.7".parse().unwrap()))); + + assert!(!PackageIdent::from_str("ns/pkg@3") + .unwrap() + .matches_id(&PackageId::new_named("ns/pkg", "2.3.7".parse().unwrap()))); + } +} diff --git a/lib/journal/Cargo.toml b/lib/journal/Cargo.toml index 3aba4591c8d..88e0f5ce233 100644 --- a/lib/journal/Cargo.toml +++ b/lib/journal/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-journal" -version = "0.13.0" +version = "0.14.0" description = "Journaling functionality used by Wasmer to save and restore WASM state" authors.workspace = true edition.workspace = true @@ -15,13 +15,13 @@ log-file = ["shared-buffer"] [dependencies] wasmer = { default-features = false, path = "../api", version = "=5.1.0" } -wasmer-wasix-types = { path = "../wasi-types", version = "0.31.0", features = [ +wasmer-wasix-types = { path = "../wasi-types", version = "0.32.0", features = [ "enable-serde", ] } -virtual-net = { path = "../virtual-net", version = "0.11.0", default-features = false, features = [ +virtual-net = { path = "../virtual-net", version = "0.12.0", default-features = false, features = [ "rkyv", ] } -virtual-fs = { path = "../virtual-fs", version = "0.19.0", default-features = false } +virtual-fs = { path = "../virtual-fs", version = "0.20.0", default-features = false } shared-buffer = { workspace = true, optional = true } base64.workspace = true diff --git a/lib/middlewares/Cargo.toml b/lib/middlewares/Cargo.toml index 250ebec9da7..2820012203a 100644 --- a/lib/middlewares/Cargo.toml +++ b/lib/middlewares/Cargo.toml @@ -13,12 +13,12 @@ rust-version.workspace = true version.workspace = true [dependencies] -wasmer = { path = "../api", version = "=5.1.0", default-features = false, features = ["compiler"] } +wasmer = { path = "../api", version = "=5.1.0", default-features = false, features = ["compiler", "wasmparser"] } wasmer-types = { path = "../types", version = "=5.1.0" } wasmer-vm = { path = "../vm", version = "=5.1.0" } [dev-dependencies] -wasmer = { path = "../api", version = "=5.1.0", features = ["compiler"] } +wasmer = { path = "../api", version = "=5.1.0", features = ["compiler", "wasmparser"] } [badges] maintenance = { status = "actively-developed" } diff --git a/lib/middlewares/src/metering.rs b/lib/middlewares/src/metering.rs index 1207269a2da..72a5820b74a 100644 --- a/lib/middlewares/src/metering.rs +++ b/lib/middlewares/src/metering.rs @@ -13,8 +13,9 @@ use std::fmt; use std::sync::{Arc, Mutex}; use wasmer::wasmparser::{BlockType as WpTypeOrFuncType, Operator}; use wasmer::{ - AsStoreMut, ExportIndex, FunctionMiddleware, GlobalInit, GlobalType, Instance, - LocalFunctionIndex, MiddlewareError, MiddlewareReaderState, ModuleMiddleware, Mutability, Type, + sys::{FunctionMiddleware, MiddlewareError, MiddlewareReaderState, ModuleMiddleware}, + AsStoreMut, ExportIndex, GlobalInit, GlobalType, Instance, LocalFunctionIndex, Mutability, + Type, }; use wasmer_types::{GlobalIndex, ModuleInfo}; @@ -59,7 +60,7 @@ impl fmt::Debug for MeteringGlobalIndexes { /// /// ```rust /// use std::sync::Arc; -/// use wasmer::{wasmparser::Operator, CompilerConfig}; +/// use wasmer::{wasmparser::Operator, sys::CompilerConfig}; /// use wasmer_middlewares::Metering; /// /// fn create_metering_middleware(compiler_config: &mut dyn CompilerConfig) { @@ -395,7 +396,11 @@ mod tests { use std::sync::Arc; use wasmer::sys::EngineBuilder; - use wasmer::{imports, wat2wasm, CompilerConfig, Cranelift, Module, Store, TypedFunction}; + use wasmer::{ + imports, + sys::{CompilerConfig, Cranelift}, + wat2wasm, Module, Store, TypedFunction, + }; fn cost_function(operator: &Operator) -> u64 { match operator { diff --git a/lib/package/Cargo.toml b/lib/package/Cargo.toml index e148d09299e..59e5d5dbcbc 100644 --- a/lib/package/Cargo.toml +++ b/lib/package/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-package" -version = "0.2.0" +version = "0.3.0" description = "Packaging related types and functions for Wasmer" authors.workspace = true diff --git a/lib/package/src/package/package.rs b/lib/package/src/package/package.rs index 35a2c064ea6..3666c134910 100644 --- a/lib/package/src/package/package.rs +++ b/lib/package/src/package/package.rs @@ -558,7 +558,7 @@ fn tempdir() -> Result { return TempDir::new(); } - // Note: When compiling to wasm32-wasi, we can't use TempDir::new() + // Note: When compiling to wasm32-wasip1, we can't use TempDir::new() // because std::env::temp_dir() will unconditionally panic. let temp_dir: PathBuf = std::env::var("TMPDIR") .unwrap_or_else(|_| "/tmp".to_string()) @@ -589,7 +589,7 @@ fn tempdir() -> Result { /// A polyfill for [`Archive::unpack()`] that is WASI-compatible. /// /// This works around `canonicalize()` being [unsupported][github] on -/// `wasm32-wasi`. +/// `wasm32-wasip1`. /// /// [github]: https://github.com/rust-lang/rust/blob/5b1dc9de77106cb08ce9a1a8deaa14f52751d7e4/library/std/src/sys/wasi/fs.rs#L654-L658 fn unpack_archive( diff --git a/lib/swift/Cargo.toml b/lib/swift/Cargo.toml index ef5b3f1b780..c3c06d6ec3f 100644 --- a/lib/swift/Cargo.toml +++ b/lib/swift/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-swift" -version = "0.3.0" +version = "0.4.0" edition = "2021" description = "Experimental wasmer bindings for swift" license = "MIT" @@ -13,16 +13,16 @@ name = "wasmer_swift" [dependencies] thiserror = "1" -tokio = { version = "1.28.1", features = [ "rt" ], default-features = false } +tokio = { version = "1.28.1", features = ["rt"], default-features = false } uniffi = "0.27" -virtual-fs = { path = "../virtual-fs", version = "=0.19.0", default-features = false, features = [ +virtual-fs = { path = "../virtual-fs", version = "=0.20.0", default-features = false, features = [ "webc-fs", ] } wasmer = { version = "=5.1.0", path = "../api", default-features = false, features = [ "wamr", "std", ] } -wasmer-wasix = { version = "=0.31.0", path = "../wasix" } +wasmer-wasix = { version = "=0.32.0", path = "../wasix" } webc.workspace = true wasmer-package.workspace = true diff --git a/lib/sys-utils/Cargo.toml b/lib/sys-utils/Cargo.toml index d86bca060d4..8ea85a9ca29 100644 --- a/lib/sys-utils/Cargo.toml +++ b/lib/sys-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-sys-utils" -version = "0.31.0" +version = "0.32.0" description = "Wasmer utilities for a sys environment." categories = ["wasm"] keywords = ["wasm", "webassembly"] diff --git a/lib/types/src/module.rs b/lib/types/src/module.rs index 69707de69d1..7e659509704 100644 --- a/lib/types/src/module.rs +++ b/lib/types/src/module.rs @@ -363,7 +363,7 @@ impl ModuleInfo { } /// Get the export types of the module - pub fn exports(&'_ self) -> ExportsIterator + '_> { + pub fn exports(&'_ self) -> ExportsIterator + '_>> { let iter = self.exports.iter().map(move |(name, export_index)| { let extern_type = match export_index { ExportIndex::Function(i) => { @@ -386,11 +386,11 @@ impl ModuleInfo { }; ExportType::new(name, extern_type) }); - ExportsIterator::new(iter, self.exports.len()) + ExportsIterator::new(Box::new(iter), self.exports.len()) } /// Get the import types of the module - pub fn imports(&'_ self) -> ImportsIterator + '_> { + pub fn imports(&'_ self) -> ImportsIterator + '_>> { let iter = self.imports .iter() @@ -416,19 +416,24 @@ impl ModuleInfo { }; ImportType::new(module, field, extern_type) }); - ImportsIterator::new(iter, self.imports.len()) + ImportsIterator::new(Box::new(iter), self.imports.len()) } /// Get the custom sections of the module given a `name`. - pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator> + 'a { - self.custom_sections - .iter() - .filter_map(move |(section_name, section_index)| { - if name != section_name { - return None; - } - Some(self.custom_sections_data[*section_index].clone()) - }) + pub fn custom_sections<'a>( + &'a self, + name: &'a str, + ) -> Box> + 'a> { + Box::new( + self.custom_sections + .iter() + .filter_map(move |(section_name, section_index)| { + if name != section_name { + return None; + } + Some(self.custom_sections_data[*section_index].clone()) + }), + ) } /// Convert a `LocalFunctionIndex` into a `FunctionIndex`. diff --git a/lib/virtual-fs/Cargo.toml b/lib/virtual-fs/Cargo.toml index 9130bbb5873..28376c0027d 100644 --- a/lib/virtual-fs/Cargo.toml +++ b/lib/virtual-fs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "virtual-fs" -version = "0.19.0" +version = "0.20.0" description = "Wasmer Virtual FileSystem" authors.workspace = true edition.workspace = true diff --git a/lib/virtual-io/Cargo.toml b/lib/virtual-io/Cargo.toml index 943fdcadd66..b4e2748dc1b 100644 --- a/lib/virtual-io/Cargo.toml +++ b/lib/virtual-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "virtual-mio" -version = "0.5.0" +version = "0.6.0" description = "Wasmer Virtual IO Engine powered by mio" authors.workspace = true edition.workspace = true diff --git a/lib/virtual-net/Cargo.toml b/lib/virtual-net/Cargo.toml index f9370c4578c..623b4897537 100644 --- a/lib/virtual-net/Cargo.toml +++ b/lib/virtual-net/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "virtual-net" -version = "0.11.0" +version = "0.12.0" description = "Wasmer Virtual Networking" authors.workspace = true edition.workspace = true @@ -23,7 +23,7 @@ libc = { workspace = true, optional = true } mio = { workspace = true, optional = true } socket2 = { workspace = true, optional = true } derive_more.workspace = true -virtual-mio = { path = "../virtual-io", version = "0.5.0", default-features = false } +virtual-mio = { path = "../virtual-io", version = "0.6.0", default-features = false } bincode = { version = "1.3" } serde = { version = "1.0", default-features = false, features = ["derive"] } pin-project-lite = "0.2.9" diff --git a/lib/vm/src/function_env.rs b/lib/vm/src/function_env.rs index 1ca855443ef..8f681b762d0 100644 --- a/lib/vm/src/function_env.rs +++ b/lib/vm/src/function_env.rs @@ -2,7 +2,8 @@ use std::any::Any; /// Underlying FunctionEnvironment used by a `VMFunction`. pub struct VMFunctionEnvironment { - contents: Box, + /// The contents of the environment. + pub contents: Box, } impl std::fmt::Debug for VMFunctionEnvironment { diff --git a/lib/vm/src/lib.rs b/lib/vm/src/lib.rs index 9cfbcac22c8..1ff649c6a75 100644 --- a/lib/vm/src/lib.rs +++ b/lib/vm/src/lib.rs @@ -57,6 +57,7 @@ pub use crate::vmcontext::{ VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, VMTrampoline, }; +pub use store::StoreObject; pub use wasmer_types::LibCall; pub use wasmer_types::MemoryError; pub use wasmer_types::MemoryStyle; diff --git a/lib/vm/src/store.rs b/lib/vm/src/store.rs index 09f25e6bb44..95f2ca712bf 100644 --- a/lib/vm/src/store.rs +++ b/lib/vm/src/store.rs @@ -8,7 +8,10 @@ use wasmer_types::StoreId; /// Trait to represent an object managed by a context. This is implemented on /// the VM types managed by the context. pub trait StoreObject: Sized { + /// List the objects in the store. fn list(ctx: &StoreObjects) -> &Vec; + + /// List the objects in the store, mutably. fn list_mut(ctx: &mut StoreObjects) -> &mut Vec; } macro_rules! impl_context_object { @@ -49,6 +52,30 @@ pub struct StoreObjects { } impl StoreObjects { + /// Create a new instance of [`Self`] + #[allow(clippy::too_many_arguments)] + pub fn new( + id: StoreId, + memories: Vec, + tables: Vec, + globals: Vec, + functions: Vec, + instances: Vec, + extern_objs: Vec, + function_environments: Vec, + ) -> Self { + Self { + id, + memories, + tables, + globals, + functions, + instances, + extern_objs, + function_environments, + } + } + /// Returns the ID of this context. pub fn id(&self) -> StoreId { self.id diff --git a/lib/wai-bindgen-wasmer/Cargo.toml b/lib/wai-bindgen-wasmer/Cargo.toml index b1861d94d0b..1dd2f6ef118 100644 --- a/lib/wai-bindgen-wasmer/Cargo.toml +++ b/lib/wai-bindgen-wasmer/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "wai-bindgen-wasmer" description = "Generate WAI glue for a Rust Wasmer host" -version = "0.31.0" +version = "0.32.0" categories = ["wasm", "os"] keywords = ["wasm", "webassembly", "wasi", "sandbox", "ABI"] readme = "README.md" diff --git a/lib/wasi-types/Cargo.toml b/lib/wasi-types/Cargo.toml index 3a8bfbb6504..36178d53cff 100644 --- a/lib/wasi-types/Cargo.toml +++ b/lib/wasi-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-wasix-types" -version = "0.31.0" +version = "0.32.0" description = "WASI and WASIX types for Wasmer WebAssembly runtime" categories = ["wasm", "os"] keywords = ["wasm", "webassembly", "wasi", "sandbox", "ABI"] diff --git a/lib/wasix/Cargo.toml b/lib/wasix/Cargo.toml index d35fef877a1..1460d7f45dd 100644 --- a/lib/wasix/Cargo.toml +++ b/lib/wasix/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-wasix" -version = "0.31.0" +version = "0.32.0" description = "WASI and WASIX implementation library for Wasmer WebAssembly runtime" categories = ["wasm", "os"] keywords = ["wasm", "webassembly", "wasi", "sandbox", "ABI"] @@ -14,7 +14,7 @@ rust-version.workspace = true [dependencies] wasmer-package.workspace = true -wasmer-wasix-types = { path = "../wasi-types", version = "0.31.0", features = [ +wasmer-wasix-types = { path = "../wasi-types", version = "0.32.0", features = [ "enable-serde", ] } wasmer-types = { path = "../types", version = "=5.1.0", default-features = false } @@ -22,14 +22,14 @@ wasmer = { path = "../api", version = "=5.1.0", default-features = false, featur "wat", "js-serializable-module", ] } -virtual-mio = { path = "../virtual-io", version = "0.5.0", default-features = false } -virtual-fs = { path = "../virtual-fs", version = "0.19.0", default-features = false, features = [ +virtual-mio = { path = "../virtual-io", version = "0.6.0", default-features = false } +virtual-fs = { path = "../virtual-fs", version = "0.20.0", default-features = false, features = [ "webc-fs", ] } -virtual-net = { path = "../virtual-net", version = "0.11.0", default-features = false, features = [ +virtual-net = { path = "../virtual-net", version = "0.12.0", default-features = false, features = [ "rkyv", ] } -wasmer-journal = { path = "../journal", version = "0.13.0", default-features = false } +wasmer-journal = { path = "../journal", version = "0.14.0", default-features = false } wasmer-config = { version = "0.10.0", path = "../config" } http.workspace = true diff --git a/lib/wasix/README.md b/lib/wasix/README.md index 052190faf08..482122152ea 100644 --- a/lib/wasix/README.md +++ b/lib/wasix/README.md @@ -8,7 +8,7 @@ is being standardized in the WebAssembly subgroup. Very succinctly, from the user perspective, WASI is a set of WebAssembly module _imports_ under a specific _namespace_ (which varies based on the WASI version). A program compiled for the -`wasm32-wasi` target will be able to support standard I/O, file I/O, +`wasm32-wasip1` target will be able to support standard I/O, file I/O, filesystem manipulation, memory management, time, string, environment variables, program startup etc. @@ -47,7 +47,7 @@ fn main() { Then, let's compile it to a WebAssembly module with WASI support: ```sh -$ rustc --target wasm32-wasi hello.rs +$ rustc --target wasm32-wasip1 hello.rs ``` Finally, let's execute it with the `wasmer` CLI: diff --git a/lib/wasix/src/lib.rs b/lib/wasix/src/lib.rs index 939c13c4fae..1a3550ec838 100644 --- a/lib/wasix/src/lib.rs +++ b/lib/wasix/src/lib.rs @@ -27,7 +27,7 @@ compile_error!("The `sys` feature must be enabled only for non-`wasm32` target." #[cfg(all(feature = "js", not(target_arch = "wasm32")))] compile_error!( - "The `js` feature must be enabled only for the `wasm32` target (either `wasm32-unknown-unknown` or `wasm32-wasi`)." + "The `js` feature must be enabled only for the `wasm32` target (either `wasm32-unknown-unknown` or `wasm32-wasip1`)." ); #[cfg(all(test, target_arch = "wasm32"))] diff --git a/scripts/update-version.py b/scripts/update-version.py index 845395155b7..8aae7528ef0 100644 --- a/scripts/update-version.py +++ b/scripts/update-version.py @@ -48,4 +48,4 @@ def replace_version_iss(path): elif "publish.py" in file: replace_version_py(root + "/" + file) -os.system("cargo generate-lockfile") \ No newline at end of file +os.system("cargo generate-lockfile") diff --git a/tests/compilers/artifact.rs b/tests/compilers/artifact.rs index 53eca021058..1068cccb0d9 100644 --- a/tests/compilers/artifact.rs +++ b/tests/compilers/artifact.rs @@ -1,5 +1,7 @@ use std::{fs, path::PathBuf}; + use wasmer::{Engine, Module}; +use wasmer_types::Features; #[test] fn artifact_serialization_roundtrip() { @@ -24,8 +26,13 @@ fn artifact_serialization_roundtrip() { #[ignore = "Please enable it when tests fail, so we can generate new versions of the .wasmu files"] fn artifact_serialization_build() { use std::str::FromStr; - use wasmer::sys::{get_default_compiler_config, Features, NativeEngineExt}; - use wasmer::{CpuFeature, Target, Triple}; + use wasmer::{ + sys::{ + engine::{get_default_compiler_config, NativeEngineExt}, + CpuFeature, Target, Triple, + }, + Engine, Module, + }; let file_names = ["bash.wasm", "cowsay.wasm", "python-3.11.3.wasm"]; let operating_systems = ["linux", "windows"]; diff --git a/tests/compilers/config.rs b/tests/compilers/config.rs index 96739558762..dcf3386ad62 100644 --- a/tests/compilers/config.rs +++ b/tests/compilers/config.rs @@ -1,6 +1,9 @@ use std::sync::Arc; use wasmer::sys::Features; -use wasmer::{CompilerConfig, ModuleMiddleware, Store}; +use wasmer::{ + sys::{CompilerConfig, ModuleMiddleware}, + Store, +}; #[derive(Clone, Debug, PartialEq, Eq)] pub enum Compiler { diff --git a/tests/compilers/middlewares.rs b/tests/compilers/middlewares.rs index b7687ba4bf2..31c476bc518 100644 --- a/tests/compilers/middlewares.rs +++ b/tests/compilers/middlewares.rs @@ -3,7 +3,7 @@ use anyhow::Result; use std::sync::Arc; use wasmer::wasmparser::Operator; use wasmer::FunctionEnv; -use wasmer::*; +use wasmer::{sys::*, *}; #[derive(Debug)] struct Add2MulGen { diff --git a/tests/compilers/serialize.rs b/tests/compilers/serialize.rs index bc60a2b9376..582c1ec8a72 100644 --- a/tests/compilers/serialize.rs +++ b/tests/compilers/serialize.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use wasmer::*; +use wasmer::{sys::engine::NativeEngineExt, *}; #[test] fn sanity_test_artifact_deserialize() { diff --git a/tests/integration/cli/tests/create_exe.rs b/tests/integration/cli/tests/create_exe.rs index 2fbf782ae33..bbed12bd6cc 100644 --- a/tests/integration/cli/tests/create_exe.rs +++ b/tests/integration/cli/tests/create_exe.rs @@ -398,7 +398,7 @@ fn create_exe_works_underscore_module_name() -> anyhow::Result<()> { let mut create_exe_flags = Vec::new(); for a in atoms.iter() { - let object_path = operating_dir.as_path().join(&format!("{a}.o")); + let object_path = operating_dir.as_path().join(format!("{a}.o")); let _output: Vec = WasmerCreateObj { current_dir: operating_dir.clone(), wasm_path: wasm_path.clone(), diff --git a/tests/integration/cli/tests/publish.rs b/tests/integration/cli/tests/publish.rs index 44393b0afa7..cb18c9fc779 100644 --- a/tests/integration/cli/tests/publish.rs +++ b/tests/integration/cli/tests/publish.rs @@ -116,7 +116,7 @@ fn wasmer_init_publish() { .arg("build") .arg("--release") .arg("--target") - .arg("wasm32-wasi") + .arg("wasm32-wasip1") .arg("--manifest-path") .arg(path.join("randomversion").join("Cargo.toml")) .assert() diff --git a/tests/integration/ios/Cargo.toml b/tests/integration/ios/Cargo.toml index 2dd032b6769..c2778e7e574 100644 --- a/tests/integration/ios/Cargo.toml +++ b/tests/integration/ios/Cargo.toml @@ -9,4 +9,4 @@ publish = false [features] default = ["webc_runner"] -webc_runner = [] \ No newline at end of file +webc_runner = [] diff --git a/tests/lib/wast/Cargo.toml b/tests/lib/wast/Cargo.toml index 080d9ffc964..1c06a28388b 100644 --- a/tests/lib/wast/Cargo.toml +++ b/tests/lib/wast/Cargo.toml @@ -11,10 +11,10 @@ readme = "README.md" edition = "2018" [dependencies] -wasmer-types = { path = "../../../lib/types", version="=5.0.1" } -wasmer-wasix = { path = "../../../lib/wasix", version="=0.31.0" } +wasmer-types = { path = "../../../lib/types", version = "=5.1.0" } +wasmer-wasix = { path = "../../../lib/wasix", version = "=0.32.0" } wasmer = { path = "../../../lib/api", version = "=5.1.0", default-features = false } -virtual-fs = { path = "../../../lib/virtual-fs", version = "0.19.0" } +virtual-fs = { path = "../../../lib/virtual-fs", version = "0.20.0" } anyhow = "1.0" wast = "216.0.0" @@ -23,8 +23,8 @@ tempfile = "3.6.0" thiserror = "1.0" futures = "0.3" tokio = { workspace = true, features = [ - "io-util", - "rt", + "io-util", + "rt", ], default-features = false } [features] diff --git a/tests/wasi-fyi/build.sh b/tests/wasi-fyi/build.sh index d063bb07617..c677c414007 100755 --- a/tests/wasi-fyi/build.sh +++ b/tests/wasi-fyi/build.sh @@ -5,5 +5,5 @@ for input in *.rs; do output="$(basename $input .rs).wasm" echo "Compiling $input" - rustc +nightly --target=wasm32-wasi -o "$output" "$input" + rustc +nightly --target=wasm32-wasip1 -o "$output" "$input" done diff --git a/tests/wasi-wast/src/set_up_toolchain.rs b/tests/wasi-wast/src/set_up_toolchain.rs index 4ff4da5deb1..c4399e17b4d 100644 --- a/tests/wasi-wast/src/set_up_toolchain.rs +++ b/tests/wasi-wast/src/set_up_toolchain.rs @@ -17,7 +17,7 @@ fn install_toolchain(toolchain_name: &str) { let rustup_out = Command::new("rustup") .arg("target") .arg("add") - .arg("wasm32-wasi") + .arg("wasm32-wasip1") .arg("--toolchain") .arg(toolchain_name) .output() diff --git a/tests/wasi-wast/src/wasitests.rs b/tests/wasi-wast/src/wasitests.rs index d01d048c13e..c8d6ae01ad1 100644 --- a/tests/wasi-wast/src/wasitests.rs +++ b/tests/wasi-wast/src/wasitests.rs @@ -159,7 +159,7 @@ fn compile_wasm_for_version( command .arg(format!("+{}", version.get_compiler_toolchain())) - .arg("--target=wasm32-wasi") + .arg("--target=wasm32-wasip1") .arg("-C") .arg("opt-level=z") .arg(&temp_wasi_rs_file_name)