diff --git a/.dockerignore b/.dockerignore index 586a34e..8daaba8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -49,4 +49,4 @@ arbitrator/prover/test-cases/**/* arbitrator/prover/test-cases # external tools and IDEs -.vscode \ No newline at end of file +.vscode diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..891c617 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,27 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 0000000..0cc7db7 --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,2 @@ +name: "Nitro CodeQL config" + diff --git a/.github/workflows/arbitrator-ci.yml b/.github/workflows/arbitrator-ci.yml new file mode 100644 index 0000000..4764601 --- /dev/null +++ b/.github/workflows/arbitrator-ci.yml @@ -0,0 +1,189 @@ +name: Arbitrator CI +run-name: Arbitrator CI triggered from @${{ github.actor }} of ${{ github.head_ref }} + +on: + workflow_dispatch: + inputs: + enable_tmate: + type: boolean + description: 'Enable tmate' + required: false + default: false + merge_group: + pull_request: + paths: + - 'arbitrator/**' + - 'contracts' + - '.github/workflows/arbitrator-ci.yml' + - 'Makefile' + push: + branches: + - master + +env: + RUST_BACKTRACE: 1 +# RUSTFLAGS: -Dwarnings # TODO: re-enable after wasmer upgrade + WABT_VERSION: 1.0.32 + +jobs: + arbitrator: + name: Run Arbitrator tests + runs-on: ubuntu-8 + steps: + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + if: ${{ github.event_name == 'workflow_dispatch' && inputs.enable_tmate }} + with: + detached: true + + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Ubuntu dependencies + run: | + sudo apt-get update && sudo apt-get install -y \ + build-essential cmake lld-14 libudev-dev + sudo ln -s /usr/bin/wasm-ld-14 /usr/local/bin/wasm-ld + + - name: Install go + uses: actions/setup-go@v4 + with: + go-version: 1.23.x + + - name: Install custom go-ethereum + run: | + cd /tmp + git clone --branch v1.14.11 --depth 1 https://github.com/ethereum/go-ethereum.git + cd go-ethereum + go build -o /usr/local/bin/geth ./cmd/geth + + - name: Setup nodejs + uses: actions/setup-node@v3 + with: + node-version: '18' + cache: 'yarn' + cache-dependency-path: '**/yarn.lock' + + - name: Install rust stable + uses: dtolnay/rust-toolchain@stable + with: + toolchain: 'stable' + components: 'llvm-tools-preview, rustfmt, clippy' + + - name: Install rust nightly + uses: dtolnay/rust-toolchain@nightly + id: install-rust-nightly + with: + toolchain: 'nightly-2024-08-06' + targets: 'wasm32-wasi, wasm32-unknown-unknown' + components: 'rust-src, rustfmt, clippy' + + - name: Set STYLUS_NIGHTLY_VER environment variable + run: echo "STYLUS_NIGHTLY_VER=+$(rustup toolchain list | grep '^nightly' | head -n1 | cut -d' ' -f1)" >> "$GITHUB_ENV" + + - name: Cache Rust intermediate build products + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + arbitrator/target/ + arbitrator/wasm-libraries/target/ + key: ${{ runner.os }}-cargo-${{ steps.install-rust.outputs.rustc_hash }}-full-${{ hashFiles('arbitrator/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-${{ steps.install-rust.outputs.rustc_hash }}-full- + ${{ runner.os }}-cargo-${{ steps.install-rust.outputs.rustc_hash }}- + + - name: Cache wabt build + id: cache-wabt + uses: actions/cache@v3 + with: + path: ~/wabt-prefix + key: ${{ runner.os }}-wabt-${{ env.WABT_VERSION }} + + - name: Install latest wabt + if: steps.cache-wabt.outputs.cache-hit != 'true' + run: | + cd "$(mktemp -d)" + git clone --recursive -b "$WABT_VERSION" https://github.com/WebAssembly/wabt . + mkdir build + cd build + mkdir -p ~/wabt-prefix + cmake .. -DCMAKE_INSTALL_PREFIX="$HOME/wabt-prefix" + make -j + make install + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Cache cbrotli + uses: actions/cache@v3 + id: cache-cbrotli + with: + path: | + target/include/brotli/ + target/lib-wasm/ + target/lib/libbrotlicommon-static.a + target/lib/libbrotlienc-static.a + target/lib/libbrotlidec-static.a + key: ${{ runner.os }}-brotli-3-${{ hashFiles('scripts/build-brotli.sh') }}-${{ hashFiles('.github/workflows/arbitrator-ci.yaml') }} + restore-keys: ${{ runner.os }}-brotli-2- + + - name: Build cbrotli-local + if: steps.cache-cbrotli.outputs.cache-hit != 'true' + run: ./scripts/build-brotli.sh -l + + - name: Setup emsdk + if: steps.cache-cbrotli.outputs.cache-hit != 'true' + uses: mymindstorm/setup-emsdk@v12 + with: + # Make sure to set a version number! + version: 3.1.6 + # This is the name of the cache folder. + # The cache folder will be placed in the build directory, + # so make sure it doesn't conflict with anything! + actions-cache-folder: 'emsdk-cache' + no-cache: true + + - name: Build cbrotli-wasm + if: steps.cache-cbrotli.outputs.cache-hit != 'true' + run: ./scripts/build-brotli.sh -w + + - name: Add wabt to path + run: echo "$HOME/wabt-prefix/bin" >> "$GITHUB_PATH" + + - name: Make arbitrator libraries + run: make -j wasm-ci-build + + - name: Clippy check + run: cargo clippy --all --manifest-path arbitrator/Cargo.toml -- -D warnings + + - name: Run rust tests + run: cargo test -p arbutil -p prover -p jit -p stylus --release --manifest-path arbitrator/prover/Cargo.toml + + - name: Rustfmt + run: cargo fmt -p arbutil -p prover -p jit -p stylus --manifest-path arbitrator/Cargo.toml -- --check + + - name: Rustfmt - langs/rust + run: cargo fmt --all --manifest-path arbitrator/langs/rust/Cargo.toml -- --check + + - name: Make proofs from test cases + run: make -j test-gen-proofs + + - name: Start geth server + run: | + geth --dev --http --http.port 8545 & + sleep 2 + + - name: Run proof validation tests + run: | + npm install --global yarn + cd contracts + yarn install + yarn build + yarn build:forge:yul + yarn hardhat --network localhost test test/prover/*.ts diff --git a/.github/workflows/arbitrator-skip-ci.yml b/.github/workflows/arbitrator-skip-ci.yml new file mode 100644 index 0000000..10c9bf9 --- /dev/null +++ b/.github/workflows/arbitrator-skip-ci.yml @@ -0,0 +1,21 @@ +name: Arbitrator skip CI +run-name: Arbitrator skip CI triggered from @${{ github.actor }} of ${{ github.head_ref }} + +on: + merge_group: + pull_request: + paths-ignore: + - 'arbitrator/**' + - 'contracts/src/osp/**' + - 'contracts/src/mock/**' + - 'contracts/test/**' + - 'contracts/hardhat.config.ts' + - 'Makefile' + +jobs: + arbitrator: + name: Run Arbitrator tests + runs-on: ubuntu-latest + steps: + - name: Do nothing + run: echo "doing nothing" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a944f08 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,198 @@ +name: Go tests CI +run-name: Go tests CI triggered from @${{ github.actor }} of ${{ github.head_ref }} + +on: + workflow_dispatch: + merge_group: + pull_request: + push: + branches: + - master + - develop + +jobs: + test: + name: Go Tests + runs-on: ubuntu-8 + + # Creates a redis container for redis tests + services: + redis: + image: redis + ports: + - 6379:6379 + + strategy: + fail-fast: false + matrix: + test-mode: [defaults, race, challenge, stylus, long] + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + + - name: Install dependencies + run: sudo apt update && sudo apt install -y wabt gotestsum + + - name: Setup nodejs + uses: actions/setup-node@v3 + with: + node-version: '18' + cache: 'yarn' + cache-dependency-path: '**/yarn.lock' + + - name: Install go + uses: actions/setup-go@v4 + with: + go-version: 1.23.x + + - name: Install wasm-ld + run: | + sudo apt-get update && sudo apt-get install -y lld-14 + sudo ln -s /usr/bin/wasm-ld-14 /usr/local/bin/wasm-ld + + - name: Install rust stable + uses: dtolnay/rust-toolchain@stable + with: + toolchain: 'stable' + targets: 'wasm32-wasi, wasm32-unknown-unknown' + components: 'llvm-tools-preview, rustfmt, clippy' + + - name: Install rust nightly + uses: dtolnay/rust-toolchain@nightly + id: install-rust-nightly + with: + toolchain: 'nightly-2024-08-06' + targets: 'wasm32-wasi, wasm32-unknown-unknown' + components: 'rust-src, rustfmt, clippy' + + - name: Set STYLUS_NIGHTLY_VER environment variable + run: echo "STYLUS_NIGHTLY_VER=+$(rustup toolchain list | grep '^nightly' | head -n1 | cut -d' ' -f1)" >> "$GITHUB_ENV" + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Cache Build Products + uses: actions/cache@v3 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}-${{ matrix.test-mode }} + restore-keys: ${{ runner.os }}-go- + + - name: Cache Rust Build Products + uses: actions/cache@v3 + with: + path: | + ~/.cargo/ + arbitrator/target/ + arbitrator/wasm-libraries/target/ + arbitrator/wasm-libraries/soft-float/ + target/etc/initial-machine-cache/ + /home/runner/.rustup/toolchains/ + key: ${{ runner.os }}-cargo-${{ steps.install-rust.outputs.rustc_hash }}-min-${{ hashFiles('arbitrator/Cargo.lock') }}-${{ matrix.test-mode }} + restore-keys: ${{ runner.os }}-cargo-${{ steps.install-rust.outputs.rustc_hash }}- + + - name: Cache cbrotli + uses: actions/cache@v3 + id: cache-cbrotli + with: + path: | + target/include/brotli/ + target/lib-wasm/ + target/lib/libbrotlicommon-static.a + target/lib/libbrotlienc-static.a + target/lib/libbrotlidec-static.a + key: ${{ runner.os }}-brotli-${{ hashFiles('scripts/build-brotli.sh') }}-${{ hashFiles('.github/workflows/arbitrator-ci.yaml') }}-${{ matrix.test-mode }} + restore-keys: ${{ runner.os }}-brotli- + + - name: Build cbrotli-local + if: steps.cache-cbrotli.outputs.cache-hit != 'true' + run: ./scripts/build-brotli.sh -l + + - name: Build cbrotli-wasm in docker + if: steps.cache-cbrotli.outputs.cache-hit != 'true' + run: ./scripts/build-brotli.sh -w -d + + - name: Build + run: make build test-go-deps -j + + - name: Build all lint dependencies + run: make -j build-node-deps + + - name: Lint + uses: golangci/golangci-lint-action@v3 + with: + version: latest + skip-pkg-cache: true + - name: Custom Lint + run: | + go run ./linters ./... + + - name: Set environment variables + run: | + mkdir -p target/tmp/deadbeefbee + echo "TMPDIR=$(pwd)/target/tmp/deadbeefbee" >> "$GITHUB_ENV" + echo "GOMEMLIMIT=6GiB" >> "$GITHUB_ENV" + echo "GOGC=80" >> "$GITHUB_ENV" + echo "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> "$GITHUB_ENV" + + - name: run tests without race detection and path state scheme + if: matrix.test-mode == 'defaults' + env: + TEST_STATE_SCHEME: path + run: | + echo "Running tests with Path Scheme" >> full.log + ${{ github.workspace }}/.github/workflows/gotestsum.sh --tags cionly --timeout 20m --cover + + - name: run tests without race detection and hash state scheme + if: matrix.test-mode == 'defaults' + env: + TEST_STATE_SCHEME: hash + run: | + echo "Running tests with Hash Scheme" >> full.log + ${{ github.workspace }}/.github/workflows/gotestsum.sh --tags cionly --timeout 20m + + - name: run tests with race detection and hash state scheme + if: matrix.test-mode == 'race' + env: + TEST_STATE_SCHEME: hash + run: | + echo "Running tests with Hash Scheme" >> full.log + ${{ github.workspace }}/.github/workflows/gotestsum.sh --race --timeout 30m + + - name: run redis tests + if: matrix.test-mode == 'defaults' + run: | + echo "Running redis tests" >> full.log + TEST_REDIS=redis://localhost:6379/0 gotestsum --format short-verbose -- -p 1 -run TestRedis ./arbnode/... ./system_tests/... -coverprofile=coverage-redis.txt -covermode=atomic -coverpkg=./... + + - name: run challenge tests + if: matrix.test-mode == 'challenge' + run: ${{ github.workspace }}/.github/workflows/gotestsum.sh --tags challengetest --run TestChallenge --cover + + - name: run stylus tests + if: matrix.test-mode == 'stylus' + run: ${{ github.workspace }}/.github/workflows/gotestsum.sh --tags stylustest --run TestProgramArbitrator --timeout 60m --cover + + - name: run long stylus tests + if: matrix.test-mode == 'long' + run: ${{ github.workspace }}/.github/workflows/gotestsum.sh --tags stylustest --run TestProgramLong --timeout 60m --cover + + - name: Archive detailed run log + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.test-mode }}-full.log + path: full.log + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v2 + if: matrix.test-mode == 'defaults' + with: + fail_ci_if_error: false + files: ./coverage.txt,./coverage-redis.txt + verbose: false + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..2644794 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,144 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "master" ] + merge_group: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '18 21 * * 5' + +jobs: + analyze: + name: Analyze + if: github.repository == 'OffchainLabs/nitro' # don't run in any forks without "Advanced Security" enabled + runs-on: ubuntu-8 + permissions: + actions: read + contents: read + security-events: write + env: + WABT_VERSION: 1.0.32 + + strategy: + fail-fast: false + matrix: + language: [ 'go' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + + - name: Install dependencies + run: sudo apt update && sudo apt install -y wabt + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + config-file: ./.github/codeql/codeql-config.yml + + - name: Setup nodejs + uses: actions/setup-node@v3 + with: + node-version: '18' + cache: 'yarn' + cache-dependency-path: '**/yarn.lock' + + - name: Install go + uses: actions/setup-go@v4 + with: + go-version: 1.23.x + + - name: Install rust stable + uses: dtolnay/rust-toolchain@stable + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Cache Rust Build Products + uses: actions/cache@v3 + with: + path: | + ~/.cargo/registry/ + ~/.cargo/git/ + arbitrator/target/ + arbitrator/wasm-libraries/target/ + arbitrator/wasm-libraries/soft-float/SoftFloat/build + target/etc/initial-machine-cache/ + key: ${{ runner.os }}-cargo-${{ steps.install-rust.outputs.rustc_hash }}-min-${{ hashFiles('arbitrator/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo-${{ steps.install-rust.outputs.rustc_hash }}- + + - name: Cache wabt build + id: cache-wabt + uses: actions/cache@v3 + with: + path: ~/wabt-prefix + key: ${{ runner.os }}-wabt-codeql-${{ env.WABT_VERSION }} + + - name: Cache cbrotli + uses: actions/cache@v3 + id: cache-cbrotli + with: + path: | + target/include/brotli/ + target/lib-wasm/ + target/lib/libbrotlicommon-static.a + target/lib/libbrotlienc-static.a + target/lib/libbrotlidec-static.a + key: ${{ runner.os }}-brotli-3a-${{ hashFiles('scripts/build-brotli.sh') }}-${{ hashFiles('.github/workflows/arbitrator-ci.yaml') }} + restore-keys: ${{ runner.os }}-brotli- + + - name: Build cbrotli-local + if: steps.cache-cbrotli.outputs.cache-hit != 'true' + run: ./scripts/build-brotli.sh -l + + - name: Cache Build Products + uses: actions/cache@v3 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-go- + + - name: Build all lint dependencies + run: make -j build-node-deps + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..2aacf32 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,104 @@ +name: Docker build CI +run-name: Docker build CI triggered from @${{ github.actor }} of ${{ github.head_ref }} + +on: + workflow_dispatch: + merge_group: + pull_request: + push: + branches: + - master + - develop + +jobs: + docker: + name: Docker build + runs-on: ubuntu-8 + services: + # local registery + registry: + image: registry:2 + ports: + - 5000:5000 + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: network=host + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ hashFiles('Dockerfile') }} + restore-keys: ${{ runner.os }}-buildx- + + - name: Build nitro-node docker + uses: docker/build-push-action@v5 + with: + target: nitro-node + push: true + context: . + tags: localhost:5000/nitro-node:latest + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max + + - name: Build nitro-node-dev docker + uses: docker/build-push-action@v5 + with: + target: nitro-node-dev + push: true + context: . + tags: localhost:5000/nitro-node-dev:latest + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max + + - name: Start background nitro-testnode + shell: bash + run: | + cd nitro-testnode + ./test-node.bash --init --dev & + + - name: Wait for rpc to come up + shell: bash + run: | + ${{ github.workspace }}/.github/workflows/waitForNitro.sh + + - name: Print WAVM module root + id: module-root + run: | + # Unfortunately, `docker cp` seems to always result in a "permission denied" + # We work around this by piping a tarball through stdout + docker run --rm --entrypoint tar localhost:5000/nitro-node-dev:latest -cf - target/machines/latest | tar xf - + module_root="$(cat "target/machines/latest/module-root.txt")" + echo "module-root=$module_root" >> "$GITHUB_OUTPUT" + echo -e "\x1b[1;34mWAVM module root:\x1b[0m $module_root" + + - name: Upload WAVM machine as artifact + uses: actions/upload-artifact@v3 + with: + name: wavm-machine-${{ steps.module-root.outputs.module-root }} + path: target/machines/latest/* + if-no-files-found: error + + - name: Move cache + # Temp fix + # https://github.com/docker/build-push-action/issues/252 + # https://github.com/moby/buildkit/issues/1896 + run: | + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache + + - name: Clear cache on failure + if: failure() + run: | + keys=(${{ runner.os }}-buildx- ${{ runner.os }}-buildx-${{ hashFiles('Dockerfile') }}) + for key in "${keys[@]}"; do + curl -X DELETE -H "Accept: application/vnd.github.v3+json" -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" "https://api.github.com/repos/${{ github.repository }}/actions/caches/$key" + done diff --git a/.github/workflows/gotestsum.sh b/.github/workflows/gotestsum.sh new file mode 100755 index 0000000..ed63184 --- /dev/null +++ b/.github/workflows/gotestsum.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +check_missing_value() { + if [[ $1 -eq 0 || $2 == -* ]]; then + echo "missing $3 argument value" + exit 1 + fi +} + +timeout="" +tags="" +run="" +race=false +cover=false +while [[ $# -gt 0 ]]; do + case $1 in + --timeout) + shift + check_missing_value $# "$1" "--timeout" + timeout=$1 + shift + ;; + --tags) + shift + check_missing_value $# "$1" "--tags" + tags=$1 + shift + ;; + --run) + shift + check_missing_value $# "$1" "--run" + run=$1 + shift + ;; + --race) + race=true + shift + ;; + --cover) + cover=true + shift + ;; + *) + echo "Invalid argument: $1" + exit 1 + ;; + esac +done + +packages=$(go list ./...) +for package in $packages; do + cmd="stdbuf -oL gotestsum --format short-verbose --packages=\"$package\" --rerun-fails=2 --no-color=false --" + + if [ "$timeout" != "" ]; then + cmd="$cmd -timeout $timeout" + fi + + if [ "$tags" != "" ]; then + cmd="$cmd -tags=$tags" + fi + + if [ "$run" != "" ]; then + cmd="$cmd -run=$run" + fi + + if [ "$race" == true ]; then + cmd="$cmd -race" + fi + + if [ "$cover" == true ]; then + cmd="$cmd -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,./go-ethereum/..." + fi + + cmd="$cmd > >(stdbuf -oL tee -a full.log | grep -vE \"INFO|seal\")" + + echo "" + echo running tests for "$package" + echo "$cmd" + + if ! eval "$cmd"; then + exit 1 + fi +done diff --git a/.github/workflows/merge-checks.yml b/.github/workflows/merge-checks.yml new file mode 100644 index 0000000..c9f7957 --- /dev/null +++ b/.github/workflows/merge-checks.yml @@ -0,0 +1,42 @@ +name: Merge Checks + +on: + pull_request_target: + branches: [ master ] + types: [synchronize, opened, reopened, labeled, unlabeled] + +permissions: + statuses: write + +jobs: + check-design-approved: + name: Check if Design Approved + runs-on: ubuntu-latest + steps: + - name: Check if design approved and update status + run: | + set -x pipefail + status_state="pending" + if ${{ contains(github.event.pull_request.labels.*.name, 'design-approved') && !contains(github.event.pull_request.labels.*.name, 'after-next-version') }}; then + status_state="success" + else + resp="$(curl -sSL --fail-with-body \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/$GITHUB_REPOSITORY/commits/${{ github.event.pull_request.head.sha }}/statuses")" + if ! jq -e '.[] | select(.context == "Design Approved Check")' > /dev/null <<< "$resp"; then + # Design not approved yet and no status exists + # Keep it without a status to keep the green checkmark appearing + # Otherwise, the commit and PR's CI will appear to be indefinitely pending + # Merging will still be blocked until the required status appears + exit 0 + fi + fi + curl -sSL --fail-with-body \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/$GITHUB_REPOSITORY/statuses/${{ github.event.pull_request.head.sha }}" \ + -d '{"context":"Design Approved Check","state":"'"$status_state"'"}' diff --git a/.github/workflows/release-ci.yml b/.github/workflows/release-ci.yml new file mode 100644 index 0000000..5282510 --- /dev/null +++ b/.github/workflows/release-ci.yml @@ -0,0 +1,30 @@ +name: Release CI +run-name: Release CI triggered from @${{ github.actor }} of ${{ github.head_ref }} + +on: + workflow_dispatch: + +jobs: + build_and_run: + runs-on: ubuntu-8 + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: network=host + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ hashFiles('Dockerfile') }} + restore-keys: ${{ runner.os }}-buildx- + + - name: Startup Nitro testnode + run: ./scripts/startup-testnode.bash diff --git a/.github/workflows/shellcheck-ci.yml b/.github/workflows/shellcheck-ci.yml new file mode 100644 index 0000000..d1c7b58 --- /dev/null +++ b/.github/workflows/shellcheck-ci.yml @@ -0,0 +1,30 @@ +name: ShellCheck CI +run-name: ShellCheck CI triggered from @${{ github.actor }} of ${{ github.head_ref }} + +on: + workflow_dispatch: + merge_group: + pull_request: + push: + branches: + - master + +jobs: + shellcheck: + name: Run ShellCheck + runs-on: ubuntu-8 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@master + with: + ignore_paths: >- + ./fastcache/** + ./contracts/** + ./safe-smart-account/** + ./go-ethereum/** + ./nitro-testnode/** + ./brotli/** + ./arbitrator/** diff --git a/.github/workflows/submodule-pin-check.yml b/.github/workflows/submodule-pin-check.yml new file mode 100644 index 0000000..90419b5 --- /dev/null +++ b/.github/workflows/submodule-pin-check.yml @@ -0,0 +1,69 @@ +name: Merge Checks + +on: + pull_request_target: + branches: [ master ] + types: [synchronize, opened, reopened] + +permissions: + statuses: write + +jobs: + submodule-pin-check: + name: Check Submodule Pin + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: true + ref: "${{ github.event.pull_request.merge_commit_sha }}" + + - name: Check all submodules are ancestors of origin/HEAD or configured branch + run: | + status_state="pending" + declare -Ar exceptions=( + [contracts]=origin/develop + [nitro-testnode]=origin/master + + #TODO Rachel to check these are the intended branches. + [arbitrator/langs/c]=origin/vm-storage-cache + [arbitrator/tools/wasmer]=origin/adopt-v4.2.8 + ) + divergent=0 + for mod in `git submodule --quiet foreach 'echo $name'`; do + branch=origin/HEAD + if [[ -v exceptions[$mod] ]]; then + branch=${exceptions[$mod]} + fi + + if ! git -C $mod merge-base --is-ancestor HEAD $branch; then + echo $mod diverges from $branch + divergent=1 + fi + done + if [ $divergent -eq 0 ]; then + status_state="success" + else + resp="$(curl -sSL --fail-with-body \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/$GITHUB_REPOSITORY/commits/${{ github.event.pull_request.head.sha }}/statuses")" + + if ! jq -e '.[] | select(.context == "Submodule Pin Check")' > /dev/null <<< "$resp"; then + # Submodule pin check is failling and no status exists + # Keep it without a status to keep the green checkmark appearing + # Otherwise, the commit and PR's CI will appear to be indefinitely pending + # Merging will still be blocked until the required status appears + exit 0 + fi + fi + curl -sSL --fail-with-body \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/$GITHUB_REPOSITORY/statuses/${{ github.event.pull_request.head.sha }}" \ + -d '{"context":"Submodule Pin Check","state":"'"$status_state"'"}' diff --git a/.github/workflows/waitForNitro.sh b/.github/workflows/waitForNitro.sh new file mode 100755 index 0000000..cf3f648 --- /dev/null +++ b/.github/workflows/waitForNitro.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# poll the nitro endpoint until we get a 0 return code or 30mins have passed, in that case exit 1 +timeout_time=$(($(date +%s) + 1800)) + +while (( $(date +%s) <= timeout_time )); do + if curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":45678,"method":"eth_chainId","params":[]}' 'http://localhost:8547'; then + exit 0 + else + sleep 20 + fi +done + +exit 1 \ No newline at end of file diff --git a/.gitignore b/.gitignore index d6b9398..b94d61e 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,3 @@ system_tests/test-data/* .configs/ system_tests/testdata/* arbos/testdata/* -keys/* diff --git a/.gitmodules b/.gitmodules index a120835..28a725c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,18 +1,18 @@ +[submodule "contracts"] + path = contracts + url = https://github.com/OffchainLabs/nitro-contracts [submodule "fastcache"] path = fastcache url = https://github.com/OffchainLabs/fastcache -[submodule "nitro-testnode"] - path = nitro-testnode - url = https://github.com/OffchainLabs/nitro-testnode [submodule "fhevm-go"] path = fhevm-go url = git@github.com:z1labs/Cypher-fhEVM-Go.git -[submodule "go-ethereum"] - path = go-ethereum - url = git@github.com:z1labs/Cypher-Go-Ethereum.git +[submodule "nitro-testnode"] + path = nitro-testnode + url = https://github.com/OffchainLabs/nitro-testnode [submodule "safe-smart-account"] path = safe-smart-account url = https://github.com/safe-global/safe-smart-account.git -[submodule "contracts"] - path = contracts - url = https://github.com/OffchainLabs/nitro-contracts +[submodule "go-ethereum"] + path = go-ethereum + url = git@github.com:z1labs/Cypher-Go-Ethereum.git diff --git a/.golangci.yml b/.golangci.yml index fe90dd2..0594670 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -44,7 +44,6 @@ linters-settings: gosec: excludes: - G404 # checks that random numbers are securely generated - - G402 govet: enable-all: true diff --git a/Dockerfile b/Dockerfile index b5af904..25bcd51 100644 --- a/Dockerfile +++ b/Dockerfile @@ -235,7 +235,7 @@ COPY ./scripts/download-machine.sh . #RUN ./download-machine.sh consensus-v20 0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4 RUN ./download-machine.sh consensus-v30 0xb0de9cb89e4d944ae6023a3b62276e54804c242fd8c4c2d8e6cc4450f5fa8b1b && true RUN ./download-machine.sh consensus-v31 0x260f5fa5c3176a856893642e149cf128b5a8de9f828afec8d11184415dd8dc69 - +RUN ./download-machine.sh consensus-v32 0x184884e1eb9fefdc158f6c8ac912bb183bf3cf83f0090317e0bc4ac5860baa39 FROM rust-base AS tfhe-builder WORKDIR /workspace @@ -265,18 +265,20 @@ ARG modified="" ENV NITRO_VERSION=$version ENV NITRO_DATETIME=$datetime ENV NITRO_MODIFIED=$modified + RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ apt-get install -y wabt binutils binutils-gold gcc g++ - COPY go.mod go.sum ./ COPY go-ethereum/go.mod go-ethereum/go.sum go-ethereum/ COPY fastcache/go.mod fastcache/go.sum fastcache/ COPY fhevm-go/go.mod fhevm-go/go.sum fhevm-go/ RUN go mod download - COPY . ./ +RUN go run solgen/gen.go +RUN ls -R solgen/go/ + COPY --from=contracts-builder /workspace/contracts/build/contracts/src/precompiles/ contracts/build/contracts/src/precompiles/ COPY --from=contracts-builder /workspace/contracts/out/ contracts/out/ diff --git a/LICENSE.md b/LICENSE.md index ea9a53d..25768b3 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -22,7 +22,7 @@ Additional Use Grant: You may use the Licensed Work in a production environment Expansion Program Term of Use](https://docs.arbitrum.foundation/assets/files/Arbitrum%20Expansion%20Program%20Jan182024-4f08b0c2cb476a55dc153380fa3e64b0.pdf). For purposes of this Additional Use Grant, the "Covered Arbitrum Chains" are (a) Arbitrum One (chainid:42161), Arbitrum Nova (chainid:42170), - rbitrum Rinkeby testnet/Rinkarby (chainid:421611),Arbitrum Nitro + Arbitrum Rinkeby testnet/Rinkarby (chainid:421611),Arbitrum Nitro Goerli testnet (chainid:421613), and Arbitrum Sepolia Testnet (chainid:421614); (b) any future blockchains authorized to be designated as Covered Arbitrum Chains by the decentralized autonomous diff --git a/README.md b/README.md index f825da0..56c1cc0 100644 --- a/README.md +++ b/README.md @@ -246,4 +246,4 @@ Check the documetation for a step by step guide how to run own [Build and deploy smart contracts]: -[Use Metamask Cypher snap to for an easy access to decryption service]: +[Use Metamask Cypher snap to for an easy access to decryption service]: \ No newline at end of file diff --git a/arbcompress/compress_common.go b/arbcompress/compress_common.go index a61dd9a..997232e 100644 --- a/arbcompress/compress_common.go +++ b/arbcompress/compress_common.go @@ -17,6 +17,8 @@ func compressedBufferSizeFor(length int) int { return length + (length>>10)*8 + 64 // actual limit is: length + (length >> 14) * 4 + 6 } -func CompressLevel(input []byte, level int) ([]byte, error) { +func CompressLevel(input []byte, level uint64) ([]byte, error) { + // level is trusted and shouldn't be anything crazy + // #nosec G115 return Compress(input, uint32(level), EmptyDictionary) } diff --git a/arbcompress/native.go b/arbcompress/native.go index 8244010..f7b8f0b 100644 --- a/arbcompress/native.go +++ b/arbcompress/native.go @@ -7,7 +7,7 @@ package arbcompress /* -#cgo CFLAGS: -g -Wall -I${SRCDIR}/../target/include/ +#cgo CFLAGS: -g -I${SRCDIR}/../target/include/ #cgo LDFLAGS: ${SRCDIR}/../target/lib/libstylus.a -lm #include "arbitrator.h" */ diff --git a/arbitrator/Cargo.lock b/arbitrator/Cargo.lock index 79a9117..8644114 100644 --- a/arbitrator/Cargo.lock +++ b/arbitrator/Cargo.lock @@ -215,7 +215,6 @@ dependencies = [ "prover", "serde", "serde_json", - "serde_with 3.9.0", ] [[package]] @@ -496,6 +495,12 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +[[package]] +name = "clru" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd0f76e066e64fdc5631e3bb46381254deab9ef1158292f27c8c57e3bf3fe59" + [[package]] name = "colorchoice" version = "1.0.2" @@ -705,38 +710,14 @@ dependencies = [ "typenum", ] -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", -] - [[package]] name = "darling" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core 0.20.10", - "darling_macro 0.20.10", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", + "darling_core", + "darling_macro", ] [[package]] @@ -753,24 +734,13 @@ dependencies = [ "syn 2.0.72", ] -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core 0.13.4", - "quote", - "syn 1.0.109", -] - [[package]] name = "darling_macro" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "darling_core 0.20.10", + "darling_core", "quote", "syn 2.0.72", ] @@ -928,7 +898,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" dependencies = [ - "darling 0.20.10", + "darling", "proc-macro2", "quote", "syn 2.0.72", @@ -1289,9 +1259,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libfuzzer-sys" @@ -1750,7 +1720,7 @@ dependencies = [ "rustc-demangle", "serde", "serde_json", - "serde_with 1.14.0", + "serde_with", "sha2 0.9.9", "sha3 0.9.1", "smallvec", @@ -2073,16 +2043,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_with" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" -dependencies = [ - "serde", - "serde_with_macros 1.5.2", -] - [[package]] name = "serde_with" version = "3.9.0" @@ -2097,29 +2057,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "serde_with_macros 3.9.0", + "serde_with_macros", "time", ] -[[package]] -name = "serde_with_macros" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" -dependencies = [ - "darling 0.13.4", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "serde_with_macros" version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" dependencies = [ - "darling 0.20.10", + "darling", "proc-macro2", "quote", "syn 2.0.72", @@ -2226,12 +2174,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strsim" version = "0.11.1" @@ -2270,13 +2212,13 @@ dependencies = [ "bincode", "brotli", "caller-env", + "clru", "derivative", "eyre", "fnv", "hex", "lazy_static", "libc", - "lru", "num-bigint", "parking_lot", "prover", diff --git a/arbitrator/Cargo.toml b/arbitrator/Cargo.toml index 94ca08b..eaafb6e 100644 --- a/arbitrator/Cargo.toml +++ b/arbitrator/Cargo.toml @@ -24,9 +24,7 @@ repository = "https://github.com/OffchainLabs/nitro.git" rust-version = "1.67" [workspace.dependencies] -cfg-if = "1.0.0" lazy_static = "1.4.0" -lru = "0.12.3" num_enum = { version = "0.7.2", default-features = false } ruint2 = "1.9.0" wasmparser = "0.121" diff --git a/arbitrator/arbutil/src/evm/api.rs b/arbitrator/arbutil/src/evm/api.rs index 093e7f2..9d4c78c 100644 --- a/arbitrator/arbutil/src/evm/api.rs +++ b/arbitrator/arbutil/src/evm/api.rs @@ -77,7 +77,7 @@ pub trait EvmApi: Send + 'static { /// Reads the 32-byte value in the EVM state trie at offset `key`. /// Returns the value and the access cost in gas. /// Analogous to `vm.SLOAD`. - fn get_bytes32(&mut self, key: Bytes32) -> (Bytes32, u64); + fn get_bytes32(&mut self, key: Bytes32, evm_api_gas_to_use: u64) -> (Bytes32, u64); /// Stores the given value at the given key in Stylus VM's cache of the EVM state trie. /// Note that the actual values only get written after calls to `set_trie_slots`. diff --git a/arbitrator/arbutil/src/evm/mod.rs b/arbitrator/arbutil/src/evm/mod.rs index 1671e67..36dadd9 100644 --- a/arbitrator/arbutil/src/evm/mod.rs +++ b/arbitrator/arbutil/src/evm/mod.rs @@ -74,9 +74,12 @@ pub const GASPRICE_GAS: u64 = GAS_QUICK_STEP; // vm.GasQuickStep (see jump_table.go) pub const ORIGIN_GAS: u64 = GAS_QUICK_STEP; +pub const ARBOS_VERSION_STYLUS_CHARGING_FIXES: u64 = 32; + #[derive(Clone, Copy, Debug, Default)] #[repr(C)] pub struct EvmData { + pub arbos_version: u64, pub block_basefee: Bytes32, pub chainid: u64, pub block_coinbase: Bytes20, diff --git a/arbitrator/arbutil/src/evm/req.rs b/arbitrator/arbutil/src/evm/req.rs index b1c8d99..0304f2d 100644 --- a/arbitrator/arbutil/src/evm/req.rs +++ b/arbitrator/arbutil/src/evm/req.rs @@ -7,8 +7,6 @@ use crate::{ storage::{StorageCache, StorageWord}, user::UserOutcomeKind, }, - format::Utf8OrHex, - pricing::EVM_API_INK, Bytes20, Bytes32, }; use eyre::{bail, eyre, Result}; @@ -100,13 +98,13 @@ impl> EvmApiRequestor { } impl> EvmApi for EvmApiRequestor { - fn get_bytes32(&mut self, key: Bytes32) -> (Bytes32, u64) { + fn get_bytes32(&mut self, key: Bytes32, evm_api_gas_to_use: u64) -> (Bytes32, u64) { let cache = &mut self.storage_cache; let mut cost = cache.read_gas(); let value = cache.entry(key).or_insert_with(|| { let (res, _, gas) = self.handler.request(EvmApiMethod::GetBytes32, key); - cost = cost.saturating_add(gas).saturating_add(EVM_API_INK); + cost = cost.saturating_add(gas).saturating_add(evm_api_gas_to_use); StorageWord::known(res.try_into().unwrap()) }); (value.value, cost) @@ -140,8 +138,13 @@ impl> EvmApi for EvmApiRequestor { } let (res, _, cost) = self.request(EvmApiMethod::SetTrieSlots, data); - if res[0] != EvmApiStatus::Success.into() { - bail!("{}", String::from_utf8_or_hex(res)); + let status = res + .first() + .copied() + .map(EvmApiStatus::from) + .unwrap_or(EvmApiStatus::Failure); + if status != EvmApiStatus::Success { + bail!("{:?}", status); } Ok(cost) } @@ -156,8 +159,13 @@ impl> EvmApi for EvmApiRequestor { data.extend(key); data.extend(value); let (res, ..) = self.request(EvmApiMethod::SetTransientBytes32, data); - if res[0] != EvmApiStatus::Success.into() { - bail!("{}", String::from_utf8_or_hex(res)); + let status = res + .first() + .copied() + .map(EvmApiStatus::from) + .unwrap_or(EvmApiStatus::Failure); + if status != EvmApiStatus::Success { + bail!("{:?}", status); } Ok(()) } @@ -290,9 +298,10 @@ impl> EvmApi for EvmApiRequestor { let mut request = Vec::with_capacity(2 * 8 + 3 * 2 + name.len() + args.len() + outs.len()); request.extend(start_ink.to_be_bytes()); request.extend(end_ink.to_be_bytes()); - request.extend((name.len() as u16).to_be_bytes()); - request.extend((args.len() as u16).to_be_bytes()); - request.extend((outs.len() as u16).to_be_bytes()); + // u32 is enough to represent the slices lengths because the WASM environment runs in 32 bits. + request.extend((name.len() as u32).to_be_bytes()); + request.extend((args.len() as u32).to_be_bytes()); + request.extend((outs.len() as u32).to_be_bytes()); request.extend(name.as_bytes()); request.extend(args); request.extend(outs); diff --git a/arbitrator/arbutil/src/types.rs b/arbitrator/arbutil/src/types.rs index 6cf1d6c..722a89b 100644 --- a/arbitrator/arbutil/src/types.rs +++ b/arbitrator/arbutil/src/types.rs @@ -8,6 +8,7 @@ use std::{ borrow::Borrow, fmt, ops::{Deref, DerefMut}, + str::FromStr, }; // These values must be kept in sync with `arbutil/preimage_type.go`, @@ -83,6 +84,32 @@ impl From for Bytes32 { } } +impl FromStr for Bytes32 { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + // Remove the "0x" prefix if present + let s = s.strip_prefix("0x").unwrap_or(s); + + // Pad with leading zeros if the string is shorter than 64 characters (32 bytes) + let padded = format!("{:0>64}", s); + + // Decode the hex string using the hex crate + let decoded_bytes = hex::decode(padded).map_err(|_| "Invalid hex string")?; + + // Ensure the decoded bytes is exactly 32 bytes + if decoded_bytes.len() != 32 { + return Err("Hex string too long for Bytes32"); + } + + // Create a 32-byte array and fill it with the decoded bytes. + let mut b = [0u8; 32]; + b.copy_from_slice(&decoded_bytes); + + Ok(Bytes32(b)) + } +} + impl TryFrom<&[u8]> for Bytes32 { type Error = std::array::TryFromSliceError; @@ -249,3 +276,77 @@ impl From for Bytes20 { <[u8; 20]>::from(x).into() } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_bytes32() { + let b = Bytes32::from(0x12345678u32); + let expected = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x12, 0x34, 0x56, 0x78, + ]; + assert_eq!(b, Bytes32(expected)); + } + + #[test] + fn test_from_str_short() { + // Short hex string + let b = Bytes32::from_str("0x12345678").unwrap(); + let expected = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x12, 0x34, 0x56, 0x78, + ]; + assert_eq!(b, Bytes32(expected)); + } + + #[test] + fn test_from_str_very_short() { + // Short hex string + let b = Bytes32::from_str("0x1").unwrap(); + let expected = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0x1, + ]; + assert_eq!(b, Bytes32(expected)); + } + + #[test] + fn test_from_str_no_prefix() { + // Short hex string + let b = Bytes32::from_str("12345678").unwrap(); + let expected = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x12, 0x34, 0x56, 0x78, + ]; + assert_eq!(b, Bytes32(expected)); + } + + #[test] + fn test_from_str_full() { + // Full-length hex string + let b = + Bytes32::from_str("0x0000000000000000000000000000000000000000000000000000000012345678") + .unwrap(); + let expected = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x12, 0x34, 0x56, 0x78, + ]; + assert_eq!(b, Bytes32(expected)); + } + + #[test] + fn test_from_str_invalid_non_hex() { + let s = "0x123g5678"; // Invalid character 'g' + assert!(Bytes32::from_str(s).is_err()); + } + + #[test] + fn test_from_str_too_big() { + let s = + "0123456789ABCDEF0123456789ABCDEF01234567890123456789ABCDEF01234567890123456789ABCDEF0"; // 65 characters + assert!(Bytes32::from_str(s).is_err()); + } +} diff --git a/arbitrator/bench/Cargo.toml b/arbitrator/bench/Cargo.toml index 3ab5b99..74b948a 100644 --- a/arbitrator/bench/Cargo.toml +++ b/arbitrator/bench/Cargo.toml @@ -3,10 +3,6 @@ name = "bench" version = "0.1.0" edition = "2021" -[lib] -name = "bench" -path = "src/lib.rs" - [[bin]] name = "benchbin" path = "src/bin.rs" @@ -20,7 +16,6 @@ clap = { version = "4.4.8", features = ["derive"] } gperftools = { version = "0.2.0", optional = true } serde = { version = "1.0.130", features = ["derive", "rc"] } serde_json = "1.0.67" -serde_with = { version = "3.8.1", features = ["base64"] } [features] counters = [] diff --git a/arbitrator/bench/src/bin.rs b/arbitrator/bench/src/bin.rs index f7e69f5..60a7036 100644 --- a/arbitrator/bench/src/bin.rs +++ b/arbitrator/bench/src/bin.rs @@ -1,6 +1,5 @@ use std::{path::PathBuf, time::Duration}; -use bench::prepare::*; use clap::Parser; use eyre::bail; @@ -10,11 +9,12 @@ use gperftools::profiler::PROFILER; #[cfg(feature = "heapprof")] use gperftools::heap_profiler::HEAP_PROFILER; -use prover::machine::MachineStatus; - #[cfg(feature = "counters")] use prover::{machine, memory, merkle}; +use prover::machine::MachineStatus; +use prover::prepare::prepare_machine; + #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] struct Args { diff --git a/arbitrator/jit/src/machine.rs b/arbitrator/jit/src/machine.rs index 2a3c5c5..02523f7 100644 --- a/arbitrator/jit/src/machine.rs +++ b/arbitrator/jit/src/machine.rs @@ -129,7 +129,9 @@ pub fn create(opts: &Opts, env: WasmEnv) -> (Instance, FunctionEnv, Sto "send_response" => func!(program::send_response), "create_stylus_config" => func!(program::create_stylus_config), "create_evm_data" => func!(program::create_evm_data), + "create_evm_data_v2" => func!(program::create_evm_data_v2), "activate" => func!(program::activate), + "activate_v2" => func!(program::activate_v2), }, }; diff --git a/arbitrator/jit/src/program.rs b/arbitrator/jit/src/program.rs index c608a3c..084afe9 100644 --- a/arbitrator/jit/src/program.rs +++ b/arbitrator/jit/src/program.rs @@ -16,8 +16,45 @@ use prover::{ programs::{config::PricingParams, prelude::*}, }; -/// activates a user program +const DEFAULT_STYLUS_ARBOS_VERSION: u64 = 31; + pub fn activate( + env: WasmEnvMut, + wasm_ptr: GuestPtr, + wasm_size: u32, + pages_ptr: GuestPtr, + asm_estimate_ptr: GuestPtr, + init_cost_ptr: GuestPtr, + cached_init_cost_ptr: GuestPtr, + stylus_version: u16, + debug: u32, + codehash: GuestPtr, + module_hash_ptr: GuestPtr, + gas_ptr: GuestPtr, + err_buf: GuestPtr, + err_buf_len: u32, +) -> Result { + activate_v2( + env, + wasm_ptr, + wasm_size, + pages_ptr, + asm_estimate_ptr, + init_cost_ptr, + cached_init_cost_ptr, + stylus_version, + DEFAULT_STYLUS_ARBOS_VERSION, + debug, + codehash, + module_hash_ptr, + gas_ptr, + err_buf, + err_buf_len, + ) +} + +/// activates a user program +pub fn activate_v2( mut env: WasmEnvMut, wasm_ptr: GuestPtr, wasm_size: u32, @@ -25,7 +62,8 @@ pub fn activate( asm_estimate_ptr: GuestPtr, init_cost_ptr: GuestPtr, cached_init_cost_ptr: GuestPtr, - version: u16, + stylus_version: u16, + arbos_version_for_gas: u64, debug: u32, codehash: GuestPtr, module_hash_ptr: GuestPtr, @@ -40,7 +78,15 @@ pub fn activate( let page_limit = mem.read_u16(pages_ptr); let gas_left = &mut mem.read_u64(gas_ptr); - match Module::activate(&wasm, codehash, version, page_limit, debug, gas_left) { + match Module::activate( + &wasm, + codehash, + stylus_version, + arbos_version_for_gas, + page_limit, + debug, + gas_left, + ) { Ok((module, data)) => { mem.write_u64(gas_ptr, *gas_left); mem.write_u16(pages_ptr, data.footprint); @@ -222,9 +268,47 @@ pub fn create_stylus_config( Ok(res as u64) } -/// Creates an `EvmData` handler from its component parts. pub fn create_evm_data( + env: WasmEnvMut, + block_basefee_ptr: GuestPtr, + chainid: u64, + block_coinbase_ptr: GuestPtr, + block_gas_limit: u64, + block_number: u64, + block_timestamp: u64, + contract_address_ptr: GuestPtr, + module_hash_ptr: GuestPtr, + msg_sender_ptr: GuestPtr, + msg_value_ptr: GuestPtr, + tx_gas_price_ptr: GuestPtr, + tx_origin_ptr: GuestPtr, + cached: u32, + reentrant: u32, +) -> Result { + create_evm_data_v2( + env, + DEFAULT_STYLUS_ARBOS_VERSION, + block_basefee_ptr, + chainid, + block_coinbase_ptr, + block_gas_limit, + block_number, + block_timestamp, + contract_address_ptr, + module_hash_ptr, + msg_sender_ptr, + msg_value_ptr, + tx_gas_price_ptr, + tx_origin_ptr, + cached, + reentrant, + ) +} + +/// Creates an `EvmData` handler from its component parts. +pub fn create_evm_data_v2( mut env: WasmEnvMut, + arbos_version: u64, block_basefee_ptr: GuestPtr, chainid: u64, block_coinbase_ptr: GuestPtr, @@ -243,6 +327,7 @@ pub fn create_evm_data( let (mut mem, _) = env.jit_env(); let evm_data = EvmData { + arbos_version, block_basefee: mem.read_bytes32(block_basefee_ptr), cached: cached != 0, chainid, diff --git a/arbitrator/jit/src/wavmio.rs b/arbitrator/jit/src/wavmio.rs index 062d18d..0ca666d 100644 --- a/arbitrator/jit/src/wavmio.rs +++ b/arbitrator/jit/src/wavmio.rs @@ -8,8 +8,6 @@ use crate::{ }; use arbutil::{Color, PreimageType}; use caller_env::{GuestPtr, MemAccess}; -use sha2::Sha256; -use sha3::{Digest, Keccak256}; use std::{ io, io::{BufReader, BufWriter, ErrorKind}, @@ -170,19 +168,25 @@ pub fn resolve_preimage_impl( error!("Missing requested preimage for hash {hash_hex} in {name}") }; - // Check if preimage rehashes to the provided hash. Exclude blob preimages - let calculated_hash: [u8; 32] = match preimage_type { - PreimageType::Keccak256 => Keccak256::digest(preimage).into(), - PreimageType::Sha2_256 => Sha256::digest(preimage).into(), - PreimageType::EthVersionedHash => *hash, - }; - if calculated_hash != *hash { - error!( - "Calculated hash {} of preimage {} does not match provided hash {}", - hex::encode(calculated_hash), - hex::encode(preimage), - hex::encode(*hash) - ); + #[cfg(debug_assertions)] + { + use sha2::Sha256; + use sha3::{Digest, Keccak256}; + + // Check if preimage rehashes to the provided hash. Exclude blob preimages + let calculated_hash: [u8; 32] = match preimage_type { + PreimageType::Keccak256 => Keccak256::digest(preimage).into(), + PreimageType::Sha2_256 => Sha256::digest(preimage).into(), + PreimageType::EthVersionedHash => *hash, + }; + if calculated_hash != *hash { + error!( + "Calculated hash {} of preimage {} does not match provided hash {}", + hex::encode(calculated_hash), + hex::encode(preimage), + hex::encode(*hash) + ); + } } if offset % 32 != 0 { diff --git a/arbitrator/prover/Cargo.toml b/arbitrator/prover/Cargo.toml index 5475647..da329b1 100644 --- a/arbitrator/prover/Cargo.toml +++ b/arbitrator/prover/Cargo.toml @@ -19,10 +19,10 @@ num = "0.4" rustc-demangle = "0.1.21" serde = { version = "1.0.130", features = ["derive", "rc"] } serde_json = "1.0.67" +serde_with = { version = "3.8.1", features = ["base64"] } sha3 = "0.9.1" static_assertions = "1.1.0" structopt = "0.3.23" -serde_with = "1.12.1" parking_lot = "0.12.1" lazy_static.workspace = true itertools = "0.10.5" diff --git a/arbitrator/prover/src/binary.rs b/arbitrator/prover/src/binary.rs index aa55374..2260f6b 100644 --- a/arbitrator/prover/src/binary.rs +++ b/arbitrator/prover/src/binary.rs @@ -9,7 +9,9 @@ use crate::{ }, value::{ArbValueType, FunctionType, IntegerValType, Value}, }; -use arbutil::{math::SaturatingSum, Bytes32, Color, DebugColor}; +use arbutil::{ + evm::ARBOS_VERSION_STYLUS_CHARGING_FIXES, math::SaturatingSum, Bytes32, Color, DebugColor, +}; use eyre::{bail, ensure, eyre, Result, WrapErr}; use fnv::{FnvHashMap as HashMap, FnvHashSet as HashSet}; use nom::{ @@ -641,6 +643,7 @@ impl<'a> WasmBinary<'a> { /// Parses and instruments a user wasm pub fn parse_user( wasm: &'a [u8], + arbos_version_for_gas: u64, page_limit: u16, compile: &CompileConfig, codehash: &Bytes32, @@ -678,6 +681,10 @@ impl<'a> WasmBinary<'a> { limit!(65536, code.expr.len(), "opcodes in func body"); } + if arbos_version_for_gas >= ARBOS_VERSION_STYLUS_CHARGING_FIXES { + limit!(513, bin.imports.len(), "imports") + } + let table_entries = bin.tables.iter().map(|x| x.initial).saturating_sum(); limit!(4096, table_entries, "table entries"); diff --git a/arbitrator/prover/src/lib.rs b/arbitrator/prover/src/lib.rs index 0f53747..08473c2 100644 --- a/arbitrator/prover/src/lib.rs +++ b/arbitrator/prover/src/lib.rs @@ -11,6 +11,8 @@ pub mod machine; /// cbindgen:ignore pub mod memory; pub mod merkle; +pub mod parse_input; +pub mod prepare; mod print; pub mod programs; mod reinterpret; diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 358876b..4ece1f7 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -371,13 +371,16 @@ impl Module { for import in &bin.imports { let module = import.module; let have_ty = &bin.types[import.offset as usize]; - let (forward, import_name) = match import.name.strip_prefix(Module::FORWARDING_PREFIX) { - Some(name) => (true, name), - None => (false, import.name), - }; + // allow_hostapi is only set for system modules like the + // forwarder. We restrict stripping the prefix for user modules. + let (forward, import_name) = + if allow_hostapi && import.name.starts_with(Self::FORWARDING_PREFIX) { + (true, &import.name[Self::FORWARDING_PREFIX.len()..]) + } else { + (false, import.name) + }; - let mut qualified_name = format!("{module}__{import_name}"); - qualified_name = qualified_name.replace(&['/', '.', '-'] as &[char], "_"); + let qualified_name = format!("{module}__{import_name}"); let func = if let Some(import) = available_imports.get(&qualified_name) { let call = match forward { diff --git a/arbitrator/prover/src/main.rs b/arbitrator/prover/src/main.rs index dba32e0..a889cc6 100644 --- a/arbitrator/prover/src/main.rs +++ b/arbitrator/prover/src/main.rs @@ -8,6 +8,7 @@ use eyre::{eyre, Context, Result}; use fnv::{FnvHashMap as HashMap, FnvHashSet as HashSet}; use prover::{ machine::{GlobalState, InboxIdentifier, Machine, MachineStatus, PreimageResolver, ProofInfo}, + prepare::prepare_machine, utils::{file_bytes, hash_preimage, CBytes}, wavm::Opcode, }; @@ -86,6 +87,10 @@ struct Opts { skip_until_host_io: bool, #[structopt(long)] max_steps: Option, + // JSON inputs supercede any of the command-line inputs which could + // be specified in the JSON file. + #[structopt(long)] + json_inputs: Option, } fn file_with_stub_header(path: &Path, headerlength: usize) -> Result> { @@ -135,83 +140,8 @@ fn main() -> Result<()> { } } } - let mut inbox_contents = HashMap::default(); - let mut inbox_position = opts.inbox_position; - let mut delayed_position = opts.delayed_inbox_position; - let inbox_header_len; - let delayed_header_len; - if opts.inbox_add_stub_headers { - inbox_header_len = INBOX_HEADER_LEN; - delayed_header_len = DELAYED_HEADER_LEN + 1; - } else { - inbox_header_len = 0; - delayed_header_len = 0; - } - - for path in opts.inbox { - inbox_contents.insert( - (InboxIdentifier::Sequencer, inbox_position), - file_with_stub_header(&path, inbox_header_len)?, - ); - println!("read file {:?} to seq. inbox {}", &path, inbox_position); - inbox_position += 1; - } - for path in opts.delayed_inbox { - inbox_contents.insert( - (InboxIdentifier::Delayed, delayed_position), - file_with_stub_header(&path, delayed_header_len)?, - ); - delayed_position += 1; - } - let mut preimages: HashMap> = HashMap::default(); - if let Some(path) = opts.preimages { - let mut file = BufReader::new(File::open(path)?); - loop { - let mut ty_buf = [0u8; 1]; - match file.read_exact(&mut ty_buf) { - Ok(()) => {} - Err(e) if e.kind() == ErrorKind::UnexpectedEof => break, - Err(e) => return Err(e.into()), - } - let preimage_ty: PreimageType = ty_buf[0].try_into()?; - - let mut size_buf = [0u8; 8]; - file.read_exact(&mut size_buf)?; - let size = u64::from_le_bytes(size_buf) as usize; - let mut buf = vec![0u8; size]; - file.read_exact(&mut buf)?; - - let hash = hash_preimage(&buf, preimage_ty)?; - preimages - .entry(preimage_ty) - .or_default() - .insert(hash.into(), buf.as_slice().into()); - } - } - let preimage_resolver = - Arc::new(move |_, ty, hash| preimages.get(&ty).and_then(|m| m.get(&hash)).cloned()) - as PreimageResolver; - - let last_block_hash = decode_hex_arg(&opts.last_block_hash, "--last-block-hash")?; - let last_send_root = decode_hex_arg(&opts.last_send_root, "--last-send-root")?; - - let global_state = GlobalState { - u64_vals: [opts.inbox_position, opts.position_within_message], - bytes32_vals: [last_block_hash, last_send_root], - }; - - let mut mach = Machine::from_paths( - &opts.libraries, - &opts.binary, - true, - opts.allow_hostapi, - opts.debug_funcs, - true, - global_state, - inbox_contents, - preimage_resolver, - )?; + let mut mach = initialize_machine(&opts)?; for path in &opts.stylus_modules { let err = || eyre!("failed to read module at {}", path.to_string_lossy().red()); @@ -414,6 +344,13 @@ fn main() -> Result<()> { }); } + println!( + "End GlobalState:\n BlockHash: {:?}\n SendRoot: {:?}\n Batch: {}\n PosInBatch: {}", + mach.get_global_state().bytes32_vals[0], + mach.get_global_state().bytes32_vals[1], + mach.get_global_state().u64_vals[0], + mach.get_global_state().u64_vals[1] + ); println!("End machine status: {:?}", mach.get_status()); println!("End machine hash: {}", mach.hash()); println!("End machine stack: {:?}", mach.get_data_stack()); @@ -462,7 +399,6 @@ fn main() -> Result<()> { } } } - let opts_binary = opts.binary; let opts_libraries = opts.libraries; let format_pc = |module_num: usize, func_num: usize| -> (String, String) { @@ -543,3 +479,87 @@ fn main() -> Result<()> { } Ok(()) } + +fn initialize_machine(opts: &Opts) -> eyre::Result { + if let Some(json_inputs) = opts.json_inputs.clone() { + prepare_machine(json_inputs, opts.binary.clone()) + } else { + let mut inbox_contents = HashMap::default(); + let mut inbox_position = opts.inbox_position; + let mut delayed_position = opts.delayed_inbox_position; + let inbox_header_len; + let delayed_header_len; + if opts.inbox_add_stub_headers { + inbox_header_len = INBOX_HEADER_LEN; + delayed_header_len = DELAYED_HEADER_LEN + 1; + } else { + inbox_header_len = 0; + delayed_header_len = 0; + } + + for path in opts.inbox.clone() { + inbox_contents.insert( + (InboxIdentifier::Sequencer, inbox_position), + file_with_stub_header(&path, inbox_header_len)?, + ); + println!("read file {:?} to seq. inbox {}", &path, inbox_position); + inbox_position += 1; + } + for path in opts.delayed_inbox.clone() { + inbox_contents.insert( + (InboxIdentifier::Delayed, delayed_position), + file_with_stub_header(&path, delayed_header_len)?, + ); + delayed_position += 1; + } + + let mut preimages: HashMap> = HashMap::default(); + if let Some(path) = opts.preimages.clone() { + let mut file = BufReader::new(File::open(path)?); + loop { + let mut ty_buf = [0u8; 1]; + match file.read_exact(&mut ty_buf) { + Ok(()) => {} + Err(e) if e.kind() == ErrorKind::UnexpectedEof => break, + Err(e) => return Err(e.into()), + } + let preimage_ty: PreimageType = ty_buf[0].try_into()?; + + let mut size_buf = [0u8; 8]; + file.read_exact(&mut size_buf)?; + let size = u64::from_le_bytes(size_buf) as usize; + let mut buf = vec![0u8; size]; + file.read_exact(&mut buf)?; + + let hash = hash_preimage(&buf, preimage_ty)?; + preimages + .entry(preimage_ty) + .or_default() + .insert(hash.into(), buf.as_slice().into()); + } + } + let preimage_resolver = + Arc::new(move |_, ty, hash| preimages.get(&ty).and_then(|m| m.get(&hash)).cloned()) + as PreimageResolver; + + let last_block_hash = decode_hex_arg(&opts.last_block_hash, "--last-block-hash")?; + let last_send_root = decode_hex_arg(&opts.last_send_root, "--last-send-root")?; + + let global_state = GlobalState { + u64_vals: [opts.inbox_position, opts.position_within_message], + bytes32_vals: [last_block_hash, last_send_root], + }; + + Machine::from_paths( + &opts.libraries, + &opts.binary, + true, + opts.allow_hostapi, + opts.debug_funcs, + true, + global_state, + inbox_contents, + preimage_resolver, + ) + } +} diff --git a/arbitrator/prover/src/parse_input.rs b/arbitrator/prover/src/parse_input.rs new file mode 100644 index 0000000..fa7adb4 --- /dev/null +++ b/arbitrator/prover/src/parse_input.rs @@ -0,0 +1,112 @@ +use arbutil::Bytes32; +use serde::Deserialize; +use serde_json; +use serde_with::base64::Base64; +use serde_with::As; +use serde_with::DisplayFromStr; +use std::{ + collections::HashMap, + io::{self, BufRead}, +}; + +/// prefixed_hex deserializes hex strings which are prefixed with `0x` +/// +/// The default hex deserializer does not support prefixed hex strings. +/// +/// It is an error to use this deserializer on a string that does not +/// begin with `0x`. +mod prefixed_hex { + use serde::{self, Deserialize, Deserializer}; + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + if let Some(s) = s.strip_prefix("0x") { + hex::decode(s).map_err(serde::de::Error::custom) + } else { + Err(serde::de::Error::custom("missing 0x prefix")) + } + } +} + +#[derive(Debug)] +pub struct UserWasm(Vec); + +/// UserWasm is a wrapper around Vec +/// +/// It is useful for decompressing a brotli-compressed wasm module. +/// +/// Note: The wrapped Vec is already Base64 decoded before +/// from(Vec) is called by serde. +impl UserWasm { + /// as_vec returns the decompressed wasm module as a Vec + pub fn as_vec(&self) -> Vec { + self.0.clone() + } +} + +impl AsRef<[u8]> for UserWasm { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +/// The Vec is compressed using brotli, and must be decompressed before use. +impl From> for UserWasm { + fn from(data: Vec) -> Self { + let decompressed = brotli::decompress(&data, brotli::Dictionary::Empty).unwrap(); + Self(decompressed) + } +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct BatchInfo { + pub number: u64, + #[serde(with = "As::")] + pub data_b64: Vec, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct StartState { + #[serde(with = "prefixed_hex")] + pub block_hash: Vec, + #[serde(with = "prefixed_hex")] + pub send_root: Vec, + pub batch: u64, + pub pos_in_batch: u64, +} + +/// FileData is the deserialized form of the input JSON file. +/// +/// The go JSON library in json.go uses some custom serialization and +/// compression logic that needs to be reversed when deserializing the +/// JSON in rust. +/// +/// Note: It is important to change this file whenever the go JSON +/// serialization changes. +#[derive(Debug, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct FileData { + pub id: u64, + pub has_delayed_msg: bool, + pub delayed_msg_nr: u64, + #[serde(with = "As::>>")] + pub preimages_b64: HashMap>>, + pub batch_info: Vec, + #[serde(with = "As::")] + pub delayed_msg_b64: Vec, + pub start_state: StartState, + #[serde(with = "As::>>")] + pub user_wasms: HashMap>, +} + +impl FileData { + pub fn from_reader(mut reader: R) -> io::Result { + let data = serde_json::from_reader(&mut reader)?; + Ok(data) + } +} diff --git a/arbitrator/prover/src/prepare.rs b/arbitrator/prover/src/prepare.rs new file mode 100644 index 0000000..a485267 --- /dev/null +++ b/arbitrator/prover/src/prepare.rs @@ -0,0 +1,65 @@ +use arbutil::{Bytes32, PreimageType}; +use std::collections::HashMap; +use std::fs::File; +use std::io::BufReader; +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +use crate::machine::{argument_data_to_inbox, GlobalState, Machine}; +use crate::parse_input::*; +use crate::utils::CBytes; + +pub fn prepare_machine(preimages: PathBuf, machines: PathBuf) -> eyre::Result { + let file = File::open(preimages)?; + let reader = BufReader::new(file); + + let data = FileData::from_reader(reader)?; + let preimages = data + .preimages_b64 + .into_iter() + .flat_map(|preimage| preimage.1.into_iter()) + .collect::>>(); + let preimage_resolver = move |_: u64, _: PreimageType, hash: Bytes32| -> Option { + preimages + .get(&hash) + .map(|data| CBytes::from(data.as_slice())) + }; + let preimage_resolver = Arc::new(Box::new(preimage_resolver)); + + let binary_path = Path::new(&machines); + let mut mach = Machine::new_from_wavm(binary_path)?; + + let block_hash: [u8; 32] = data.start_state.block_hash.try_into().unwrap(); + let block_hash: Bytes32 = block_hash.into(); + let send_root: [u8; 32] = data.start_state.send_root.try_into().unwrap(); + let send_root: Bytes32 = send_root.into(); + let bytes32_vals: [Bytes32; 2] = [block_hash, send_root]; + let u64_vals: [u64; 2] = [data.start_state.batch, data.start_state.pos_in_batch]; + let start_state = GlobalState { + bytes32_vals, + u64_vals, + }; + + for (arch, wasm) in data.user_wasms.iter() { + if arch != "wavm" { + continue; + } + for (id, wasm) in wasm.iter() { + mach.add_stylus_module(*id, wasm.as_vec()); + } + } + + mach.set_global_state(start_state); + + mach.set_preimage_resolver(preimage_resolver); + + let identifier = argument_data_to_inbox(0).unwrap(); + for batch_info in data.batch_info.iter() { + mach.add_inbox_msg(identifier, batch_info.number, batch_info.data_b64.clone()); + } + + let identifier = argument_data_to_inbox(1).unwrap(); + mach.add_inbox_msg(identifier, data.delayed_msg_nr, data.delayed_msg_b64); + + Ok(mach) +} diff --git a/arbitrator/prover/src/programs/config.rs b/arbitrator/prover/src/programs/config.rs index 1a37294..0353589 100644 --- a/arbitrator/prover/src/programs/config.rs +++ b/arbitrator/prover/src/programs/config.rs @@ -17,7 +17,7 @@ use { meter::Meter, start::StartMover, MiddlewareWrapper, }, std::sync::Arc, - wasmer::{Cranelift, CraneliftOptLevel, Engine, Store}, + wasmer::{Cranelift, CraneliftOptLevel, Engine, Store, Target}, wasmer_compiler_singlepass::Singlepass, }; @@ -180,17 +180,19 @@ impl CompileConfig { } #[cfg(feature = "native")] - pub fn store(&self) -> Store { - let mut compiler: Box = match self.debug.cranelift { + pub fn engine(&self, target: Target) -> Engine { + use wasmer::sys::EngineBuilder; + + let mut wasmer_config: Box = match self.debug.cranelift { true => { - let mut compiler = Cranelift::new(); - compiler.opt_level(CraneliftOptLevel::Speed); - Box::new(compiler) + let mut wasmer_config = Cranelift::new(); + wasmer_config.opt_level(CraneliftOptLevel::Speed); + Box::new(wasmer_config) } false => Box::new(Singlepass::new()), }; - compiler.canonicalize_nans(true); - compiler.enable_verifier(); + wasmer_config.canonicalize_nans(true); + wasmer_config.enable_verifier(); let start = MiddlewareWrapper::new(StartMover::new(self.debug.debug_info)); let meter = MiddlewareWrapper::new(Meter::new(&self.pricing)); @@ -200,22 +202,24 @@ impl CompileConfig { // add the instrumentation in the order of application // note: this must be consistent with the prover - compiler.push_middleware(Arc::new(start)); - compiler.push_middleware(Arc::new(meter)); - compiler.push_middleware(Arc::new(dygas)); - compiler.push_middleware(Arc::new(depth)); - compiler.push_middleware(Arc::new(bound)); + wasmer_config.push_middleware(Arc::new(start)); + wasmer_config.push_middleware(Arc::new(meter)); + wasmer_config.push_middleware(Arc::new(dygas)); + wasmer_config.push_middleware(Arc::new(depth)); + wasmer_config.push_middleware(Arc::new(bound)); if self.debug.count_ops { let counter = Counter::new(); - compiler.push_middleware(Arc::new(MiddlewareWrapper::new(counter))); + wasmer_config.push_middleware(Arc::new(MiddlewareWrapper::new(counter))); } - Store::new(compiler) + EngineBuilder::new(wasmer_config) + .set_target(Some(target)) + .into() } #[cfg(feature = "native")] - pub fn engine(&self) -> Engine { - self.store().engine().clone() + pub fn store(&self, target: Target) -> Store { + Store::new(self.engine(target)) } } diff --git a/arbitrator/prover/src/programs/mod.rs b/arbitrator/prover/src/programs/mod.rs index a5df2e3..a35308e 100644 --- a/arbitrator/prover/src/programs/mod.rs +++ b/arbitrator/prover/src/programs/mod.rs @@ -8,7 +8,7 @@ use crate::{ programs::config::CompileConfig, value::{FunctionType as ArbFunctionType, Value}, }; -use arbutil::{math::SaturatingSum, Bytes32, Color}; +use arbutil::{evm::ARBOS_VERSION_STYLUS_CHARGING_FIXES, math::SaturatingSum, Bytes32, Color}; use eyre::{bail, eyre, Report, Result, WrapErr}; use fnv::FnvHashMap as HashMap; use std::fmt::Debug; @@ -418,58 +418,64 @@ impl Module { pub fn activate( wasm: &[u8], codehash: &Bytes32, - version: u16, + stylus_version: u16, + arbos_version_for_gas: u64, // must only be used for activation gas page_limit: u16, debug: bool, gas: &mut u64, ) -> Result<(Self, StylusData)> { - // converts a number of microseconds to gas - // TODO: collapse to a single value after finalizing factors - let us_to_gas = |us: u64| { - let fudge = 2; - let sync_rate = 1_000_000 / 2; - let speed = 7_000_000; - us.saturating_mul(fudge * speed) / sync_rate - }; - - macro_rules! pay { - ($us:expr) => { - let amount = us_to_gas($us); - if *gas < amount { - *gas = 0; - bail!("out of gas"); - } - *gas -= amount; + let compile = CompileConfig::version(stylus_version, debug); + let (bin, stylus_data) = + WasmBinary::parse_user(wasm, arbos_version_for_gas, page_limit, &compile, codehash) + .wrap_err("failed to parse wasm")?; + + if arbos_version_for_gas > 0 { + // converts a number of microseconds to gas + // TODO: collapse to a single value after finalizing factors + let us_to_gas = |us: u64| { + let fudge = 2; + let sync_rate = 1_000_000 / 2; + let speed = 7_000_000; + us.saturating_mul(fudge * speed) / sync_rate }; - } - - // pay for wasm - let wasm_len = wasm.len() as u64; - pay!(wasm_len.saturating_mul(31_733 / 100_000)); - - let compile = CompileConfig::version(version, debug); - let (bin, stylus_data) = WasmBinary::parse_user(wasm, page_limit, &compile, codehash) - .wrap_err("failed to parse wasm")?; - // pay for funcs - let funcs = bin.functions.len() as u64; - pay!(funcs.saturating_mul(17_263) / 100_000); - - // pay for data - let data = bin.datas.iter().map(|x| x.data.len()).saturating_sum() as u64; - pay!(data.saturating_mul(17_376) / 100_000); - - // pay for elements - let elems = bin.elements.iter().map(|x| x.range.len()).saturating_sum() as u64; - pay!(elems.saturating_mul(17_376) / 100_000); - - // pay for memory - let mem = bin.memories.first().map(|x| x.initial).unwrap_or_default(); - pay!(mem.saturating_mul(2217)); - - // pay for code - let code = bin.codes.iter().map(|x| x.expr.len()).saturating_sum() as u64; - pay!(code.saturating_mul(535) / 1_000); + macro_rules! pay { + ($us:expr) => { + let amount = us_to_gas($us); + if *gas < amount { + *gas = 0; + bail!("out of gas"); + } + *gas -= amount; + }; + } + + // pay for wasm + if arbos_version_for_gas >= ARBOS_VERSION_STYLUS_CHARGING_FIXES { + let wasm_len = wasm.len() as u64; + pay!(wasm_len.saturating_mul(31_733) / 100_000); + } + + // pay for funcs + let funcs = bin.functions.len() as u64; + pay!(funcs.saturating_mul(17_263) / 100_000); + + // pay for data + let data = bin.datas.iter().map(|x| x.data.len()).saturating_sum() as u64; + pay!(data.saturating_mul(17_376) / 100_000); + + // pay for elements + let elems = bin.elements.iter().map(|x| x.range.len()).saturating_sum() as u64; + pay!(elems.saturating_mul(17_376) / 100_000); + + // pay for memory + let mem = bin.memories.first().map(|x| x.initial).unwrap_or_default(); + pay!(mem.saturating_mul(2217)); + + // pay for code + let code = bin.codes.iter().map(|x| x.expr.len()).saturating_sum() as u64; + pay!(code.saturating_mul(535) / 1_000); + } let module = Self::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data)) .wrap_err("failed to build user module")?; diff --git a/arbitrator/stylus/Cargo.toml b/arbitrator/stylus/Cargo.toml index 4717bd6..ea1d878 100644 --- a/arbitrator/stylus/Cargo.toml +++ b/arbitrator/stylus/Cargo.toml @@ -21,11 +21,11 @@ thiserror = "1.0.33" bincode = "1.3.3" lazy_static.workspace = true libc = "0.2.108" -lru.workspace = true eyre = "0.6.5" rand = "0.8.5" fnv = "1.0.7" hex = "0.4.3" +clru = "0.6.2" [dev-dependencies] num-bigint = "0.4.4" diff --git a/arbitrator/stylus/src/cache.rs b/arbitrator/stylus/src/cache.rs index 06739f2..c1fdaac 100644 --- a/arbitrator/stylus/src/cache.rs +++ b/arbitrator/stylus/src/cache.rs @@ -2,16 +2,19 @@ // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE use arbutil::Bytes32; +use clru::{CLruCache, CLruCacheConfig, WeightScale}; use eyre::Result; use lazy_static::lazy_static; -use lru::LruCache; use parking_lot::Mutex; use prover::programs::config::CompileConfig; +use std::hash::RandomState; use std::{collections::HashMap, num::NonZeroUsize}; use wasmer::{Engine, Module, Store}; +use crate::target_cache::target_native; + lazy_static! { - static ref INIT_CACHE: Mutex = Mutex::new(InitCache::new(256)); + static ref INIT_CACHE: Mutex = Mutex::new(InitCache::new(256 * 1024 * 1024)); } macro_rules! cache { @@ -20,9 +23,16 @@ macro_rules! cache { }; } +pub struct LruCounters { + pub hits: u32, + pub misses: u32, + pub does_not_fit: u32, +} + pub struct InitCache { long_term: HashMap, - lru: LruCache, + lru: CLruCache, + lru_counters: LruCounters, } #[derive(Clone, Copy, Hash, PartialEq, Eq)] @@ -46,11 +56,16 @@ impl CacheKey { struct CacheItem { module: Module, engine: Engine, + entry_size_estimate_bytes: usize, } impl CacheItem { - fn new(module: Module, engine: Engine) -> Self { - Self { module, engine } + fn new(module: Module, engine: Engine, entry_size_estimate_bytes: usize) -> Self { + Self { + module, + engine, + entry_size_estimate_bytes, + } } fn data(&self) -> (Module, Store) { @@ -58,23 +73,66 @@ impl CacheItem { } } +struct CustomWeightScale; +impl WeightScale for CustomWeightScale { + fn weight(&self, _key: &CacheKey, val: &CacheItem) -> usize { + // clru defines that each entry consumes (weight + 1) of the cache capacity. + // We subtract 1 since we only want to use the weight as the size of the entry. + val.entry_size_estimate_bytes.saturating_sub(1) + } +} + +#[repr(C)] +pub struct LruCacheMetrics { + pub size_bytes: u64, + pub count: u32, + pub hits: u32, + pub misses: u32, + pub does_not_fit: u32, +} + +pub fn deserialize_module( + module: &[u8], + version: u16, + debug: bool, +) -> Result<(Module, Engine, usize)> { + let engine = CompileConfig::version(version, debug).engine(target_native()); + let module = unsafe { Module::deserialize_unchecked(&engine, module)? }; + + let asm_size_estimate_bytes = module.serialize()?.len(); + // add 128 bytes for the cache item overhead + let entry_size_estimate_bytes = asm_size_estimate_bytes + 128; + + Ok((module, engine, entry_size_estimate_bytes)) +} + impl InitCache { // current implementation only has one tag that stores to the long_term // future implementations might have more, but 0 is a reserved tag // that will never modify long_term state const ARBOS_TAG: u32 = 1; - fn new(size: usize) -> Self { + const DOES_NOT_FIT_MSG: &'static str = "Failed to insert into LRU cache, item too large"; + + fn new(size_bytes: usize) -> Self { Self { long_term: HashMap::new(), - lru: LruCache::new(NonZeroUsize::new(size).unwrap()), + lru: CLruCache::with_config( + CLruCacheConfig::new(NonZeroUsize::new(size_bytes).unwrap()) + .with_scale(CustomWeightScale), + ), + lru_counters: LruCounters { + hits: 0, + misses: 0, + does_not_fit: 0, + }, } } - pub fn set_lru_size(size: u32) { + pub fn set_lru_capacity(capacity_bytes: u64) { cache!() .lru - .resize(NonZeroUsize::new(size.try_into().unwrap()).unwrap()) + .resize(NonZeroUsize::new(capacity_bytes.try_into().unwrap()).unwrap()) } /// Retrieves a cached value, updating items as necessary. @@ -89,8 +147,11 @@ impl InitCache { // See if the item is in the LRU cache, promoting if so if let Some(item) = cache.lru.get(&key) { - return Some(item.data()); + let data = item.data(); + cache.lru_counters.hits += 1; + return Some(data); } + cache.lru_counters.misses += 1; None } @@ -114,20 +175,24 @@ impl InitCache { if long_term_tag == Self::ARBOS_TAG { cache.long_term.insert(key, item.clone()); } else { - cache.lru.promote(&key) + // only calls get to move the key to the head of the LRU list + cache.lru.get(&key); } return Ok(item.data()); } drop(cache); - let engine = CompileConfig::version(version, debug).engine(); - let module = unsafe { Module::deserialize_unchecked(&engine, module)? }; + let (module, engine, entry_size_estimate_bytes) = + deserialize_module(module, version, debug)?; - let item = CacheItem::new(module, engine); + let item = CacheItem::new(module, engine, entry_size_estimate_bytes); let data = item.data(); let mut cache = cache!(); if long_term_tag != Self::ARBOS_TAG { - cache.lru.put(key, item); + if cache.lru.put_with_weight(key, item).is_err() { + cache.lru_counters.does_not_fit += 1; + eprintln!("{}", Self::DOES_NOT_FIT_MSG); + }; } else { cache.long_term.insert(key, item); } @@ -142,7 +207,9 @@ impl InitCache { let key = CacheKey::new(module_hash, version, debug); let mut cache = cache!(); if let Some(item) = cache.long_term.remove(&key) { - cache.lru.put(key, item); + if cache.lru.put_with_weight(key, item).is_err() { + eprintln!("{}", Self::DOES_NOT_FIT_MSG); + } } } @@ -153,7 +220,48 @@ impl InitCache { let mut cache = cache!(); let cache = &mut *cache; for (key, item) in cache.long_term.drain() { - cache.lru.put(key, item); // not all will fit, just a heuristic + // not all will fit, just a heuristic + if cache.lru.put_with_weight(key, item).is_err() { + eprintln!("{}", Self::DOES_NOT_FIT_MSG); + } } } + + pub fn get_lru_metrics() -> LruCacheMetrics { + let mut cache = cache!(); + + let count = cache.lru.len(); + let metrics = LruCacheMetrics { + // add 1 to each entry to account that we subtracted 1 in the weight calculation + size_bytes: (cache.lru.weight() + count).try_into().unwrap(), + + count: count.try_into().unwrap(), + + hits: cache.lru_counters.hits, + misses: cache.lru_counters.misses, + does_not_fit: cache.lru_counters.does_not_fit, + }; + + // Empty counters. + // go side, which is the only consumer of this function besides tests, + // will read those counters and increment its own prometheus counters with them. + cache.lru_counters = LruCounters { + hits: 0, + misses: 0, + does_not_fit: 0, + }; + + metrics + } + + // only used for testing + pub fn clear_lru_cache() { + let mut cache = cache!(); + cache.lru.clear(); + cache.lru_counters = LruCounters { + hits: 0, + misses: 0, + does_not_fit: 0, + }; + } } diff --git a/arbitrator/stylus/src/lib.rs b/arbitrator/stylus/src/lib.rs index 3c53359..abea428 100644 --- a/arbitrator/stylus/src/lib.rs +++ b/arbitrator/stylus/src/lib.rs @@ -11,13 +11,14 @@ use arbutil::{ format::DebugBytes, Bytes32, }; -use cache::InitCache; +use cache::{deserialize_module, InitCache, LruCacheMetrics}; use evm_api::NativeRequestHandler; use eyre::ErrReport; use native::NativeInstance; use prover::programs::{prelude::*, StylusData}; use run::RunProgram; use std::{marker::PhantomData, mem, ptr}; +use target_cache::{target_cache_get, target_cache_set}; pub use brotli; pub use prover; @@ -29,6 +30,7 @@ pub mod run; mod cache; mod evm_api; +mod target_cache; mod util; #[cfg(test)] @@ -122,9 +124,9 @@ impl RustBytes { } } -/// Instruments and "activates" a user wasm. +/// "activates" a user wasm. /// -/// The `output` is either the serialized asm & module pair or an error string. +/// The `output` is either the module or an error string. /// Returns consensus info such as the module hash and footprint on success. /// /// Note that this operation costs gas and is limited by the amount supplied via the `gas` pointer. @@ -137,10 +139,10 @@ impl RustBytes { pub unsafe extern "C" fn stylus_activate( wasm: GoSliceData, page_limit: u16, - version: u16, + stylus_version: u16, + arbos_version_for_gas: u64, debug: bool, output: *mut RustBytes, - asm_len: *mut usize, codehash: *const Bytes32, module_hash: *mut Bytes32, stylus_data: *mut StylusData, @@ -152,18 +154,105 @@ pub unsafe extern "C" fn stylus_activate( let codehash = &*codehash; let gas = &mut *gas; - let (asm, module, info) = - match native::activate(wasm, codehash, version, page_limit, debug, gas) { - Ok(val) => val, - Err(err) => return output.write_err(err), - }; - *asm_len = asm.len(); + let (module, info) = match native::activate( + wasm, + codehash, + stylus_version, + arbos_version_for_gas, + page_limit, + debug, + gas, + ) { + Ok(val) => val, + Err(err) => return output.write_err(err), + }; + *module_hash = module.hash(); *stylus_data = info; - let mut data = asm; - data.extend(&*module.into_bytes()); - output.write(data); + output.write(module.into_bytes()); + UserOutcomeKind::Success +} + +/// "compiles" a user wasm. +/// +/// The `output` is either the asm or an error string. +/// Returns consensus info such as the module hash and footprint on success. +/// +/// # Safety +/// +/// `output` must not be null. +#[no_mangle] +pub unsafe extern "C" fn stylus_compile( + wasm: GoSliceData, + version: u16, + debug: bool, + name: GoSliceData, + output: *mut RustBytes, +) -> UserOutcomeKind { + let wasm = wasm.slice(); + let output = &mut *output; + let name = match String::from_utf8(name.slice().to_vec()) { + Ok(val) => val, + Err(err) => return output.write_err(err.into()), + }; + let target = match target_cache_get(&name) { + Ok(val) => val, + Err(err) => return output.write_err(err), + }; + + let asm = match native::compile(wasm, version, debug, target) { + Ok(val) => val, + Err(err) => return output.write_err(err), + }; + + output.write(asm); + UserOutcomeKind::Success +} + +#[no_mangle] +/// # Safety +/// +/// `output` must not be null. +pub unsafe extern "C" fn wat_to_wasm(wat: GoSliceData, output: *mut RustBytes) -> UserOutcomeKind { + let output = &mut *output; + let wasm = match wasmer::wat2wasm(wat.slice()) { + Ok(val) => val, + Err(err) => return output.write_err(err.into()), + }; + output.write(wasm.into_owned()); + UserOutcomeKind::Success +} + +/// sets target index to a string +/// +/// String format is: Triple+CpuFeature+CpuFeature.. +/// +/// # Safety +/// +/// `output` must not be null. +#[no_mangle] +pub unsafe extern "C" fn stylus_target_set( + name: GoSliceData, + description: GoSliceData, + output: *mut RustBytes, + native: bool, +) -> UserOutcomeKind { + let output = &mut *output; + let name = match String::from_utf8(name.slice().to_vec()) { + Ok(val) => val, + Err(err) => return output.write_err(err.into()), + }; + + let desc_str = match String::from_utf8(description.slice().to_vec()) { + Ok(val) => val, + Err(err) => return output.write_err(err.into()), + }; + + if let Err(err) = target_cache_set(name, desc_str, native) { + return output.write_err(err); + }; + UserOutcomeKind::Success } @@ -220,10 +309,10 @@ pub unsafe extern "C" fn stylus_call( status } -/// resize lru +/// set lru cache capacity #[no_mangle] -pub extern "C" fn stylus_cache_lru_resize(size: u32) { - InitCache::set_lru_size(size); +pub extern "C" fn stylus_set_cache_lru_capacity(capacity_bytes: u64) { + InitCache::set_lru_capacity(capacity_bytes); } /// Caches an activated user program. @@ -274,3 +363,32 @@ pub unsafe extern "C" fn stylus_drop_vec(vec: RustBytes) { mem::drop(vec.into_vec()) } } + +/// Gets lru cache metrics. +#[no_mangle] +pub extern "C" fn stylus_get_lru_cache_metrics() -> LruCacheMetrics { + InitCache::get_lru_metrics() +} + +/// Clears lru cache. +/// Only used for testing purposes. +#[no_mangle] +pub extern "C" fn stylus_clear_lru_cache() { + InitCache::clear_lru_cache() +} + +/// Gets lru entry size in bytes. +/// Only used for testing purposes. +#[no_mangle] +pub extern "C" fn stylus_get_lru_entry_size_estimate_bytes( + module: GoSliceData, + version: u16, + debug: bool, +) -> u64 { + match deserialize_module(module.slice(), version, debug) { + Err(error) => panic!("tried to get invalid asm!: {error}"), + Ok((_, _, lru_entry_size_estimate_bytes)) => { + lru_entry_size_estimate_bytes.try_into().unwrap() + } + } +} diff --git a/arbitrator/stylus/src/native.rs b/arbitrator/stylus/src/native.rs index a7b996e..516c660 100644 --- a/arbitrator/stylus/src/native.rs +++ b/arbitrator/stylus/src/native.rs @@ -4,7 +4,7 @@ use crate::{ cache::InitCache, env::{MeterData, WasmEnv}, - host, util, + host, }; use arbutil::{ evm::{ @@ -33,11 +33,13 @@ use std::{ ops::{Deref, DerefMut}, }; use wasmer::{ - imports, AsStoreMut, Function, FunctionEnv, Instance, Memory, Module, Pages, Store, + imports, AsStoreMut, Function, FunctionEnv, Instance, Memory, Module, Pages, Store, Target, TypedFunction, Value, WasmTypeList, }; use wasmer_vm::VMExtern; +use crate::target_cache::target_native; + #[derive(Debug)] pub struct NativeInstance> { pub instance: Instance, @@ -98,7 +100,7 @@ impl> NativeInstance { evm_data: EvmData, ) -> Result { let env = WasmEnv::new(compile, None, evm, evm_data); - let store = env.compile.store(); + let store = env.compile.store(target_native()); let module = unsafe { Module::deserialize_unchecked(&store, module)? }; Self::from_module(module, store, env) } @@ -137,9 +139,10 @@ impl> NativeInstance { evm_data: EvmData, compile: &CompileConfig, config: StylusConfig, + target: Target, ) -> Result { let env = WasmEnv::new(compile.clone(), Some(config), evm_api, evm_data); - let store = env.compile.store(); + let store = env.compile.store(target); let wat_or_wasm = std::fs::read(path)?; let module = Module::new(&store, wat_or_wasm)?; Self::from_module(module, store, env) @@ -347,8 +350,8 @@ impl> StartlessMachine for NativeInstance { } } -pub fn module(wasm: &[u8], compile: CompileConfig) -> Result> { - let mut store = compile.store(); +pub fn module(wasm: &[u8], compile: CompileConfig, target: Target) -> Result> { + let mut store = compile.store(target); let module = Module::new(&store, wasm)?; macro_rules! stub { (u8 <- $($types:tt)+) => { @@ -428,7 +431,6 @@ pub fn module(wasm: &[u8], compile: CompileConfig) -> Result> { imports.define("console", "tee_f64", stub!(f64 <- |_: f64|)); imports.define("debug", "null_host", stub!(||)); } - Instance::new(&mut store, &module, &imports)?; let module = module.serialize()?; Ok(module.to_vec()) @@ -437,18 +439,26 @@ pub fn module(wasm: &[u8], compile: CompileConfig) -> Result> { pub fn activate( wasm: &[u8], codehash: &Bytes32, - version: u16, + stylus_version: u16, + arbos_version_for_gas: u64, page_limit: u16, debug: bool, gas: &mut u64, -) -> Result<(Vec, ProverModule, StylusData)> { - let compile = CompileConfig::version(version, debug); - let (module, stylus_data) = - ProverModule::activate(wasm, codehash, version, page_limit, debug, gas)?; +) -> Result<(ProverModule, StylusData)> { + let (module, stylus_data) = ProverModule::activate( + wasm, + codehash, + stylus_version, + arbos_version_for_gas, + page_limit, + debug, + gas, + )?; + + Ok((module, stylus_data)) +} - let asm = match self::module(wasm, compile) { - Ok(asm) => asm, - Err(err) => util::panic_with_wasm(wasm, err), - }; - Ok((asm, module, stylus_data)) +pub fn compile(wasm: &[u8], version: u16, debug: bool, target: Target) -> Result> { + let compile = CompileConfig::version(version, debug); + self::module(wasm, compile, target) } diff --git a/arbitrator/stylus/src/target_cache.rs b/arbitrator/stylus/src/target_cache.rs new file mode 100644 index 0000000..a1d6382 --- /dev/null +++ b/arbitrator/stylus/src/target_cache.rs @@ -0,0 +1,81 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use eyre::{eyre, OptionExt, Result}; +use lazy_static::lazy_static; +use parking_lot::RwLock; +use std::{collections::HashMap, str::FromStr}; +use wasmer_types::{CpuFeature, Target, Triple}; + +lazy_static! { + static ref TARGET_CACHE: RwLock> = RwLock::new(HashMap::new()); + static ref TARGET_NATIVE: RwLock = RwLock::new(Target::default()); +} + +fn target_from_string(input: String) -> Result { + if input.is_empty() { + return Ok(Target::default()); + } + let mut parts = input.split('+'); + + let Some(triple_string) = parts.next() else { + return Err(eyre!("no architecture")); + }; + + let triple = match Triple::from_str(triple_string) { + Ok(val) => val, + Err(e) => return Err(eyre!(e)), + }; + + let mut features = CpuFeature::set(); + for flag in parts { + features.insert(CpuFeature::from_str(flag)?); + } + + Ok(Target::new(triple, features)) +} + +/// Populates `TARGET_CACHE` inserting target specified by `description` under `name` key. +/// Additionally, if `native` is set it sets `TARGET_NATIVE` to the specified target. +pub fn target_cache_set(name: String, description: String, native: bool) -> Result<()> { + let target = target_from_string(description)?; + + if native { + if !target.is_native() { + return Err(eyre!("arch not native")); + } + let flags_not_supported = Target::default() + .cpu_features() + .complement() + .intersection(*target.cpu_features()); + if !flags_not_supported.is_empty() { + let mut err_message = String::new(); + err_message.push_str("cpu flags not supported on local cpu for: "); + for item in flags_not_supported.iter() { + err_message.push('+'); + err_message.push_str(&item.to_string()); + } + return Err(eyre!(err_message)); + } + *TARGET_NATIVE.write() = target.clone(); + } + + TARGET_CACHE.write().insert(name, target); + + Ok(()) +} + +pub fn target_native() -> Target { + TARGET_NATIVE.read().clone() +} + +pub fn target_cache_get(name: &str) -> Result { + if name.is_empty() { + return Ok(TARGET_NATIVE.read().clone()); + } + TARGET_CACHE + .read() + .get(name) + .cloned() + .ok_or_eyre("arch not set") +} diff --git a/arbitrator/stylus/src/test/api.rs b/arbitrator/stylus/src/test/api.rs index 92d7317..66d600a 100644 --- a/arbitrator/stylus/src/test/api.rs +++ b/arbitrator/stylus/src/test/api.rs @@ -14,6 +14,7 @@ use eyre::Result; use parking_lot::Mutex; use prover::programs::{memory::MemoryModel, prelude::*}; use std::{collections::HashMap, sync::Arc}; +use wasmer::Target; use super::TestInstance; @@ -53,7 +54,7 @@ impl TestEvmApi { pub fn deploy(&mut self, address: Bytes20, config: StylusConfig, name: &str) -> Result<()> { let file = format!("tests/{name}/target/wasm32-unknown-unknown/release/{name}.wasm"); let wasm = std::fs::read(file)?; - let module = native::module(&wasm, self.compile.clone())?; + let module = native::module(&wasm, self.compile.clone(), Target::default())?; self.contracts.lock().insert(address, module); self.configs.lock().insert(address, config); Ok(()) @@ -67,7 +68,7 @@ impl TestEvmApi { } impl EvmApi for TestEvmApi { - fn get_bytes32(&mut self, key: Bytes32) -> (Bytes32, u64) { + fn get_bytes32(&mut self, key: Bytes32, _evm_api_gas_to_use: u64) -> (Bytes32, u64) { let storage = &mut self.storage.lock(); let storage = storage.get_mut(&self.program).unwrap(); let value = storage.get(&key).cloned().unwrap_or_default(); diff --git a/arbitrator/stylus/src/test/misc.rs b/arbitrator/stylus/src/test/misc.rs index ae44a88..92c4394 100644 --- a/arbitrator/stylus/src/test/misc.rs +++ b/arbitrator/stylus/src/test/misc.rs @@ -9,12 +9,12 @@ use crate::{ }; use eyre::Result; use prover::programs::{prelude::*, start::StartMover}; -use wasmer::{imports, Function}; +use wasmer::{imports, Function, Target}; #[test] fn test_bulk_memory() -> Result<()> { let (compile, config, ink) = test_configs(); - let mut store = compile.store(); + let mut store = compile.store(Target::default()); let filename = "../prover/test-cases/bulk-memory.wat"; let imports = imports! { "env" => { diff --git a/arbitrator/stylus/src/test/mod.rs b/arbitrator/stylus/src/test/mod.rs index 236e563..00c9c62 100644 --- a/arbitrator/stylus/src/test/mod.rs +++ b/arbitrator/stylus/src/test/mod.rs @@ -16,7 +16,7 @@ use rand::prelude::*; use std::{collections::HashMap, path::Path, sync::Arc}; use wasmer::{ imports, wasmparser::Operator, CompilerConfig, Function, FunctionEnv, Imports, Instance, - Module, Store, + Module, Store, Target, }; use wasmer_compiler_singlepass::Singlepass; @@ -33,7 +33,7 @@ type TestInstance = NativeInstance; impl TestInstance { fn new_test(path: &str, compile: CompileConfig) -> Result { - let mut store = compile.store(); + let mut store = compile.store(Target::default()); let imports = imports! { "test" => { "noop" => Function::new_typed(&mut store, || {}), @@ -86,7 +86,14 @@ impl TestInstance { config: StylusConfig, ) -> Result<(Self, TestEvmApi)> { let (mut evm, evm_data) = TestEvmApi::new(compile.clone()); - let native = Self::from_path(path, evm.clone(), evm_data, compile, config)?; + let native = Self::from_path( + path, + evm.clone(), + evm_data, + compile, + config, + Target::default(), + )?; let footprint = native.memory().ty(&native.store).minimum.0 as u16; evm.set_pages(footprint); Ok((native, evm)) diff --git a/arbitrator/stylus/src/test/native.rs b/arbitrator/stylus/src/test/native.rs index 503e587..9669932 100644 --- a/arbitrator/stylus/src/test/native.rs +++ b/arbitrator/stylus/src/test/native.rs @@ -381,7 +381,7 @@ fn test_storage() -> Result<()> { let (mut native, mut evm) = TestInstance::new_with_evm(filename, &compile, config)?; run_native(&mut native, &store_args, ink)?; - assert_eq!(evm.get_bytes32(key.into()).0, Bytes32(value)); + assert_eq!(evm.get_bytes32(key.into(), 0).0, Bytes32(value)); assert_eq!(run_native(&mut native, &load_args, ink)?, value); let mut machine = Machine::from_user_path(Path::new(filename), &compile)?; @@ -465,7 +465,7 @@ fn test_calls() -> Result<()> { run_native(&mut native, &args, ink)?; for (key, value) in slots { - assert_eq!(evm.get_bytes32(key).0, value); + assert_eq!(evm.get_bytes32(key, 0).0, value); } Ok(()) } diff --git a/arbitrator/stylus/tests/erc20/Cargo.lock b/arbitrator/stylus/tests/erc20/Cargo.lock index c3e2159..f5e1e0b 100644 --- a/arbitrator/stylus/tests/erc20/Cargo.lock +++ b/arbitrator/stylus/tests/erc20/Cargo.lock @@ -575,9 +575,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.23" +version = "0.37.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" dependencies = [ "bitflags", "errno", diff --git a/arbitrator/stylus/tests/write-result-len.wat b/arbitrator/stylus/tests/write-result-len.wat new file mode 100644 index 0000000..4c9ad35 --- /dev/null +++ b/arbitrator/stylus/tests/write-result-len.wat @@ -0,0 +1,24 @@ +;; Copyright 2024, Offchain Labs, Inc. +;; For license information, see https://github.com/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (memory (export "memory") 2 2) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $len i32) + + ;; write args to 0x0 + (call $read_args (i32.const 0)) + + ;; treat first 4 bytes as size to write + (i32.load (i32.const 0)) + local.set $len + + ;; call write + (call $write_result (i32.const 0) (local.get $len)) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/tools/module_roots/wasmer/.config/nextest.toml b/arbitrator/tools/module_roots/wasmer/.config/nextest.toml new file mode 100644 index 0000000..3d30908 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.config/nextest.toml @@ -0,0 +1,10 @@ +[profile.ci] +# Print out output for failing tests as soon as they fail, and also at the end +# of the run (for easy scrollability). +failure-output = "immediate-final" +# Do not cancel the test run on the first failure. +fail-fast = false +# Automatically mark a test as "slow" after 60 seconds, then kill it after 180s +slow-timeout = { period = "60s", terminate-after = 3 } +# Retry a couple times in case there are flaky tests +retries = 3 diff --git a/arbitrator/tools/module_roots/wasmer/.devcontainer/Dockerfile b/arbitrator/tools/module_roots/wasmer/.devcontainer/Dockerfile new file mode 100644 index 0000000..0db8981 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.devcontainer/Dockerfile @@ -0,0 +1 @@ +FROM rust:1 diff --git a/arbitrator/tools/module_roots/wasmer/.devcontainer/devcontainer.json b/arbitrator/tools/module_roots/wasmer/.devcontainer/devcontainer.json new file mode 100644 index 0000000..775daa3 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.devcontainer/devcontainer.json @@ -0,0 +1,18 @@ +{ + "name": "Wasmer dev environment", + "dockerFile": "Dockerfile", + + // Set *default* container specific settings.json values on container create. + "settings": { + "terminal.integrated.shell.linux": "/bin/bash", + "lldb.executable": "/usr/bin/lldb" + }, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "rust-lang.rust-analyzer", + "dtsvet.vscode-wasm", + "bungcip.better-toml", + "vadimcn.vscode-lldb" + ] +} diff --git a/arbitrator/tools/module_roots/wasmer/.gitattributes b/arbitrator/tools/module_roots/wasmer/.gitattributes new file mode 100644 index 0000000..6486387 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.gitattributes @@ -0,0 +1,4 @@ +CHANGELOG.md merge=union +*.wast linguist-vendored +*.wat linguist-vendored +scene*.txt -text diff --git a/arbitrator/tools/module_roots/wasmer/.github/CODEOWNERS b/arbitrator/tools/module_roots/wasmer/.github/CODEOWNERS new file mode 100644 index 0000000..1508856 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/CODEOWNERS @@ -0,0 +1,23 @@ +benches @syrusakbary +examples +fuzz @syrusakbary +lib/api @syrusakbary +lib/c-api +lib/cache @syrusakbary +lib/cli @syrusakbary +lib/compiler @syrusakbary +lib/compiler-cranelift @syrusakbary +lib/compiler-llvm +lib/compiler-singlepass @syrusakbary +lib/deprecated +lib/emscripten +lib/engine @syrusakbary +lib/engine-jit @syrusakbary +lib/engine-native @syrusakbary +lib/engine-object-file @syrusakbary +lib/object @syrusakbary +lib/vm @syrusakbary +lib/wasi +lib/wasmer-types @syrusakbary +scripts @syrusakbary +tests diff --git a/arbitrator/tools/module_roots/wasmer/.github/ISSUE_TEMPLATE/---bug-report.md b/arbitrator/tools/module_roots/wasmer/.github/ISSUE_TEMPLATE/---bug-report.md new file mode 100644 index 0000000..6d6491e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/ISSUE_TEMPLATE/---bug-report.md @@ -0,0 +1,50 @@ +--- +name: "\U0001F41E Bug report" +about: Create a report to help us improve +title: '' +labels: "\U0001F41E bug" +assignees: '' + +--- + + + +### Describe the bug + + + +```sh +wasmer -vV; rustc -vV +``` + + +### Steps to reproduce + + +### Expected behavior + + +### Actual behavior + + + +### Additional context + diff --git a/arbitrator/tools/module_roots/wasmer/.github/ISSUE_TEMPLATE/---feature-request.md b/arbitrator/tools/module_roots/wasmer/.github/ISSUE_TEMPLATE/---feature-request.md new file mode 100644 index 0000000..be57463 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/ISSUE_TEMPLATE/---feature-request.md @@ -0,0 +1,26 @@ +--- +name: "\U0001F389 Feature request" +about: Suggest an idea for this project +title: '' +labels: "\U0001F389 enhancement" +assignees: '' + +--- + +Thanks for proposing a new feature! + +### Motivation + +A clear and concise description of what the motivation for the new feature is, and what problem it is solving. + +### Proposed solution + +A clear and concise description of the feature you would like to add, and how it solves the motivating problem. + +### Alternatives + +A clear and concise description of any alternative solutions or features you've considered, and why you're proposed solution is better. + +### Additional context + +Add any other context or screenshots about the feature request here. diff --git a/arbitrator/tools/module_roots/wasmer/.github/ISSUE_TEMPLATE/--question.md b/arbitrator/tools/module_roots/wasmer/.github/ISSUE_TEMPLATE/--question.md new file mode 100644 index 0000000..ad79ab2 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/ISSUE_TEMPLATE/--question.md @@ -0,0 +1,16 @@ +--- +name: "❓ Question" +about: Ask a question about this project +title: '' +labels: "❓ question" +assignees: '' + +--- + +### Summary + +A clear and concise summary of your question. + +### Additional details + +Provide any additional details here. diff --git a/arbitrator/tools/module_roots/wasmer/.github/cross-linux-aarch64/Dockerfile b/arbitrator/tools/module_roots/wasmer/.github/cross-linux-aarch64/Dockerfile new file mode 100644 index 0000000..88b177a --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/cross-linux-aarch64/Dockerfile @@ -0,0 +1,11 @@ +FROM rust:1 +#FROM ghcr.io/cross-rs/aarch64-unknown-linux-gnu:edge + +# set CROSS_DOCKER_IN_DOCKER to inform `cross` that it is executed from within a container +ENV CROSS_DOCKER_IN_DOCKER=true + +RUN cargo install cross +RUN dpkg --add-architecture arm64 && \ + apt-get update && \ + apt-get install -qy curl && \ + curl -sSL https://get.docker.com/ | sh \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/.github/cross-linux-riscv64/Dockerfile b/arbitrator/tools/module_roots/wasmer/.github/cross-linux-riscv64/Dockerfile new file mode 100644 index 0000000..b158afb --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/cross-linux-riscv64/Dockerfile @@ -0,0 +1,70 @@ +FROM debian:stable AS openssl_riscv64 +#FROM ghcr.io/cross-rs/riscv64gc-unknown-linux-gnu:edge AS openssl_riscv64 + +# set CROSS_DOCKER_IN_DOCKER to inform `cross` that it is executed from within a container +ENV CROSS_DOCKER_IN_DOCKER=true + +RUN apt-get update && \ + apt-get install --assume-yes --no-install-recommends \ + ca-certificates \ + curl \ + cpio \ + sharutils \ + gnupg \ + build-essential \ + libc6-dev + +#COPY install_deb.sh / + +#install libssl-dev for riscv64! +#RUN /install_deb.sh riscv64 libssl-dev +#RUN dpkg --add-architecture riscv64 +#RUN apt-get update +#RUN apt-get install libssl-dev:riscv64 +#ENV RISCV64GC_UNKNOWN_LINUX_GNU_OPENSSL_INCLUDE_DIR=/usr/include +#ENV RISCV64GC_UNKNOWN_LINUX_GNU_OPENSSL_LIB_DIR=/usr/lib/riscv64-linux-gnu + + +# install rust tools +RUN curl --proto "=https" --tlsv1.2 --retry 3 -sSfL https://sh.rustup.rs | sh -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" +RUN rustup -v toolchain install 1.73 +# add docker the manual way +COPY install_docker.sh / +RUN /install_docker.sh + +RUN apt-get update && \ + apt-get install --assume-yes --no-install-recommends \ + docker-ce \ + docker-ce-cli \ + containerd.io \ + docker-buildx-plugin \ + docker-compose-plugin + +RUN apt-get update && apt-get install -y --no-install-recommends \ + gcc-riscv64-linux-gnu \ + g++-riscv64-linux-gnu \ + qemu-user-static \ + libssl-dev \ + pkg-config \ + libc6-dev-riscv64-cross + +ENV CROSS_TOOLCHAIN_PREFIX=riscv64-linux-gnu- +ENV CROSS_SYSROOT=/usr/riscv64-linux-gnu +ENV CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER="$CROSS_TOOLCHAIN_PREFIX"gcc \ + AR_riscv64gc_unknown_linux_gnu="$CROSS_TOOLCHAIN_PREFIX"ar \ + CC_riscv64gc_unknown_linux_gnu="$CROSS_TOOLCHAIN_PREFIX"gcc \ + CXX_riscv64gc_unknown_linux_gnu="$CROSS_TOOLCHAIN_PREFIX"g++ \ + CFLAGS_riscv64gc_unknown_linux_gnu="-march=rv64gc -mabi=lp64d" \ + BINDGEN_EXTRA_CLANG_ARGS_riscv64gc_unknown_linux_gnu="--sysroot=$CROSS_SYSROOT" \ + QEMU_LD_PREFIX="$CROSS_SYSROOT" \ + RUST_TEST_THREADS=1 \ + PKG_CONFIG_PATH="/usr/lib/riscv64-linux-gnu/pkgconfig/:${PKG_CONFIG_PATH}" + +RUN rustup target add riscv64gc-unknown-linux-gnu --toolchain 1.73-x86_64-unknown-linux-gnu + +#compile libssl-dev for riscv64! +COPY build_openssl.sh / +RUN /build_openssl.sh +ENV RISCV64GC_UNKNOWN_LINUX_GNU_OPENSSL_INCLUDE_DIR=/openssl_riscv64/include +ENV RISCV64GC_UNKNOWN_LINUX_GNU_OPENSSL_LIB_DIR=/openssl_riscv64/lib diff --git a/arbitrator/tools/module_roots/wasmer/.github/cross-linux-riscv64/build_openssl.sh b/arbitrator/tools/module_roots/wasmer/.github/cross-linux-riscv64/build_openssl.sh new file mode 100755 index 0000000..f42adc1 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/cross-linux-riscv64/build_openssl.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -x +set -euo pipefail + +apt-get install wget + +wget https://www.openssl.org/source/openssl-3.1.1.tar.gz + +tar xf openssl-3.1.1.tar.gz +rm openssl-3.1.1.tar.gz +cd openssl-3.1.1 + +AR=riscv64-linux-gnu-ar NM=riscv64-linux-gnu-nm CC=riscv64-linux-gnu-gcc \ + ./Configure -static no-asm no-tests --prefix=/openssl_riscv64 linux64-riscv64 + +make -j +make install diff --git a/arbitrator/tools/module_roots/wasmer/.github/cross-linux-riscv64/install_deb.sh b/arbitrator/tools/module_roots/wasmer/.github/cross-linux-riscv64/install_deb.sh new file mode 100755 index 0000000..20f3e36 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/cross-linux-riscv64/install_deb.sh @@ -0,0 +1,57 @@ +#!/bin/bash +set -x +set -euo pipefail + +arch="${1}" +shift + +# need to install certain local dependencies +export DEBIAN_FRONTEND=noninteractive +apt-get update +apt-get install --assume-yes --no-install-recommends \ + ca-certificates \ + curl \ + cpio \ + sharutils \ + debian-ports-archive-keyring \ + gnupg + +# Add port from bookworm to get some riscv packages +debsource="deb http://deb.debian.org/debian-ports unstable main" + +# temporarily use debian sources rather than ubuntu. +touch /etc/apt/sources.list +mv /etc/apt/sources.list /etc/apt/sources.list.bak +echo -e "${debsource}" > /etc/apt/sources.list + +dpkg --add-architecture "${arch}" || echo "foreign-architecture ${arch}" \ + > /etc/dpkg/dpkg.cfg.d/multiarch + +## Add Debian keys. +#curl --retry 3 -sSfL 'https://ftp-master.debian.org/keys/archive-key-{11,12}.asc' -O +#curl --retry 3 -sSfL 'https://ftp-master.debian.org/keys/archive-key-{11,12}-security.asc' -O +#curl --retry 3 -sSfL 'https://ftp-master.debian.org/keys/release-{11,12}.asc' -O +#curl --retry 3 -sSfL 'https://www.ports.debian.org/archive_{2023}.key' -O +# +#for key in *.asc *.key; do +# apt-key add "${key}" +# rm "${key}" +#done + +# allow apt-get to retry downloads +echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80-retries + +apt-get update +for dep in $@; do + apt-get install "${dep}:${arch}" --assume-yes +done + +# restore our old sources list +mv -f /etc/apt/sources.list.bak /etc/apt/sources.list +if [ -f /etc/dpkg/dpkg.cfg.d/multiarch.bak ]; then + mv /etc/dpkg/dpkg.cfg.d/multiarch.bak /etc/dpkg/dpkg.cfg.d/multiarch +fi + +# can fail if arch is used (amd64 and/or i386) +dpkg --remove-architecture "${arch}" || true +apt-get update \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/.github/cross-linux-riscv64/install_docker.sh b/arbitrator/tools/module_roots/wasmer/.github/cross-linux-riscv64/install_docker.sh new file mode 100755 index 0000000..ea7e7ae --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/cross-linux-riscv64/install_docker.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -x +set -euo pipefail + +mkdir -m 0755 -p /etc/apt/keyrings +curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + +echo \ + "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \ + "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \ + tee /etc/apt/sources.list.d/docker.list > /dev/null diff --git a/arbitrator/tools/module_roots/wasmer/.github/pull_request_template.md b/arbitrator/tools/module_roots/wasmer/.github/pull_request_template.md new file mode 100644 index 0000000..96866a3 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/pull_request_template.md @@ -0,0 +1,11 @@ + + +# Description + diff --git a/arbitrator/tools/module_roots/wasmer/.github/s3-cache-cleanup.py b/arbitrator/tools/module_roots/wasmer/.github/s3-cache-cleanup.py new file mode 100755 index 0000000..c28d7a0 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/s3-cache-cleanup.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +# Deletes all objects in an S3 bucket that are older than a given number of days. +# Used for cleaning up the custom Github Actions cache. + +import boto3 +import datetime +import os + +# Define the S3 bucket name and the number of days to retain objects +days_to_retain = 7 + +bucket_name = os.environ['AWS_BUCKET_NAME'] +access_key = os.environ['AWS_ACCESS_KEY_ID'] +secret_key = os.environ['AWS_SECRET_ACCESS_KEY'] +endpoint = os.environ['AWS_ENDPOINT'] + +# Create a connection to the S3 service +s3 = boto3.resource('s3', + endpoint_url = endpoint, + aws_access_key_id = access_key, + aws_secret_access_key = secret_key, + region_name = 'auto', +) + +bucket = s3.Bucket(bucket_name) + +# Calculate the retention date. +cutoff_date = (datetime.datetime.now() - datetime.timedelta(days=days_to_retain)) +cutoff_date = cutoff_date.replace(tzinfo=datetime.timezone.utc) + +print(f'Deleting all objects in bucket {bucket_name} older than {cutoff_date}...') + +total_count = 0 +deleted_count = 0 + +for obj in bucket.objects.all(): + total_count += 1 + if obj.last_modified < cutoff_date: + print(f'Deleting {obj.key}...') + obj.delete() + deleted_count += 1 + +print(f'Complete! Deleted {deleted_count} objects out of a total {total_count}.') diff --git a/arbitrator/tools/module_roots/wasmer/.github/stale.yml b/arbitrator/tools/module_roots/wasmer/.github/stale.yml new file mode 100644 index 0000000..f992c78 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/stale.yml @@ -0,0 +1,17 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 365 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 30 +# Issues with these labels will never be considered stale +exemptLabels: + - "🐞 bug" +# Label to use when marking an issue as stale +staleLabel: "🏚 stale" +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: > + Feel free to reopen the issue if it has been closed by mistake. diff --git a/arbitrator/tools/module_roots/wasmer/.github/workflows/benchmark.yaml b/arbitrator/tools/module_roots/wasmer/.github/workflows/benchmark.yaml new file mode 100644 index 0000000..ae78b55 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/workflows/benchmark.yaml @@ -0,0 +1,75 @@ +name: Run Benchmarks and upload results + +on: + push: + branches: + - benchmark # TODO: change it back to master once we really track the results. We commented this as speed.wasmer.io is failing + +env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: git + MSRV: "1.73" + +jobs: + run_benchmark: + name: Benchmark on ${{ matrix.build }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + build: [linux] + include: + - build: linux + os: ubuntu-latest + env: + SCCACHE_AZURE_BLOB_CONTAINER: wasmerstoragesccacheblob + SCCACHE_AZURE_CONNECTION_STRING: ${{ secrets.SCCACHE_AZURE_CONNECTION_STRING }} + steps: + - uses: actions/checkout@v3 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.MSRV }} + - name: Configure cargo data directory + # After this point, all cargo registry and crate data is stored in + # $GITHUB_WORKSPACE/.cargo_home. This allows us to cache only the files + # that are needed during the build process. Additionally, this works + # around a bug in the 'cache' action that causes directories outside of + # the workspace dir to be saved/restored incorrectly. + run: echo "CARGO_HOME=$(pwd)/.cargo_home" >> $GITHUB_ENV + - name: Cache + uses: actions/cache@master + with: + # Note: crates from the git repo always get rebuilt + # so we cache only those subdirectories of target/{debug|release} that + # contain the build output for crates that come from the registry. + path: |- + .cargo_home + target/*/.* + target/*/build + target/*/deps + key: ${{ matrix.os }}-${{ hashFiles('Cargo.lock') }} + restore-keys: | + ${{ matrix.os }} + - name: Install LLVM (Linux) + if: matrix.os == 'ubuntu-latest' + run: | + curl --proto '=https' --tlsv1.2 -sSf https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz -L -o llvm.tar.xz + mkdir -p /opt/llvm-10 + tar xf llvm.tar.xz --strip-components=1 -C /opt/llvm-10 + echo '/opt/llvm-10/bin' >> $GITHUB_PATH + echo 'name=LLVM_SYS_100_PREFIX=/opt/llvm-10' >> $GITHUB_ENV + - name: Install Python + uses: actions/setup-python@v2 + with: + python-version: 3.8 + - name: Install Python dependencies + run: | + pip install codespeed-client + pip install toml + - name: Run Benchmark + run: | + make bench + git clone https://github.com/wasmerio/wasmer-bench + + python3 wasmer-bench/send_metrics.py + diff --git a/arbitrator/tools/module_roots/wasmer/.github/workflows/build.yml b/arbitrator/tools/module_roots/wasmer/.github/workflows/build.yml new file mode 100644 index 0000000..a46e46f --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/workflows/build.yml @@ -0,0 +1,607 @@ +name: Builds + +env: + RUST_BACKTRACE: 1 + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: git + MSRV: "1.73" + +on: + push: + branches: + - 'master' + tags: + # this is _not_ a regex, see: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet + - '[0-9]+.[0-9]+.[0-9]+*' + workflow_dispatch: + inputs: + release: + description: 'Make release' +jobs: + setup: + name: Set up + runs-on: ubuntu-latest + outputs: + VERSION: ${{ steps.setup.outputs.VERSION }} + DOING_RELEASE: ${{ steps.setup.outputs.DOING_RELEASE }} + steps: + - name: Set up env vars + id: setup + shell: bash + run: | + VERSION=${GITHUB_REF/refs\/tags\//} + echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT + DOING_RELEASE=$(echo $VERSION | grep -c '^[0-9]\+\.[0-9]\+\.[0-9]\+\(-\([a-zA-Z]\+\)\?[0-9]*\)\?$' || true) + echo "DOING_RELEASE=${DOING_RELEASE}" >> $GITHUB_OUTPUT + echo $VERSION + echo $DOING_RELEASE + + build: + name: Build on ${{ matrix.build }} + runs-on: ${{ matrix.os }} + needs: setup + strategy: + fail-fast: false + matrix: + include: + - build: linux-x64 + os: ubuntu-20.04 + artifact_name: 'wasmer-linux-amd64' + llvm_url: 'https://github.com/wasmerio/llvm-custom-builds/releases/download/15.x/llvm-linux-amd64.tar.xz' + cross_compilation_artifact_name: 'cross_compiled_from_linux' + use_sccache: false + use_llvm: true + build_wasm: true + - build: macos-x64 + os: macos-11 + llvm_url: 'https://github.com/wasmerio/llvm-custom-builds/releases/download/15.x/llvm-darwin-amd64.tar.xz' + artifact_name: 'wasmer-darwin-amd64' + cross_compilation_artifact_name: 'cross_compiled_from_mac' + use_sccache: false + use_llvm: true + build_wasm: false + - build: macos-arm64 + os: macos-14 + target: aarch64-apple-darwin + artifact_name: 'wasmer-darwin-arm64' + use_sccache: false + use_llvm: false + build_wasm: false + - build: windows-x64 + os: windows-2019 + artifact_name: 'wasmer-windows-amd64' + llvm_url: 'https://github.com/wasmerio/llvm-custom-builds/releases/download/15.x/llvm-windows-amd64.tar.xz' + cross_compilation_artifact_name: 'cross_compiled_from_win' + use_sccache: false + use_llvm: true + build_wasm: false + - build: linux-musl-x64 + os: ubuntu-latest + artifact_name: 'wasmer-linux-musl-amd64' + #llvm_url: 'https://github.com/wasmerio/llvm-custom-builds/releases/download/15.x/llvm-linux-amd64.tar.xz' + container: alpine:latest + use_sccache: false + use_llvm: false + build_wasm: true + container: ${{ matrix.container }} + env: + SCCACHE_AZURE_BLOB_CONTAINER: wasmerstoragesccacheblob + SCCACHE_AZURE_CONNECTION_STRING: ${{ secrets.SCCACHE_AZURE_CONNECTION_STRING }} + TARGET: ${{ matrix.target }} + steps: + - uses: actions/checkout@v3 + - name: Set up base deps on musl + if: matrix.build == 'linux-musl-x64' + run: ./scripts/alpine-linux-install-deps.sh + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.MSRV }} + target: ${{ matrix.target }} + - uses: Swatinem/rust-cache@v2 + if: matrix.use_sccache != true + - name: Install LLVM (macOS Apple Silicon) + if: matrix.os == 'macos-11.0' && !matrix.llvm_url + run: | + brew install llvm + - name: Install LLVM + if: matrix.llvm_url + shell: bash + run: | + LLVM_DIR=$(pwd)/${{ env.LLVM_DIR }} + mkdir -p ${LLVM_DIR} + curl --proto '=https' --tlsv1.2 -sSf "${{ matrix.llvm_url }}" -L -o - | tar xJv -C ${LLVM_DIR} + echo "${LLVM_DIR}/bin" >> $GITHUB_PATH + env: + LLVM_DIR: .llvm + - name: Configure LLVM (Windows) + # The Custom Windows build does not contains llvm-config.exe, so need to setup manualy here + if: matrix.build == 'windows-x64' && matrix.llvm_url + shell: bash + run: | + LLVM_DIR=$(pwd)/${{ env.LLVM_DIR }} + echo LLVM_SYS_150_PREFIX="${LLVM_DIR}" >> $GITHUB_ENV + echo LLVM_ENABLE=1 >> $GITHUB_ENV + env: + LLVM_DIR: .llvm + - name: Set up dependencies for Mac OS + run: | + brew install automake + # 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.os == 'macos-latest' || matrix.os == 'macos-11.0' + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ matrix.build }}-${{ matrix.target }}-cargo-${{ hashFiles('Cargo.lock') }}-v1 + - uses: actions/cache@v2 + if: matrix.use_sccache + with: + path: ${{ runner.tool_cache }}/cargo-sccache + key: ${{ matrix.build }}-${{ matrix.target }}-sccache-bin-${{ env.CARGO_SCCACHE_VERSION }}-v1 + - name: Install sccache + if: matrix.use_sccache + run: | + if [ ! -f '${{ runner.tool_cache }}/cargo-sccache/bin/sccache' ]; then + cargo install sccache --no-default-features --features=dist-client,azure --root '${{ runner.tool_cache }}/cargo-sccache' + fi + shell: bash + - name: Setup Rust target + run: | + mkdir -p .cargo + cat << EOF > .cargo/config.toml + [build] + target = "${{ matrix.target }}" + EOF + if: matrix.target + - name: Set sccache port + if: matrix.use_sccache && matrix.random_sccache_port + run: | + netstat -aln | awk ' + $6 == "LISTEN" { + if ($4 ~ "[.:][0-9]+$") { + n = split($4, a, /[:.]/); + port = a[n]; + p[port] = 1 + } + } + END { + for (i = 3000; i < 65000 && p[i]; i++){}; + if (i == 65000) {exit 1}; + print "SCCACHE_SERVER_PORT=" i + } + ' >> $GITHUB_ENV + # echo "SCCACHE_SERVER_PORT=9000" + echo "Setting random sccache port to: $SCCACHE_SERVER_PORT" + shell: bash + - name: Start sccache + if: matrix.use_sccache + run: | + chmod +x '${{ runner.tool_cache }}/cargo-sccache/bin/sccache' + '${{ runner.tool_cache }}/cargo-sccache/bin/sccache' --start-server + '${{ runner.tool_cache }}/cargo-sccache/bin/sccache' -s + echo 'RUSTC_WRAPPER=${{ runner.tool_cache }}/cargo-sccache/bin/sccache' >> $GITHUB_ENV + shell: bash + - name: Build C API headless + shell: bash + run: | + make package-capi-headless + - name: Build C API + shell: bash + run: | + make build-capi + - name: Build Wasmer binary with LLVM + if: matrix.use_llvm + shell: bash + run: | + make build-wasmer + env: + ENABLE_LLVM: 1 + - name: Build Wasmer binary without LLVM + if: matrix.use_llvm != true + shell: bash + run: | + make build-wasmer + #- name: Build Wasmer binary on Wasm32-WASI without LLVM + # if: matrix.build_wasm + # shell: bash + # run: | + # make build-wasmer-wasm + - name: Install Nightly Rust for Headless + if: matrix.build != 'linux-musl-x64' + uses: dtolnay/rust-toolchain@master + with: + toolchain: "nightly-2023-10-05" + target: ${{ matrix.target }} + components: "rust-src" + - name: Build Minimal Wasmer Headless + if: matrix.build != 'linux-musl-x64' + run: | + cargo install xargo + echo "" >> Cargo.toml + echo "[profile.release]" >> Cargo.toml + echo "opt-level = 'z'" >> Cargo.toml + echo "debug = false" >> Cargo.toml + echo "debug-assertions = false" >> Cargo.toml + echo "overflow-checks = false" >> Cargo.toml + echo "lto = true" >> Cargo.toml + echo "panic = 'abort'" >> Cargo.toml + echo "incremental = false" >> Cargo.toml + echo "codegen-units = 1" >> Cargo.toml + echo "rpath = false" >> Cargo.toml + rustup override set nightly-2023-10-05 + make build-wasmer-headless-minimal + rustup override unset + - name: Dist + run: | + make distribution + - name: Upload Artifacts + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.artifact_name }} + path: dist + if-no-files-found: error + retention-days: 2 + + windows_gnu: + name: Windows GNU + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install Windows-GNU linker + shell: bash + run: | + sudo apt install -y mingw-w64 + - uses: dtolnay/rust-toolchain@stable + with: + target: x86_64-pc-windows-gnu + - name: Install Windows-GNU target + shell: bash + run: | + rustup target add x86_64-pc-windows-gnu + - name: Install Windows 10 SDK with xwin + shell: bash + run: | + mkdir -p /tmp/xwin + mkdir -p /tmp/xwindownload + mkdir -p /tmp/xwincache + git clone https://github.com/wasmerio/xwin --depth=1 /tmp/xwin + cargo build --release --manifest-path=/tmp/xwin/Cargo.toml + /tmp/xwin/target/release/xwin --accept-license --cache-dir /tmp/xwincache splat --output /tmp/xwindownload + mkdir -p /tmp/winsdk + cp /tmp/xwindownload/sdk/lib/10.0.20348/um/x86_64/WS2_32.lib /tmp/winsdk/ + cp /tmp/xwindownload/sdk/lib/10.0.20348/um/x86_64/KERNEL32.lib /tmp/winsdk/ + cp /tmp/xwindownload/sdk/lib/10.0.20348/um/x86_64/BCRYPT.lib /tmp/winsdk/ + cp /tmp/xwindownload/sdk/lib/10.0.20348/um/x86_64/ADVAPI32.lib /tmp/winsdk/ + cp /tmp/xwindownload/sdk/lib/10.0.20348/um/x86_64/USERENV.lib /tmp/winsdk/ + echo "WinSDK files:" + ls -laH /tmp/winsdk + echo "" + mkdir -p package + mkdir -p package/winsdk + cp -r /tmp/winsdk/* package/winsdk + - name: Build Wasmer C-API without LLVM + shell: bash + run: | + make build-capi + env: + RUSTFLAGS: -Cpanic=abort + CARGO_TARGET: x86_64-pc-windows-gnu + ENABLE_LLVM: 0 + - name: Build Wasmer C-API headless without LLVM + shell: bash + run: | + make build-capi-headless + env: + RUSTFLAGS: -Cpanic=abort + CARGO_TARGET: x86_64-pc-windows-gnu + ENABLE_LLVM: 0 + - name: Dist + run: | + make distribution-gnu + env: + CARGO_TARGET: x86_64-pc-windows-gnu + TARGET_DIR: target/x86_64-pc-windows-gnu/release + - name: Upload Artifacts + uses: actions/upload-artifact@v3 + with: + name: 'wasmer-windows-gnu64' + path: dist + if-no-files-found: error + retention-days: 2 + + darwin_aarch64_jsc: + name: macOS aarch64 (JSC) + runs-on: macos-11.0 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + target: aarch64-apple-darwin + - name: Install Darwin-aarch64 target + shell: bash + run: | + rustup target add aarch64-apple-darwin + - name: Build Wasmer C-API (JSC) + shell: bash + run: | + make build-capi-jsc + env: + RUSTFLAGS: -Cpanic=abort + CARGO_TARGET: aarch64-apple-darwin + - name: Dist + run: | + make distribution + env: + CARGO_TARGET: aarch64-apple-darwin + TARGET_DIR: target/aarch64-apple-darwin/release + - name: Upload Artifacts + uses: actions/upload-artifact@v3 + with: + name: 'aarch64-apple-darwin-jsc' + path: dist + if-no-files-found: error + retention-days: 2 + + darwin_x86_64_jsc: + name: macOS x86_64 (JSC) + runs-on: macos-11.0 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + target: x86_64-apple-darwin + - name: Build Wasmer C-API (JSC) + shell: bash + run: | + make build-capi-jsc + env: + RUSTFLAGS: -Cpanic=abort + CARGO_TARGET: x86_64-apple-darwin + - name: Dist + run: | + make distribution + env: + CARGO_TARGET: x86_64-apple-darwin + TARGET_DIR: target/x86_64-apple-darwin/release + - name: Upload Artifacts + uses: actions/upload-artifact@v3 + with: + name: 'x86_64-apple-darwin-jsc' + path: dist + if-no-files-found: error + retention-days: 2 + + linux_aarch64: + name: Linux aarch64 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + target: aarch64-unknown-linux-gnu + - name: Build cross image + run: | + docker build -t wasmer/aarch64 ${GITHUB_WORKSPACE}/.github/cross-linux-aarch64/ + env: + CROSS_DOCKER_IN_DOCKER: true + - name: Build Wasmer binary + run: | + make build-wasmer + env: + CARGO_BINARY: docker run -v /var/run/docker.sock:/var/run/docker.sock -v ${GITHUB_WORKSPACE}:/project -w /project wasmer/aarch64 cross + CROSS_DOCKER_IN_DOCKER: true + CARGO_TARGET: aarch64-unknown-linux-gnu + PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig + PKG_CONFIG_ALLOW_CROSS: true + ENABLE_LLVM: 0 + - name: Build C API headless + shell: bash + run: | + make package-capi-headless + env: + CARGO_BINARY: docker run -v /var/run/docker.sock:/var/run/docker.sock -v ${GITHUB_WORKSPACE}:/project -w /project wasmer/aarch64 cross + CROSS_DOCKER_IN_DOCKER: true + CARGO_TARGET: aarch64-unknown-linux-gnu + PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig + PKG_CONFIG_ALLOW_CROSS: true + ENABLE_LLVM: 0 + TARGET: aarch64-unknown-linux-gnu + TARGET_DIR: target/aarch64-unknown-linux-gnu/release + - name: Build C API + run: | + make build-capi + env: + CARGO_BINARY: docker run -v /var/run/docker.sock:/var/run/docker.sock -v ${GITHUB_WORKSPACE}:/project -w /project wasmer/aarch64 cross + CROSS_DOCKER_IN_DOCKER: true + CARGO_TARGET: aarch64-unknown-linux-gnu + PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig + PKG_CONFIG_ALLOW_CROSS: true + ENABLE_LLVM: 0 + - name: Dist + run: | + make distribution + env: + CARGO_BINARY: docker run -v /var/run/docker.sock:/var/run/docker.sock -v ${GITHUB_WORKSPACE}:/project -w /project wasmer/aarch64 cross + CROSS_DOCKER_IN_DOCKER: true + CARGO_TARGET: aarch64-unknown-linux-gnu + PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig + PKG_CONFIG_ALLOW_CROSS: true + TARGET: aarch64-unknown-linux-gnu + TARGET_DIR: target/aarch64-unknown-linux-gnu/release + - name: Upload Artifacts + uses: actions/upload-artifact@v3 + with: + name: wasmer-linux-aarch64 + path: dist + if-no-files-found: error + retention-days: 2 + + linux_riscv64: + name: Linux riscv64 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + target: riscv64gc-unknown-linux-gnu + - name: Build cross image + run: | + docker build -t wasmer/riscv64 ${GITHUB_WORKSPACE}/.github/cross-linux-riscv64/ + env: + CROSS_DOCKER_IN_DOCKER: true + - name: Build Wasmer binary + run: | + make build-wasmer + env: + CARGO_BINARY: docker run -v /var/run/docker.sock:/var/run/docker.sock -v ${GITHUB_WORKSPACE}:/project -w /project wasmer/riscv64:latest cargo + CROSS_DOCKER_IN_DOCKER: true + CARGO_TARGET: riscv64gc-unknown-linux-gnu + PKG_CONFIG_PATH: /usr/lib/riscv64-linux-gnu/pkgconfig + PKG_CONFIG_ALLOW_CROSS: true + ENABLE_LLVM: 0 + - name: Build C API headless + shell: bash + run: | + make package-capi-headless + env: + CARGO_BINARY: docker run -v /var/run/docker.sock:/var/run/docker.sock -v ${GITHUB_WORKSPACE}:/project -w /project wasmer/riscv64:latest cargo + CROSS_DOCKER_IN_DOCKER: true + CARGO_TARGET: riscv64gc-unknown-linux-gnu + PKG_CONFIG_PATH: /usr/lib/riscv64-linux-gnu/pkgconfig + PKG_CONFIG_ALLOW_CROSS: true + ENABLE_LLVM: 0 + TARGET: riscv64gc-unknown-linux-gnu + TARGET_DIR: target/riscv64gc-unknown-linux-gnu/release + - name: Build C API + run: | + make build-capi + env: + CARGO_BINARY: docker run -v /var/run/docker.sock:/var/run/docker.sock -v ${GITHUB_WORKSPACE}:/project -w /project wasmer/riscv64:latest cargo + CROSS_DOCKER_IN_DOCKER: true + CARGO_TARGET: riscv64gc-unknown-linux-gnu + PKG_CONFIG_PATH: /usr/lib/riscv64-linux-gnu/pkgconfig + PKG_CONFIG_ALLOW_CROSS: true + ENABLE_LLVM: 0 + - name: Dist + run: | + make distribution + env: + CARGO_BINARY: docker run -v /var/run/docker.sock:/var/run/docker.sock -v ${GITHUB_WORKSPACE}:/project -w /project wasmer/riscv64:latest cargo + CROSS_DOCKER_IN_DOCKER: true + CARGO_TARGET: riscv64gc-unknown-linux-gnu + PKG_CONFIG_PATH: /usr/lib/riscv64-linux-gnu/pkgconfig + PKG_CONFIG_ALLOW_CROSS: true + TARGET: riscv64gc-unknown-linux-gnu + TARGET_DIR: target/riscv64gc-unknown-linux-gnu/release + - name: Upload Artifacts + uses: actions/upload-artifact@v3 + with: + name: wasmer-linux-riscv64 + path: dist + if-no-files-found: error + retention-days: 2 + + release: + needs: [setup, build, linux_aarch64, windows_gnu, linux_riscv64] + runs-on: ubuntu-latest + if: needs.setup.outputs.DOING_RELEASE == '1' || github.event.inputs.release != '' + steps: + - name: Download the Artifacts + uses: actions/download-artifact@v2 + with: + path: artifacts + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ needs.setup.outputs.VERSION }} + release_name: Release ${{ needs.setup.outputs.VERSION }} + draft: true + prerelease: false + - name: Upload Release Asset Windows Installer + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: artifacts/wasmer-windows-amd64/WasmerInstaller.exe + asset_name: wasmer-windows.exe + asset_content_type: application/vnd.microsoft.portable-executable + - name: Upload Release Asset Windows + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: artifacts/wasmer-windows-amd64/wasmer.tar.gz + asset_name: wasmer-windows-amd64.tar.gz + asset_content_type: application/gzip + - name: Upload Release Asset Linux amd64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: artifacts/wasmer-linux-amd64/wasmer.tar.gz + asset_name: wasmer-linux-amd64.tar.gz + asset_content_type: application/gzip + - name: Upload Release Asset Linux aarch64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: artifacts/wasmer-linux-aarch64/wasmer.tar.gz + asset_name: wasmer-linux-aarch64.tar.gz + asset_content_type: application/gzip + - name: Upload Release Asset Linux riscv64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: artifacts/wasmer-linux-riscv64/wasmer.tar.gz + asset_name: wasmer-linux-riscv64.tar.gz + asset_content_type: application/gzip + - name: Upload Release Asset Windows gnu64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: artifacts/wasmer-windows-gnu64/wasmer.tar.gz + asset_name: wasmer-windows-gnu64.tar.gz + asset_content_type: application/gzip + - name: Upload Release Asset Linux amd64 (musl) + id: upload-release-asset-linux-musl-amd64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: artifacts/wasmer-linux-musl-amd64/wasmer.tar.gz + asset_name: wasmer-linux-musl-amd64.tar.gz + asset_content_type: application/gzip + - name: Upload Release Asset Mac amd64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: artifacts/wasmer-darwin-amd64/wasmer.tar.gz + asset_name: wasmer-darwin-amd64.tar.gz + asset_content_type: application/gzip + - name: Upload Release Asset Mac arm64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: artifacts/wasmer-darwin-arm64/wasmer.tar.gz + asset_name: wasmer-darwin-arm64.tar.gz + asset_content_type: application/gzip diff --git a/arbitrator/tools/module_roots/wasmer/.github/workflows/cache-bucket-cleanup.yaml b/arbitrator/tools/module_roots/wasmer/.github/workflows/cache-bucket-cleanup.yaml new file mode 100644 index 0000000..03ae072 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/workflows/cache-bucket-cleanup.yaml @@ -0,0 +1,29 @@ +# Cleans up the custom Github Actions cache bucket. + +name: Actions Cache Bucket Cleanup + +on: + schedule: + # Run once a day. + - cron: "0 3 * * *" + + # A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "cron" + cron: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + steps: + - name: Install boto3 library + run: pip install boto3 + - name: Clone repository + uses: actions/checkout@v3 + - name: Run cleanup + env: + AWS_ENDPOINT: https://1541b1e8a3fc6ad155ce67ef38899700.r2.cloudflarestorage.com + AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_ARTIFACTS_CACHE_ACCESS_TOKEN }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_ARTIFACTS_CACHE_ACCESS_KEY }} + AWS_BUCKET_NAME: wasmer-rust-artifacts-cache + run: | + ./.github/s3-cache-cleanup.py diff --git a/arbitrator/tools/module_roots/wasmer/.github/workflows/check-public-api.yaml b/arbitrator/tools/module_roots/wasmer/.github/workflows/check-public-api.yaml new file mode 100644 index 0000000..fe2ff30 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/workflows/check-public-api.yaml @@ -0,0 +1,47 @@ +name: Check public API + +env: + RUST_BACKTRACE: 1 +on: + workflow_dispatch: + inputs: + compareMasterWithVersion: + required: false + description: 'Version to compare with (i.e. "1.0.2")' +jobs: + setup: + name: Set up + runs-on: ubuntu-latest + outputs: + VERSION: ${{ steps.setup.outputs.VERSION }} + SHOULD_CHECK_API: ${{ steps.setup.outputs.SHOULD_CHECK_API }} + steps: + + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Install latest nightly + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + profile: minimal + override: true + + - name: Generate API change report + shell: bash + run: | + cargo install cargo-public-api + CARGO_LATEST_VERSION = git tag --list | tail -n 1 + LATEST_VERSION=${INPUT_COMPAREMASTERWITHVERSION:=$CARGO_LATEST_VERSION} + cargo public-api --manifest-path=lib/api/Cargo.toml --diff-git-checkouts $LATEST_VERSION master > diff.txt + + - name: Archive change report + uses: actions/upload-artifact@v3 + with: + name: api-diff-report + path: | + diff.txt + + diff --git a/arbitrator/tools/module_roots/wasmer/.github/workflows/cloudcompiler.yaml b/arbitrator/tools/module_roots/wasmer/.github/workflows/cloudcompiler.yaml new file mode 100644 index 0000000..2c82c07 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/workflows/cloudcompiler.yaml @@ -0,0 +1,78 @@ +name: Release cloudcompiler.wasm + +env: + RUST_BACKTRACE: 1 + MSRV: "1.73" + +on: + push: + tags: + # trigger action on release branch + - '[0-9]+.[0-9]+.[0-9]+*' + workflow_dispatch: + +jobs: + setup: + name: Set up + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.MSRV }} + target: ${{ matrix.target }} + - name: Install wasm32-wasi target + shell: bash + run: | + rustup target add wasm32-wasi + - 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 + [package] + name = "${{ secrets.WAPM_DEV_USERNAME }}/cloudcompiler" + version = "0.1.0" + description = "cloudcompiler.wasm" + license = "MIT" + repository = "https://github.com/wasmerio/wasmer" + + [[module]] + name = "cloudcompiler" + source = "cloudcompiler.wasm" + abi = "wasi" + + [module.interfaces] + wasi = "0.1.0-unstable" + + [[command]] + name = "cloudcompiler" + module = "cloudcompiler" + EOF + - name: Fix wasmer.toml version + run: | + 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 \ + - name: Build cloudcompiler.wasm + shell: bash + run: | + git tag && + cat target/wasm32-wasi/release/cloudcompiler/wasmer.toml && + echo "ls" && + ls target/wasm32-wasi/release/cloudcompiler + - name: Publish to WAPM + uses: wasmerio/wapm-publish@v1 + with: + registry: https://registry.wapm.dev + directory: target/wasm32-wasi/release/cloudcompiler + username: ${{ secrets.WAPM_DEV_USERNAME }} + password: ${{ secrets.WAPM_DEV_PASSWORD }} diff --git a/arbitrator/tools/module_roots/wasmer/.github/workflows/documentation.yaml b/arbitrator/tools/module_roots/wasmer/.github/workflows/documentation.yaml new file mode 100644 index 0000000..c13e09d --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/workflows/documentation.yaml @@ -0,0 +1,40 @@ +name: Documentation + +on: + push: + branches: + - 'master' + paths: + - 'lib/**' + +env: + MSRV: "1.73" + +jobs: + documentation: + name: Documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.MSRV }} + - name: Install LLVM + shell: bash + run: | + curl --proto '=https' --tlsv1.2 -sSf ${{ env.LLVM_URL }} -L -o llvm.tar.xz + mkdir ${{ env.LLVM_DIR }} + tar xf llvm.tar.xz --strip-components=1 -C ${{ env.LLVM_DIR }} + echo "${{ env.LLVM_DIR }}/bin" >> $GITHUB_PATH + echo "LLVM_SYS_120_PREFIX=${{ env.LLVM_DIR }}" >> $GITHUB_ENV + env: + LLVM_DIR: ${{ github.workspace }}/llvm-13 + LLVM_URL: 'https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.0/clang+llvm-13.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz' + - name: Build & package documentation + run: make package-docs + - name: Publish documentation + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./package/docs diff --git a/arbitrator/tools/module_roots/wasmer/.github/workflows/test.yaml b/arbitrator/tools/module_roots/wasmer/.github/workflows/test.yaml new file mode 100644 index 0000000..573f712 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.github/workflows/test.yaml @@ -0,0 +1,825 @@ +name: test-sys + +on: + push: + branches: + - master + - 'with-ci-.*' + - 'v3.0.x' + - 'v3.1.x' + pull_request: + workflow_dispatch: + inputs: + release: + description: 'Make release' + +# Automatically cancel previous workflow runs when a new commit is pushed. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + RUST_BACKTRACE: 1 + # Sparse will be enabled by dtolnay/rust-toolchain when installing nightly + # Rust, but it's not stable on 1.69 yet. By explicitly setting the protocol we + # can override that behaviour + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: git + MSRV: "1.73" + NEXTEST_PROFILE: "ci" + +jobs: + + setup: + name: Set up + runs-on: ubuntu-22.04 + outputs: + VERSION: ${{ steps.setup.outputs.VERSION }} + DOING_RELEASE: ${{ steps.setup.outputs.DOING_RELEASE }} + steps: + - name: Set up env vars + id: setup + shell: bash + run: | + VERSION=${GITHUB_REF/refs\/tags\//} + echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT + DOING_RELEASE=$(echo $VERSION | grep -c '^[0-9]\+\.[0-9]\+\.[0-9]\+\(-\([a-zA-Z]\+\)\?[0-9]*\)\?$' || true) + echo "DOING_RELEASE=${DOING_RELEASE}" >> $GITHUB_OUTPUT + echo $VERSION + echo $DOING_RELEASE + + lint: + name: Code lint + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.MSRV }} + components: rustfmt, clippy + - name: Install libtinfo + shell: bash + run: | + sudo apt install -y libtinfo5 + - name: Install LLVM (Linux) + run: | + curl --proto '=https' --tlsv1.2 -sSf https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.6/clang+llvm-15.0.6-x86_64-linux-gnu-ubuntu-18.04.tar.xz -L -o /opt/llvm.tar.xz + mkdir -p /opt/llvm-15 + tar xf /opt/llvm.tar.xz --strip-components=1 -C /opt/llvm-15 + echo '/opt/llvm-15/bin' >> $GITHUB_PATH + echo 'LLVM_SYS_150_PREFIX=/opt/llvm-15' >> $GITHUB_ENV + - name: Cache + uses: whywaita/actions-cache-s3@v2 + with: + path: | + ~/.cargo/* + ./target/* + key: r22-${{ github.repository }}-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}-wasmer-make-lint-linux-x64 + aws-s3-bucket: wasmer-rust-artifacts-cache + aws-access-key-id: ${{ secrets.CLOUDFLARE_ARTIFACTS_CACHE_ACCESS_TOKEN }} + aws-secret-access-key: ${{ secrets.CLOUDFLARE_ARTIFACTS_CACHE_ACCESS_KEY }} + aws-region: auto + aws-endpoint: https://1541b1e8a3fc6ad155ce67ef38899700.r2.cloudflarestorage.com + aws-s3-bucket-endpoint: false + aws-s3-force-path-style: true + - run: make lint + env: + ENABLE_CRANELIFT: "1" + ENABLE_LLVM: "1" + ENABLE_SINGLEPASS: "1" + - name: Assert no files have changed + run: | + git status + ! [[ $(git status -s) ]] + + cargo_deny: + name: cargo-deny + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: EmbarkStudios/cargo-deny-action@v1 + with: + log-level: error + + test_nodejs: + name: Test on NodeJS + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.MSRV }} + - name: Install NodeJS + uses: actions/setup-node@v2 + with: + node-version: 16 + - name: Install wasm-pack + run: | + curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + - name: make test-js + run: | + make test-js + + test_wasi_fyi: + name: Test wasi-fyi + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: nightly + targets: "wasm32-wasi" + - name: Install wasm-pack + run: | + curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + - name: make test-wasi-fyi + run: | + make test-wasi-fyi + + # The no_std functionality doesn't work at the moment - no point in testing it. + # - name: make test-js-core + # run: | + # make test-js-core + + test_wasm_build: + name: Test wasm build + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - name: rustup target add wasm32-wasi + run: rustup target add wasm32-wasi + - name: make build-wasmer-wasm + run: make build-wasmer-wasm + + test_build_jsc: + name: Test JSC build + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.MSRV }} + target: x86_64-unknown-linux-gnu + - name: Install NodeJS + uses: actions/setup-node@v2 + with: + node-version: 16 + - name: Install libjavascriptcoregtk-4.0-dev + run: sudo apt update && sudo apt install -y libjavascriptcoregtk-4.0-dev + - name: make build-wasmer-jsc + run: make build-wasmer-jsc + + test_build_docs_rs: + name: Test build docs rs + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: "nightly-2023-10-05" + target: x86_64-unknown-linux-gnu + - run: cargo install toml-cli # toml-cli is required to run `make test-build-docs-rs` + - name: make test-build-docs-rs-ci + run: make test-build-docs-rs-ci + + build_linux_aarch64: + name: ${{ matrix.build-what.name }} on linux-aarch64 + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + build-what: [ + { + key: capi, + build-cmd: 'make build-capi && make package-capi', + name: 'Build C-API' + }, + { + key: wasmer, + build-cmd: 'make build-wasmer && make package-wasmer && make tar-wasmer', + name: 'Build wasmer-cli' + } + ] + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.MSRV }} + target: aarch64-unknown-linux-gnu + - name: Build cross image + run: | + docker build -t wasmer/aarch64 ${GITHUB_WORKSPACE}/.github/cross-linux-aarch64/ + env: + CROSS_DOCKER_IN_DOCKER: true + - name: Build ${{ matrix.build-what.key }} + run: | + ${{ matrix.build-what.build-cmd }} + env: + CARGO_BINARY: docker run -v /var/run/docker.sock:/var/run/docker.sock -v ${GITHUB_WORKSPACE}:/project -w /project wasmer/aarch64 cross + CROSS_DOCKER_IN_DOCKER: true + CARGO_TARGET: aarch64-unknown-linux-gnu + PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig + PKG_CONFIG_ALLOW_CROSS: true + ENABLE_LLVM: 0 + - name: Dist + if: ${{ matrix.build-what.key == 'capi' }} + run: | + make distribution + env: + CARGO_BINARY: docker run -v /var/run/docker.sock:/var/run/docker.sock -v ${GITHUB_WORKSPACE}:/project -w /project wasmer/aarch64 cross + CROSS_DOCKER_IN_DOCKER: true + CARGO_TARGET: aarch64-unknown-linux-gnu + PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig + PKG_CONFIG_ALLOW_CROSS: true + TARGET: aarch64-unknown-linux-gnu + TARGET_DIR: target/aarch64-unknown-linux-gnu/release + - name: Upload Artifacts + if: ${{ matrix.build-what.key == 'capi' }} + uses: actions/upload-artifact@v3 + with: + name: capi-linux-aarch64 + path: dist + if-no-files-found: error + retention-days: 2 + + build_linux_riscv64: + name: ${{ matrix.build-what.name }} on linux-riscv64 + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + build-what: [ + { + key: capi, + build-cmd: 'make build-capi && make package-capi', + name: 'Build C-API' + }, + { + key: wasmer, + build-cmd: 'make build-wasmer && make package-wasmer && make tar-wasmer', + name: 'Build wasmer-cli' + } + ] + steps: + - uses: actions/checkout@v3 + #- uses: dtolnay/rust-toolchain@stable + # with: + # toolchain: ${{ env.MSRV }} + # target: riscv64gc-unknown-linux-gnu + - name: Build cross image + run: | + docker build -t wasmer/riscv64 ${GITHUB_WORKSPACE}/.github/cross-linux-riscv64/ + env: + CROSS_DOCKER_IN_DOCKER: true + - name: Build ${{ matrix.build-what.key }} + run: | + ${{ matrix.build-what.build-cmd }} + env: + CARGO_BINARY: docker run -v /var/run/docker.sock:/var/run/docker.sock -v ${GITHUB_WORKSPACE}:/project -w /project wasmer/riscv64 cargo + CROSS_DOCKER_IN_DOCKER: true + CARGO_TARGET: riscv64gc-unknown-linux-gnu + ENABLE_LLVM: 0 + - name: Dist + if: ${{ matrix.build-what.key == 'capi' }} + run: | + make distribution + env: + CARGO_BINARY: docker run -v /var/run/docker.sock:/var/run/docker.sock -v ${GITHUB_WORKSPACE}:/project -w /project wasmer/riscv64 cargo + CROSS_DOCKER_IN_DOCKER: true + CARGO_TARGET: riscv64gc-unknown-linux-gnu + PKG_CONFIG_PATH: /usr/lib/riscv64-linux-gnu/pkgconfig + PKG_CONFIG_ALLOW_CROSS: true + TARGET: riscv64gc-unknown-linux-gnu + TARGET_DIR: target/riscv64gc-unknown-linux-gnu/release + - name: Upload Artifacts + if: ${{ matrix.build-what.key == 'capi' }} + uses: actions/upload-artifact@v3 + with: + name: capi-linux-riscv64 + path: dist + if-no-files-found: error + retention-days: 2 + + build: + name: ${{ matrix.build-what.name }} on ${{ matrix.metadata.build }} + runs-on: ${{ matrix.metadata.os }} + needs: setup + strategy: + fail-fast: false + matrix: + build-what: [ + { + key: capi, + build-cmd: 'make build-capi && make build-capi-headless && make package-capi && make tar-capi', + name: 'Build and test C-API' + }, + { + key: wasmer, + build-cmd: 'make build-wasmer && make package-wasmer && make tar-wasmer', + name: 'Build wasmer-cli' + } + ] + metadata: [ + { + build: linux-x64, + os: ubuntu-22.04, + target: x86_64-unknown-linux-gnu, + llvm_url: 'https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.6/clang+llvm-15.0.6-x86_64-linux-gnu-ubuntu-18.04.tar.xz' + }, + { + build: linux-musl, + target: x86_64-unknown-linux-musl, + os: ubuntu-22.04, + container: 'alpine:latest' + }, + { + build: macos-x64, + os: macos-11, + target: x86_64-apple-darwin, + llvm_url: 'https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.7/clang+llvm-15.0.7-x86_64-apple-darwin21.0.tar.xz' + }, + { + build: macos-arm, + os: macos-11, + target: aarch64-apple-darwin, + }, + { + build: windows-x64, + os: windows-2019, + target: x86_64-pc-windows-msvc, + llvm_url: 'https://github.com/wasmerio/llvm-custom-builds/releases/download/15.x/llvm-windows-amd64.tar.xz' + }, + { + build: windows-gnu, + target: x86_64-pc-windows-gnu, + os: ubuntu-22.04, + } + ] + container: ${{ matrix.metadata.container }} + env: + SCCACHE_AZURE_BLOB_CONTAINER: wasmerstoragesccacheblob + SCCACHE_AZURE_CONNECTION_STRING: ${{ secrets.SCCACHE_AZURE_CONNECTION_STRING }} + steps: + - uses: actions/checkout@v3 + - name: Set up libstdc++ on Linux + if: matrix.metadata.build == 'linux-x64' + run: | + sudo apt-get update -y + sudo apt-get install -y --allow-downgrades libstdc++6 libtinfo5 + sudo apt-get install --reinstall g++ + - name: Set up base deps on musl + if: matrix.metadata.build == 'linux-musl' + run: ./scripts/alpine-linux-install-deps.sh + - name: Set up dependencies for Mac OS + run: | + brew install automake + # 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-latest' || matrix.metadata.os == 'macos-11.0' + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.MSRV }} + target: ${{ matrix.metadata.target }} + - name: Install Nextest + uses: taiki-e/install-action@nextest + - name: Install Windows-GNU linker + if: ${{ matrix.metadata.build == 'windows-gnu' }} + shell: bash + run: | + sudo apt install -y mingw-w64 + - name: Install Windows-GNU target + if: ${{ matrix.metadata.build == 'windows-gnu' }} + shell: bash + run: | + rustup target add x86_64-pc-windows-gnu + - name: Install Windows 10 SDK with xwin + if: ${{ matrix.metadata.build == 'windows-gnu' }} + shell: bash + run: | + mkdir -p /tmp/xwin + mkdir -p /tmp/xwindownload + mkdir -p /tmp/xwincache + git clone https://github.com/wasmerio/xwin --depth=1 /tmp/xwin + cargo build --release --manifest-path=/tmp/xwin/Cargo.toml + /tmp/xwin/target/release/xwin --accept-license --cache-dir /tmp/xwincache splat --output /tmp/xwindownload + mkdir -p /tmp/winsdk + cp /tmp/xwindownload/sdk/lib/10.0.20348/um/x86_64/WS2_32.lib /tmp/winsdk/ + cp /tmp/xwindownload/sdk/lib/10.0.20348/um/x86_64/KERNEL32.lib /tmp/winsdk/ + cp /tmp/xwindownload/sdk/lib/10.0.20348/um/x86_64/BCRYPT.lib /tmp/winsdk/ + cp /tmp/xwindownload/sdk/lib/10.0.20348/um/x86_64/ADVAPI32.lib /tmp/winsdk/ + cp /tmp/xwindownload/sdk/lib/10.0.20348/um/x86_64/USERENV.lib /tmp/winsdk/ + echo "WinSDK files:" + ls -laH /tmp/winsdk + echo "" + mkdir -p package + mkdir -p package/winsdk + cp -r /tmp/winsdk/* package/winsdk + - name: Install LLVM (macOS Apple Silicon) + if: matrix.metadata.os == 'macos-11.0' && !matrix.metadata.llvm_url + run: | + brew install llvm + - name: Install LLVM + if: matrix.metadata.llvm_url + shell: bash + run: | + curl --proto '=https' --tlsv1.2 -sSf ${{ matrix.metadata.llvm_url }} -L -o llvm.tar.xz + LLVM_DIR=$(pwd)/${{ env.LLVM_DIR }} + mkdir ${LLVM_DIR} + tar xf llvm.tar.xz --strip-components=1 -C ${LLVM_DIR} + echo "ENABLE_LLVM=1" >> $GITHUB_ENV + echo "${LLVM_DIR}/bin" >> $GITHUB_PATH + echo "${LLVM_DIR}/usr/bin" >> $GITHUB_PATH + echo "LLVM_SYS_150_PREFIX=${LLVM_DIR}" >> $GITHUB_ENV + env: + LLVM_DIR: .llvm + - name: Setup Rust target + shell: bash + run: | + mkdir -p .cargo + cat << EOF > .cargo/config.toml + [build] + target = "${{ matrix.metadata.target }}" + EOF + if: matrix.metadata.target + - name: which cargo + if: ${{ matrix.build-what.key == 'capi' && matrix.metadata.build == 'windows-x64' }} + run: which cargo + - name: Set cargo env + run: echo "CARGO_ROOT_DIR=$(dirname $(dirname $( which cargo )))" >> $GITHUB_ENV + - name: List root dir + shell: bash + if: ${{ matrix.build-what.key == 'capi' && matrix.metadata.build == 'windows-x64' }} + run: ls -R $CARGO_ROOT_DIR + - name: Cache + uses: whywaita/actions-cache-s3@v2 + with: + path: | + ~/.cargo/* + ./target/* + $CARGO_ROOT_DIR/* + key: r22-${{ github.repository }}-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}-wasmer-make-build-wasmer-${{ matrix.build-what.key }}-${{ matrix.metadata.build }} + aws-s3-bucket: wasmer-rust-artifacts-cache + aws-access-key-id: ${{ secrets.CLOUDFLARE_ARTIFACTS_CACHE_ACCESS_TOKEN }} + aws-secret-access-key: ${{ secrets.CLOUDFLARE_ARTIFACTS_CACHE_ACCESS_KEY }} + aws-region: auto + aws-endpoint: https://1541b1e8a3fc6ad155ce67ef38899700.r2.cloudflarestorage.com + aws-s3-bucket-endpoint: false + aws-s3-force-path-style: true + - name: Build C-API + shell: bash + run: ${{ matrix.build-what.build-cmd }} + if: ${{ matrix.build-what.key == 'capi' }} + env: + TARGET: ${{ matrix.metadata.target }} + TARGET_DIR: target/${{ matrix.metadata.target }}/release + CARGO_TARGET: ${{ matrix.metadata.target }} + - name: Build Wasmer + shell: bash + if: ${{ matrix.build-what.key == 'wasmer' && matrix.metadata.build != 'windows-gnu' }} + run: ${{ matrix.build-what.build-cmd }} + env: + TARGET: ${{ matrix.metadata.target }} + TARGET_DIR: target/${{ matrix.metadata.target }}/release + CARGO_TARGET: ${{ matrix.metadata.target }} + - name: Test C-API + shell: bash + if: ${{ matrix.build-what.key == 'capi' && !(matrix.metadata.build == 'linux-musl' || matrix.metadata.build == 'macos-arm' || matrix.metadata.build == 'windows-gnu') }} + run: make test-capi-ci + env: + TARGET: ${{ matrix.metadata.target }} + TARGET_DIR: target/${{ matrix.metadata.target }}/release + CARGO_TARGET: ${{ matrix.metadata.target }} + # C-API tests were disabled for linux-musl and macos-arm (we can't run them) + - name: Test C-API integration + shell: bash + if: ${{ matrix.build-what.key == 'capi' && !(matrix.metadata.build == 'linux-musl' || matrix.metadata.build == 'macos-arm' || matrix.metadata.build == 'windows-gnu') }} + run: export WASMER_DIR=`pwd`/package && make test-stage-7-capi-integration-tests + env: + TARGET: ${{ matrix.metadata.target }} + TARGET_DIR: target/${{ matrix.metadata.target }}/release + CARGO_TARGET: ${{ matrix.metadata.target }} + - name: Archive production artifacts + uses: actions/upload-artifact@v3 + with: + name: wasmer-cli-${{ matrix.metadata.build }} + path: build-wasmer.tar.gz + if-no-files-found: ignore + retention-days: 2 + - name: Archive production artifacts + uses: actions/upload-artifact@v3 + with: + name: capi-${{ matrix.metadata.build }} + path: build-capi.tar.gz + if-no-files-found: ignore + retention-days: 2 + + test: + name: ${{ matrix.stage.description }} on ${{ matrix.metadata.build }} + runs-on: ${{ matrix.metadata.os }} + needs: setup + strategy: + fail-fast: false + matrix: + stage: [ + { + description: 'Run wast test suite for all compilers', + make: 'test-stage-0-wast', + }, + { + description: 'Unit-test packages on std', + make: 'test-stage-1-test-all', + }, + { + description: 'Unit-test cranelift on no-std', + make: 'test-stage-2-test-compiler-cranelift-nostd', + }, + { + description: 'Unit-test singlepass on no-std', + make: 'test-stage-3-test-compiler-singlepass-nostd', + }, + { + description: 'Unit-test wasmer-cli', + make: 'test-stage-4-wasmer-cli', + }, + { + description: 'Unit-test examples', + make: 'test-stage-5-test-examples', + } + ] + metadata: [ + # We cannot test on macos-arm since we don't have ARM runners + { + build: linux-x64, + os: ubuntu-22.04, + target: x86_64-unknown-linux-gnu, + exe: '', + llvm_url: 'https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.6/clang+llvm-15.0.6-x86_64-linux-gnu-ubuntu-18.04.tar.xz' + }, + { + build: macos-x64, + os: macos-11, + target: x86_64-apple-darwin, + exe: '', + llvm_url: 'https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.7/clang+llvm-15.0.7-x86_64-apple-darwin21.0.tar.xz' + }, + { + build: macos-arm64, + os: macos-14, + target: aarch64-apple-darwin, + exe: '', + }, + { + build: windows-x64, + os: windows-2019, + target: x86_64-pc-windows-msvc, + exe: '.exe', + llvm_url: 'https://github.com/wasmerio/llvm-custom-builds/releases/download/15.x/llvm-windows-amd64.tar.xz' + }, + { + build: linux-musl, + target: x86_64-unknown-linux-musl, + os: ubuntu-22.04, + exe: '', + container: 'alpine:latest' + } + ] + container: ${{ matrix.metadata.container }} + env: + SCCACHE_AZURE_BLOB_CONTAINER: wasmerstoragesccacheblob + SCCACHE_AZURE_CONNECTION_STRING: ${{ secrets.SCCACHE_AZURE_CONNECTION_STRING }} + steps: + - uses: actions/checkout@v3 + - name: Set up libstdc++ on Linux + if: matrix.metadata.build == 'linux-x64' + run: | + sudo apt-get update -y + sudo apt-get install -y --allow-downgrades libstdc++6 + sudo apt-get install --reinstall g++ + - name: Set up base deps on musl + if: matrix.metadata.build == 'linux-musl' + run: ./scripts/alpine-linux-install-deps.sh + - name: Set up dependencies for Mac OS + run: | + brew install automake + # 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-latest' || matrix.metadata.os == 'macos-11.0' + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.MSRV }} + target: ${{ matrix.metadata.target }} + - name: Install Nextest + uses: taiki-e/install-action@nextest + - name: Install LLVM (macOS Apple Silicon) + if: matrix.metadata.os == 'macos-11.0' && !matrix.metadata.llvm_url + run: | + brew install llvm + - name: Install LLVM + if: matrix.metadata.llvm_url + shell: bash + run: | + curl --proto '=https' --tlsv1.2 -sSf ${{ matrix.metadata.llvm_url }} -L -o llvm.tar.xz + LLVM_DIR=$(pwd)/${{ env.LLVM_DIR }} + mkdir ${LLVM_DIR} + tar xf llvm.tar.xz --strip-components=1 -C ${LLVM_DIR} + echo "${LLVM_DIR}/bin" >> $GITHUB_PATH + echo "LLVM_SYS_120_PREFIX=${LLVM_DIR}" >> $GITHUB_ENV + env: + LLVM_DIR: .llvm + - name: Setup Rust target + shell: bash + run: | + mkdir -p .cargo + cat << EOF > .cargo/config.toml + [build] + target = "${{ matrix.metadata.target }}" + EOF + if: matrix.metadata.target + - name: Cache + uses: whywaita/actions-cache-s3@v2 + with: + path: | + ~/.cargo/* + ./target/* + key: r22-${{ github.repository }}-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}-wasmer-make-test-stage-${{ matrix.stage.make }}-${{ matrix.metadata.build }} + aws-s3-bucket: wasmer-rust-artifacts-cache + aws-access-key-id: ${{ secrets.CLOUDFLARE_ARTIFACTS_CACHE_ACCESS_TOKEN }} + aws-secret-access-key: ${{ secrets.CLOUDFLARE_ARTIFACTS_CACHE_ACCESS_KEY }} + aws-region: auto + aws-endpoint: https://1541b1e8a3fc6ad155ce67ef38899700.r2.cloudflarestorage.com + aws-s3-bucket-endpoint: false + aws-s3-force-path-style: true + - name: ${{ matrix.stage.description }} + run: make ${{ matrix.stage.make }} + env: + TARGET: ${{ matrix.metadata.target }} + TARGET_DIR: target/${{ matrix.metadata.target }}/release + CARGO_TARGET: ${{ matrix.metadata.target }} + + test_integration_cli: + name: CLI integration tests on ${{ matrix.build }} + runs-on: ${{ matrix.os }} + needs: [build, build_linux_aarch64, build_linux_riscv64] + strategy: + fail-fast: false + matrix: + include: + - build: linux-x64 + os: ubuntu-22.04 + target: x86_64-unknown-linux-gnu + llvm_url: 'https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.6/clang+llvm-15.0.6-x86_64-linux-gnu-ubuntu-18.04.tar.xz' + - build: macos-x64 + os: macos-11 + target: x86_64-apple-darwin + llvm_url: 'https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.7/clang+llvm-15.0.7-x86_64-apple-darwin21.0.tar.xz' + # we only build the integration-test CLI, we don't run tests + - build: macos-arm + os: macos-11 + target: aarch64-apple-darwin, + - build: linux-musl + target: x86_64-unknown-linux-musl + os: ubuntu-22.04 + container: alpine:latest + - build: windows-x64 + os: windows-2019 + target: x86_64-pc-windows-msvc + container: ${{ matrix.container }} + env: + SCCACHE_AZURE_BLOB_CONTAINER: wasmerstoragesccacheblob + SCCACHE_AZURE_CONNECTION_STRING: ${{ secrets.SCCACHE_AZURE_CONNECTION_STRING }} + steps: + - uses: actions/checkout@v3 + - uses: goto-bus-stop/setup-zig@v2 + with: + version: 0.10.0 + - name: Set up base deps on musl + if: matrix.build == 'linux-musl' + run: ./scripts/alpine-linux-install-deps.sh + - uses: actions/download-artifact@v3 + id: download + with: + name: capi-${{ matrix.build }} + - uses: actions/download-artifact@v3 + with: + name: wasmer-cli-${{ matrix.build }} + - name: 'Echo download path' + run: echo ${{steps.download.outputs.download-path}} + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.MSRV }} + target: ${{ matrix.metadata.target }} + - name: Install Nextest + uses: taiki-e/install-action@nextest + - name: Cache + uses: whywaita/actions-cache-s3@v2 + with: + path: | + ~/.cargo/* + ./target/* + key: r22-${{ github.repository }}-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}-wasmer-make-test-integration-cli-${{ matrix.build }} + aws-s3-bucket: wasmer-rust-artifacts-cache + aws-access-key-id: ${{ secrets.CLOUDFLARE_ARTIFACTS_CACHE_ACCESS_TOKEN }} + aws-secret-access-key: ${{ secrets.CLOUDFLARE_ARTIFACTS_CACHE_ACCESS_KEY }} + aws-region: auto + aws-endpoint: https://1541b1e8a3fc6ad155ce67ef38899700.r2.cloudflarestorage.com + aws-s3-bucket-endpoint: false + aws-s3-force-path-style: true + - name: Prepare package directory + shell: bash + run: | + mkdir -p package + mkdir -p package/cache + - uses: actions/download-artifact@v3 + with: + name: capi-linux-aarch64 + path: package/cache/wasmercache1 + - uses: actions/download-artifact@v3 + with: + name: capi-windows-gnu + path: package/cache/wasmercache2 + - uses: actions/download-artifact@v3 + with: + name: capi-macos-arm + path: package/cache/wasmercache3 + - uses: actions/download-artifact@v3 + with: + name: capi-macos-x64 + path: package/cache/wasmercache4 + - uses: actions/download-artifact@v3 + with: + name: capi-linux-x64 + path: package/cache/wasmercache5 + - uses: actions/download-artifact@v3 + with: + name: capi-linux-riscv64 + path: package/cache/wasmercache6 + - name: Copy .tar.gz files to proper location + shell: bash + run: | + ls package/cache/wasmercache1 + ls package/cache/wasmercache2 + ls package/cache/wasmercache3 + ls package/cache/wasmercache4 + ls package/cache/wasmercache5 + cp package/cache/wasmercache1/wasmer.tar.gz package/cache/wasmer-linux-aarch64.tar.gz + cp package/cache/wasmercache2/build-capi.tar.gz package/cache/wasmer-windows-gnu64.tar.gz + cp package/cache/wasmercache3/build-capi.tar.gz package/cache/wasmer-darwin-arm64.tar.gz + cp package/cache/wasmercache4/build-capi.tar.gz package/cache/wasmer-darwin-amd64.tar.gz + cp package/cache/wasmercache5/build-capi.tar.gz package/cache/wasmer-linux-amd64.tar.gz + cp package/cache/wasmercache6/wasmer.tar.gz package/cache/wasmer-linux-riscv64.tar.gz + - uses: actions/download-artifact@v3 + if: ${{ matrix.build == 'windows-x64' }} + with: + name: capi-windows-gnu + path: download_link + - uses: actions/download-artifact@v3 + if: ${{ matrix.build == 'linux-musl' }} + with: + name: capi-linux-musl + path: download_link + - uses: actions/download-artifact@v3 + if: ${{ matrix.build == 'macos-arm' }} + with: + name: capi-macos-arm + path: download_link + - uses: actions/download-artifact@v3 + if: ${{ matrix.build == 'macos-x64' }} + with: + name: capi-macos-x64 + path: download_link + - uses: actions/download-artifact@v3 + if: ${{ matrix.build == 'linux-x64' }} + with: + name: capi-linux-x64 + path: download_link + - name: Copy build-capi.tar.gz to link.tar.gz + shell: bash + run: | + cp download_link/build-capi.tar.gz link.tar.gz + - name: Unzip Artifacts + shell: bash + run: | + make untar-capi + - name: Unzip Artifacts + shell: bash + run: | + make untar-wasmer + - name: Test integration CLI + if: matrix.build != 'macos-arm' + shell: bash + run: | + export WASMER_PATH=`pwd`/target/${{ matrix.target }}/release/wasmer${{ matrix.exe }} + export WASMER_DIR=`pwd`/package && make test-integration-cli-ci + env: + TARGET: ${{ matrix.target }} + TARGET_DIR: target/${{ matrix.target }}/release + CARGO_TARGET: ${{ matrix.target }} + WAPM_DEV_TOKEN: ${{ secrets.WAPM_DEV_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/arbitrator/tools/module_roots/wasmer/.gitignore b/arbitrator/tools/module_roots/wasmer/.gitignore new file mode 100644 index 0000000..ed9fd0c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.gitignore @@ -0,0 +1,30 @@ +**/target +**/*.rs.bk +.DS_Store +.idea +.gdb_history +/.cargo/ +**/.vscode/* +!/.vscode/settings.json +api-docs-repo/ +/.cargo_home/ +/package/ +/dist/ +/wapm-cli/ +/src/windows-installer/WasmerInstaller.exe +/lib/c-api/wasmer.h +.xwin-cache +wapm.toml +wasmer.toml +*.snap.new +# Generated by tests on Android +/avd +/core +/vendor +out.txt +wapm.toml +build-capi.tar.gz +build-wasmer.tar.gz +lcov.info +link/ +link.tar.gz diff --git a/arbitrator/tools/module_roots/wasmer/.tarpaulin.toml b/arbitrator/tools/module_roots/wasmer/.tarpaulin.toml new file mode 100644 index 0000000..08fe94c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/.tarpaulin.toml @@ -0,0 +1,17 @@ +[cranelift_coverage] +features = "cranelift,singlepass,llvm,coverage,test-cranelift,test-jit" +examples = ["early-exit", "engine-jit", "engine-native", "engine-headless", "cross-compilation", "compiler-cranelift", "exported-function", "wasi"] +release = true + +[llvm_coverage] +features = "cranelift,singlepass,llvm,coverage,test-llvm,test-jit" +examples = ["compiler-llvm"] +release = true + +[singlepass_coverage] +features = "cranelift,singlepass,llvm,coverage,test-singlepass,test-jit" +examples = ["compiler-singlepass"] +release = true + +[report] +out = ["Xml"] diff --git a/arbitrator/tools/module_roots/wasmer/ATTRIBUTIONS.md b/arbitrator/tools/module_roots/wasmer/ATTRIBUTIONS.md new file mode 100644 index 0000000..1f1cca7 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/ATTRIBUTIONS.md @@ -0,0 +1,605 @@ +# Wasmer Attributions + +Wasmer is a community effort and makes use of code from various other +projects ❤ī¸. +Listed below are notable sections of code that are licensed +from other projects and the relevant license of those projects. + +These are the projects that were used as inspiration and/or that we are using code from. +Each of the subcrates we have have an `Aknowledgements` section with more details. + +Projects: + +- [Emscripten](https://github.com/kripken/emscripten): for emtests test sources to ensure compatibility - [LICENSE](#emscripten) +- [Nebulet](https://github.com/nebulet/nebulet): as the base for creating a great Rust WebAssembly runtime - [LICENSE](#nebulet) +- [WAVM](https://github.com/wavm/wavm): for their great integration and testing framework - [LICENSE](#wavm) +- [wasmtime](https://github.com/CraneStation/wasmtime): for their API and internal documentation, as well as some internal implementations - [LICENSE](#wasmtime) +- [WebAssembly spec](https://github.com/WebAssembly/spec/tree/master/test): for the spectests implementation + +🙏 Please let us know if you believe there is an error or omission in +this list and we will correct. + +## Licenses + +### Nebulet + +```text +MIT License + +Copyright (c) 2018 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +### WAVM + +```text +Copyright (c) 2018, Andrew Scheidecker +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of WAVM nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The contents of [Test/spec](Test/spec) is covered by the license in [Test/spec/LICENSE](Test/spec/LICENSE). + +[Source/ThirdParty/dtoa/dtoa.c](Source/ThirdParty/dtoa/dtoa.c) is covered by the license in that file. + +[Source/ThirdParty/libunwind](Source/ThirdParty/libunwind) is covered by the license in [Source/ThirdParty/libunwind/LICENSE.TXT](Source/ThirdParty/libunwind/LICENSE.TXT). + +[Source/ThirdParty/xxhash](Source/ThirdParty/xxhash) is covered by the license in [Source/ThirdParty/xxhash/LICENSE](Source/ThirdParty/xxhash/LICENSE). +``` + +### Greenwasm + +```text + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` + +### Wasmtime + +```text + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. +``` + +### Emscripten +```text +Emscripten is available under 2 licenses, the MIT license and the +University of Illinois/NCSA Open Source License. + +Both are permissive open source licenses, with little if any +practical difference between them. + +The reason for offering both is that (1) the MIT license is +well-known, while (2) the University of Illinois/NCSA Open Source +License allows Emscripten's code to be integrated upstream into +LLVM, which uses that license, should the opportunity arise. + +The full text of both licenses follows. + +============================================================================== + +Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +============================================================================== + +Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file. +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal with the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimers. + + Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimers + in the documentation and/or other materials provided with the + distribution. + + Neither the names of Mozilla, + nor the names of its contributors may be used to endorse + or promote products derived from this Software without specific prior + written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. + +============================================================================== + +This program uses portions of Node.js source code located in src/library_path.js, +in accordance with the terms of the MIT license. Node's license follows: + + """ + Copyright Joyent, Inc. and other Node contributors. All rights reserved. + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. + """ + +The musl libc project is bundled in this repo, and it has the MIT license, see +system/lib/libc/musl/COPYRIGHT + +The third_party/ subdirectory contains code with other licenses. None of it is +used by default, but certain options use it (e.g., the optional closure compiler +flag will run closure compiler from third_party/). + +``` diff --git a/arbitrator/tools/module_roots/wasmer/CHANGELOG.md b/arbitrator/tools/module_roots/wasmer/CHANGELOG.md new file mode 100644 index 0000000..070cf7c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/CHANGELOG.md @@ -0,0 +1,1907 @@ +# Changelog + +*The format is based on [Keep a Changelog].* + +[Keep a Changelog]: http://keepachangelog.com/en/1.0.0/ + +Looking for changes that affect our C API? See the [C API Changelog](lib/c-api/CHANGELOG.md). + + +## **Unreleased** + +## 4.2.8 - 05/04/2024 + +This release improves journal support and improves the performance of the singlepass backend. +Also contains fixes to the Edge CLI. + +## Added + + - [#4510](https://github.com/wasmerio/wasmer/pull/4510) Added support for creating log file journals directly from buffers + - [#4506](https://github.com/wasmerio/wasmer/pull/4506) feat: add wasmer-argus + - [#4508](https://github.com/wasmerio/wasmer/pull/4508) Upgrade edge-{schema,util} crates + add some helper methdos + +## Changed + + - [#4541](https://github.com/wasmerio/wasmer/pull/4541) Removed some dead code + - [#4539](https://github.com/wasmerio/wasmer/pull/4539) deps: Upgrade h2 due to RUSTSEC advisory + - [#4527](https://github.com/wasmerio/wasmer/pull/4527) allow owner field in app.yaml + - [#4526](https://github.com/wasmerio/wasmer/pull/4526) feat(singlepass): use SIMD insts for popcount + - [#4507](https://github.com/wasmerio/wasmer/pull/4507) deps: Upgrade edge-schema to 0.0.3 + - [#4462](https://github.com/wasmerio/wasmer/pull/4462) DProxy + +## Fixed + + - [#4542](https://github.com/wasmerio/wasmer/pull/4542) Various fixes detected in the build + - [#4537](https://github.com/wasmerio/wasmer/pull/4537) Fix owner issues with app create + - [#4535](https://github.com/wasmerio/wasmer/pull/4535) fix(cli): Fix Edge WinterJS template + - [#4525](https://github.com/wasmerio/wasmer/pull/4525) Fix bug with `app deploy`: app URL is stale + - [#4520](https://github.com/wasmerio/wasmer/pull/4520) Fix singlepass panic + + + +## 4.2.7 - 19/03/2024 + +This release adds the `wasmer domain` command for DNS records management, and also includes an important fix to the `stack_restore` WASIX syscall (used by the `longjmp` function). + +## Added + + - [#4478](https://github.com/wasmerio/wasmer/pull/4478) chore(backend-api): Add size to PackageDistribution + +## Changed + + - [#4492](https://github.com/wasmerio/wasmer/pull/4492) No longer restoring the thread local memory when we longjmp + - [#4487](https://github.com/wasmerio/wasmer/pull/4487) Manage DNS records + - [#4220](https://github.com/wasmerio/wasmer/pull/4220) Ability to detect a tainted instance + - [#4455](https://github.com/wasmerio/wasmer/pull/4455) Implemented an exponential CPU backoff that kicks in when a run token is not held + - [#4470](https://github.com/wasmerio/wasmer/pull/4470) chore: Completely remove wasix_http_client + +## Fixed + + - [#4490](https://github.com/wasmerio/wasmer/pull/4490) Fix for a panic in the sock_recv when a file handle is missing + - [#4335](https://github.com/wasmerio/wasmer/pull/4335) Fixed an issue where the package loader was blocking the tokio runtime + - [#4473](https://github.com/wasmerio/wasmer/pull/4473) fix: fix feature = "cargo-clippy" deprecation + + + +## 4.2.6 - 03/03/2024 + +This release includes a number of DX improvements for the Wasmer CLI, as well as fixes to WASI and its filesystem implementation. + +## Added + + - [#4459](https://github.com/wasmerio/wasmer/pull/4459) feat(backend-api): Add download_url to PackageDistribution + - [#4460](https://github.com/wasmerio/wasmer/pull/4460) fix(cli): Add missing output in "app get" command and fix output of "app info" + - [#4448](https://github.com/wasmerio/wasmer/pull/4448) Add wasi-fyi tests and run them in CI + - [#4454](https://github.com/wasmerio/wasmer/pull/4454) add pagination to app list queries + - [#4443](https://github.com/wasmerio/wasmer/pull/4443) feat(backend-api): Add get_app_by_id_opt + - [#4385](https://github.com/wasmerio/wasmer/pull/4385) feat(api): Add SharedMemoryHandle + - [#3517](https://github.com/wasmerio/wasmer/pull/3517) Add HTTP example which does dynamic allocation + +## Changed + + - [#4472](https://github.com/wasmerio/wasmer/pull/4472) feat(cli): "wasmer deploy": return app version as json if --json specified + - [#4471](https://github.com/wasmerio/wasmer/pull/4471) chore: Remove wasi-experimental-io-devices + - [#4466](https://github.com/wasmerio/wasmer/pull/4466) Clean up wasmer-wasix-types and remove wasix-http-client + - [#4458](https://github.com/wasmerio/wasmer/pull/4458) tests: Disable flaky network tests on MUSL + - [#4440](https://github.com/wasmerio/wasmer/pull/4440) cli: allow filtering by log stream + - [#4435](https://github.com/wasmerio/wasmer/pull/4435) Use M1 runner for building & testing m1 + - [#4434](https://github.com/wasmerio/wasmer/pull/4434) Update Rust toolchain to 1.73 + - [#4428](https://github.com/wasmerio/wasmer/pull/4428) Try to build the package before publishing it + - [#4441](https://github.com/wasmerio/wasmer/pull/4441) increase pagination size for app logs + - [#4429](https://github.com/wasmerio/wasmer/pull/4429) Return Notsup for absolute symlinks instead of panicking + - [#4427](https://github.com/wasmerio/wasmer/pull/4427) Migrate deprecated usage + - [#4426](https://github.com/wasmerio/wasmer/pull/4426) deps: Unify wasmparser usage to a single version + - [#4292](https://github.com/wasmerio/wasmer/pull/4292) deps: Upgrade wasmparser + - [#4398](https://github.com/wasmerio/wasmer/pull/4398) Make public API consistent + - [#4407](https://github.com/wasmerio/wasmer/pull/4407) CLI: Big CLI cleanup + merge Edge CLI + - [#4395](https://github.com/wasmerio/wasmer/pull/4395) chore(wasix): Increase log level for WapmSource + - [#4403](https://github.com/wasmerio/wasmer/pull/4403) Allow users to provide custom host functions to `WasiRunner` and `WasiEnvBuilder` + - [#4359](https://github.com/wasmerio/wasmer/pull/4359) Use nanoseconds in `filestat` for directories + - [#4100](https://github.com/wasmerio/wasmer/pull/4100) Tweak logging annotations to simplify performance troubleshooting + - [#4175](https://github.com/wasmerio/wasmer/pull/4175) Update dependencies and remove unused dependencies + - [#4302](https://github.com/wasmerio/wasmer/pull/4302) Allow `WasiRunner` to mount `FileSystem` instances + - [#4394](https://github.com/wasmerio/wasmer/pull/4394) ignore append when truncate is set + - [#4263](https://github.com/wasmerio/wasmer/pull/4263) WASI journal and stateful persistence + - [#4389](https://github.com/wasmerio/wasmer/pull/4389) remove memory footprint computation + - [#4352](https://github.com/wasmerio/wasmer/pull/4352) Wait for webc, bindings, exe generation during package-publish with `--wait` flag + - [#4388](https://github.com/wasmerio/wasmer/pull/4388) Temporarily exclude tokio from docs builds + - [#4345](https://github.com/wasmerio/wasmer/pull/4345) Asyncify filesystem cache + +## Fixed + + - [#4467](https://github.com/wasmerio/wasmer/pull/4467) fix(cli): Fix auto-package version bumping in 'wasmer deploy' + - [#4452](https://github.com/wasmerio/wasmer/pull/4452) CLI: Fix logic error in indexing in app logs command + - [#4444](https://github.com/wasmerio/wasmer/pull/4444) Fix note about unaligned references in `WasmRef` docs + - [#4338](https://github.com/wasmerio/wasmer/pull/4338) [SINGLEPASS]Fix bug #4092 which was due to resource leak while doing â€Ļ + - [#4430](https://github.com/wasmerio/wasmer/pull/4430) deps: Fix accidental hyper 1.0 upgrade + remove some duplicate dependencies + - [#4322](https://github.com/wasmerio/wasmer/pull/4322) fix: Prevent panicking in VirtualTaskManagerExt::spawn_and_block_on + - [#4411](https://github.com/wasmerio/wasmer/pull/4411) chore: Fix clippy lints for headless CLI build + - [#4410](https://github.com/wasmerio/wasmer/pull/4410) fix(cli): publish: Fix panic + Make waiting for build results more modular + - [#4402](https://github.com/wasmerio/wasmer/pull/4402) Fixed some compilation errors introduced by #4204 that assume `tokio::task::block_in_place()` always exists + - [#3448](https://github.com/wasmerio/wasmer/pull/3448) Fix make install-capi-lib + - [#4393](https://github.com/wasmerio/wasmer/pull/4393) fix(api): Provide stub impls for new LinearMemory methods + - [#4391](https://github.com/wasmerio/wasmer/pull/4391) Fixed an issue where the caching compiled modules were not saving properly + - [#4386](https://github.com/wasmerio/wasmer/pull/4386) chore: Fix clippy lints in API crate + - [#4387](https://github.com/wasmerio/wasmer/pull/4387) fix: Don't log the `FmtSpan::ENTER` event because it generates unnecessary logs + + + +## 4.2.5 - 23/12/2023 + +This release includes substantial improvements to Wasmer's startup time when loading cached modules. Also includes a couple of filesystem-related fixes and updates the Edge application templates. + +## Added + + - [#4370](https://github.com/wasmerio/wasmer/pull/4370) feat(wasix): Add optional chunk timeout to ReqwestHttpClient + - [#4369](https://github.com/wasmerio/wasmer/pull/4369) WASIX: Add QueryError::Timeout + - [#4336](https://github.com/wasmerio/wasmer/pull/4336) fix(docs): add cpp in Chinese + +## Changed + + - [#4372](https://github.com/wasmerio/wasmer/pull/4372) use faster hash algorithm when loading and saving a module + - [#4367](https://github.com/wasmerio/wasmer/pull/4367) deps: Bump edge cli to 0.1.4 + - [#4366](https://github.com/wasmerio/wasmer/pull/4366) Always use the download URL provided by the Wasmer registry instead of going through the frontend redirect + - [#4350](https://github.com/wasmerio/wasmer/pull/4350) Optimize sleep_now for TokioTaskManager + - [#4351](https://github.com/wasmerio/wasmer/pull/4351) Remove the wasmer-web crate and all tests + - [#4229](https://github.com/wasmerio/wasmer/pull/4229) Use `cargo nextest` when running tests in CI + - [#4339](https://github.com/wasmerio/wasmer/pull/4339) deps: Bump edge-cli version + +## Fixed + + - [#4368](https://github.com/wasmerio/wasmer/pull/4368) fix(wasix): http client: use reasonable default connect timeout + - [#4365](https://github.com/wasmerio/wasmer/pull/4365) lib/api/src/js/store.rs: fix typo + - [#4356](https://github.com/wasmerio/wasmer/pull/4356) Fixed a subtract-with-overflow in `StaticFile::poll_read_ready()` + - [#4355](https://github.com/wasmerio/wasmer/pull/4355) Fix the `virtual_fs::StaticFile` implementation + - [#4348](https://github.com/wasmerio/wasmer/pull/4348) fix sync + + + +## 4.2.4 - 30/11/2023 + +This release allows publishing private packages and fixes the issue of the file system being accessible by WASI modules in the abscence of directory mapping. Also improves startup speed, and fixes multiple issues around the WASI filesystem, packages and apps. + +## Added + + - [#4334](https://github.com/wasmerio/wasmer/pull/4334) Add `application/wasm` to list of accepted content-types for webcs + - [#4328](https://github.com/wasmerio/wasmer/pull/4328) Add `--wait` and `--timeout` flags to `wamer publish` + - [#4315](https://github.com/wasmerio/wasmer/pull/4315) Add TTY aware output to the wasmer package and wasmer container commands + - [#4287](https://github.com/wasmerio/wasmer/pull/4287) feat(cli): Add package commands + - [#4247](https://github.com/wasmerio/wasmer/pull/4247) Add support for publishing private packages + - [#4291](https://github.com/wasmerio/wasmer/pull/4291) feat(cli): add pnpm support + +## Changed + + - [#4333](https://github.com/wasmerio/wasmer/pull/4333) deps: Bump edge-cli + - [#4332](https://github.com/wasmerio/wasmer/pull/4332) use rusty_pool instead of rayon + - [#4321](https://github.com/wasmerio/wasmer/pull/4321) deps(cli): Upgrade Edge CLI + - [#4326](https://github.com/wasmerio/wasmer/pull/4326) Always re-execute a registry query when cache lookups fail + - [#4317](https://github.com/wasmerio/wasmer/pull/4317) Bump min enumset version to 1.1.0 + - [#4300](https://github.com/wasmerio/wasmer/pull/4300) Use authentication when running a package + - [#4294](https://github.com/wasmerio/wasmer/pull/4294) Terminate after flushing file descriptors + - [#4273](https://github.com/wasmerio/wasmer/pull/4273) Update memoffset to 0.9.0 + +## Fixed + + - [#4307](https://github.com/wasmerio/wasmer/pull/4307) Fix for the non-flushing of file descriptors and a nasty deadlock + - [#4331](https://github.com/wasmerio/wasmer/pull/4331) Fix visibility validation to work when publishing a new package + - [#4314](https://github.com/wasmerio/wasmer/pull/4314) fix(cli): Prevent temporary file issues in "package download" + - [#4296](https://github.com/wasmerio/wasmer/pull/4296) fix: prevent potential UB by deriving repr C for union + - [#4192](https://github.com/wasmerio/wasmer/pull/4192) More fixes to support Wasmer JS + + + +## 4.2.3 - 26/10/2023 + +This new version fixes a bug in module bindings path. + +## Added + + +## Changed + + +## Fixed + + - [#4272](https://github.com/wasmerio/wasmer/pull/4272) fix: Fix async runtime mismatch in virtual "wasmer run" command + - [#4276](https://github.com/wasmerio/wasmer/pull/4276) fix: module path using stripping a prefix + - [#4241](https://github.com/wasmerio/wasmer/pull/4241) Fix FreeBSD ucontext_t issues + - [#4246](https://github.com/wasmerio/wasmer/pull/4246) Fix Cargo.lock for 4.2.2 release + + + +## 4.2.2 - 04/10/2023 +New wasmer version that utilizes the more recent LLVM15 to compile WASM modules. Also adds new application templates for Wasmer Edge. + +## Added + + +## Changed + + - [#4239](https://github.com/wasmerio/wasmer/pull/4239) Wasmer Deploy CLI with js and py worker templates + - [#4238](https://github.com/wasmerio/wasmer/pull/4238) Using hard coded package version number in test... + - [#4206](https://github.com/wasmerio/wasmer/pull/4206) Remove StdioMode::Reserved variant + - [#4234](https://github.com/wasmerio/wasmer/pull/4234) Migration to LLVM15 + - [#4236](https://github.com/wasmerio/wasmer/pull/4236) Updated tugstsenite + +## Fixed + + + + +## 4.2.1 - 28/09/2023 +New release of wasmer with improved WASIX compatibility. MSRV was bumped to 1.70 for this release. + +Now it is possible for packages to execute atoms from their dependencies, enabling e.g. python packages to not include the binary but use the one from the official python package. + +## Added + + - [#4213](https://github.com/wasmerio/wasmer/pull/4213) feat(wasix): Add BuiltinPackageLoader::insert_cached + - [#4202](https://github.com/wasmerio/wasmer/pull/4202) Add support for `JoinFlags::NON_BLOCKING` to `proc_join` + +## Changed + + - [#4223](https://github.com/wasmerio/wasmer/pull/4223) Allow packages to have commands that use a dependency's atom + - [#4224](https://github.com/wasmerio/wasmer/pull/4224) Use write instead of send for pipe, to accomodate socketpair + - [#4221](https://github.com/wasmerio/wasmer/pull/4221) Bump the MSRV from 1.69 to 1.70 + - [#4214](https://github.com/wasmerio/wasmer/pull/4214) Rephrase the docstring for `--forward-host-env` flag on `wasmer run` + - [#4215](https://github.com/wasmerio/wasmer/pull/4215) Ignore `fd_close(3)` to avoid breaking POSIX programs that blindly close all file descriptors + - [#4204](https://github.com/wasmerio/wasmer/pull/4204) Stability improvements + +## Fixed + + - [#4211](https://github.com/wasmerio/wasmer/pull/4211) Fixes for sockets + - [#4193](https://github.com/wasmerio/wasmer/pull/4193) Fix sockets + - [#4205](https://github.com/wasmerio/wasmer/pull/4205) Fix File System Merging Problems + + + +## 4.2.0 - 05/09/2023 +New release of wasmer, with a new 0-copy module deserialization for shorter startup time, some fixes to avoid misaligned pointer acces, and faster internal stack handling, among some other fixes. + +## Added + + - [#4199](https://github.com/wasmerio/wasmer/pull/4199) Added some Socket filtype return for fdstat syscall + - [#4186](https://github.com/wasmerio/wasmer/pull/4186) Add stdin/stdout/stderr streams to `WasiRunner` and only use async threading when requested + +## Changed + + - [#4170](https://github.com/wasmerio/wasmer/pull/4170) deps: Bump Edge client CLI to 0.1.25 + - [#4179](https://github.com/wasmerio/wasmer/pull/4179) Faster compiles for debug by using release version of cranelift + - [#4196](https://github.com/wasmerio/wasmer/pull/4196) Replace stack pool mutex with lock-free queue + - [#4180](https://github.com/wasmerio/wasmer/pull/4180) NativeEngineExt::deserialize now returns Module + - [#4190](https://github.com/wasmerio/wasmer/pull/4190) Early check that a cached artifact is compatible with current CPU Features + - [#4167](https://github.com/wasmerio/wasmer/pull/4167) Make sure vmoffset are aligned to pointer size (for #4059) + - [#4184](https://github.com/wasmerio/wasmer/pull/4184) Allow `VirtualTaskManager` to explicitly transfer a module to a blocking task on the threadpool + - [#4176](https://github.com/wasmerio/wasmer/pull/4176) Js integrity checks + - [#4171](https://github.com/wasmerio/wasmer/pull/4171) Revive "0-copy module deserialization" + - [#4173](https://github.com/wasmerio/wasmer/pull/4173) deps: Bump Edge CLI version + +## Fixed + + - [#4198](https://github.com/wasmerio/wasmer/pull/4198) chore: fix unavailable document url + - [#4191](https://github.com/wasmerio/wasmer/pull/4191) Fix invalid access to wasi instance handles in wasix proc_spawn + - [#4165](https://github.com/wasmerio/wasmer/pull/4165) Fix writing to middle of files and improve performance + + + +## 4.1.2 - 21/08/2023 +Another maintenance release, bringing some networking improvements, and centralising all wasmer caches under the same folder. + + +## Added + + - [#4127](https://github.com/wasmerio/wasmer/pull/4127) Add regression test for path_rename + +## Changed + + - [#4164](https://github.com/wasmerio/wasmer/pull/4164) deps: Bump Edge client CLI to 0.1.22 + - [#4163](https://github.com/wasmerio/wasmer/pull/4163) Bumped virtual-net crate to 0.5.0 + - [#4156](https://github.com/wasmerio/wasmer/pull/4156) Disable auto-format of toml files in VS Code + - [#4141](https://github.com/wasmerio/wasmer/pull/4141) Web http client + - [#4152](https://github.com/wasmerio/wasmer/pull/4152) Test virtual-net bincode and mpsc only on linux + - [#4115](https://github.com/wasmerio/wasmer/pull/4115) Browser cfg, smaller WASM, VPN and HTTPS redirect + - [#4138](https://github.com/wasmerio/wasmer/pull/4138) Upgrade wasmer-wasix to the newest version of wasm-bindgen + - [#4135](https://github.com/wasmerio/wasmer/pull/4135) Update to time 0.3 + - [#4109](https://github.com/wasmerio/wasmer/pull/4109) Update MSRV to 1.69 + - [#4130](https://github.com/wasmerio/wasmer/pull/4130) Update to tracing-subscriber 0.3 + +## Fixed + + - [#4160](https://github.com/wasmerio/wasmer/pull/4160) fix: Make the "wasmer cache" command use the same cache directory as the rest of the CLI + - [#4148](https://github.com/wasmerio/wasmer/pull/4148) fix(cli): Prevent panics in "wasmer login" after API failures + + + +## 4.1.1 - 03/08/2023 +Bug-fix release, fixing rename in wasi(x), using newer Rust and some macOS ARM64 speicifc issues, among other things. + +## Added + + - [#4120](https://github.com/wasmerio/wasmer/pull/4120) Added proper definition of ucontext for macos/aarch64 to avoid unaligned issue + - [#4107](https://github.com/wasmerio/wasmer/pull/4107) Added a forced shutdown on tokio runtimes as the STDIN blocks the shuâ€Ļ + - [#4108](https://github.com/wasmerio/wasmer/pull/4108) Add a temporary workaround for nanasess/setup-chromedriver#190 + +## Changed + + - [#4123](https://github.com/wasmerio/wasmer/pull/4123) Upgrade Edge CLI dependency + - [#4109](https://github.com/wasmerio/wasmer/pull/4109) Update MSRV to 1.69 + - [#4111](https://github.com/wasmerio/wasmer/pull/4111) Update login.rs + - [#4102](https://github.com/wasmerio/wasmer/pull/4102) Update to criterion 0.5 + +## Fixed + + - [#4121](https://github.com/wasmerio/wasmer/pull/4121) Fix path_rename syscall failing + - [#4117](https://github.com/wasmerio/wasmer/pull/4117) Fixed an issue where inheritance was inverted + - [#4096](https://github.com/wasmerio/wasmer/pull/4096) Fix benchmark + + + +## 4.1.0 - 24/07/2023 +This version added some more improvements and fixes, with a faster async execution, a new login flow and muliple bugfix to the `--mapdir` command among other things. +More detail in the blog post about the 4.1 Release: https://wasmer.io/posts/wasmer-4.1 + +## Added + + - [#4081](https://github.com/wasmerio/wasmer/pull/4081) Add WebcHash::parse_hex helper + - [#4046](https://github.com/wasmerio/wasmer/pull/4046) Add C API function to create Module from Engine instead of Store + +## Changed + + - [#4095](https://github.com/wasmerio/wasmer/pull/4095) Bumped the webc version + - [#4038](https://github.com/wasmerio/wasmer/pull/4038) Clean up the integration tests + - [#4085](https://github.com/wasmerio/wasmer/pull/4085) Switch to lazily loading a Wasmer package directly from disk + - [#4084](https://github.com/wasmerio/wasmer/pull/4084) Remove unused atty dependency + - [#4057](https://github.com/wasmerio/wasmer/pull/4057) login with browser using nonce + - [#4073](https://github.com/wasmerio/wasmer/pull/4073) deps(wasmer-wasix): Remove two unused dependencies + - [#4068](https://github.com/wasmerio/wasmer/pull/4068) Enable tokio's "net" feature in the virtual-net crate + - [#4065](https://github.com/wasmerio/wasmer/pull/4065) deps: Upgrade edge CLI version + - [#4064](https://github.com/wasmerio/wasmer/pull/4064) Enable the `cfg_doc` feature on docs.rs + - [#4052](https://github.com/wasmerio/wasmer/pull/4052) Selenium-style tests for wasmer.sh + - [#4061](https://github.com/wasmerio/wasmer/pull/4061) Use is-terminal to check tty + - [#4056](https://github.com/wasmerio/wasmer/pull/4056) Update hermit-abi crate, used version was yanked + - [#4055](https://github.com/wasmerio/wasmer/pull/4055) Use a no-op package loader by default in PluggableRuntime + - [#4011](https://github.com/wasmerio/wasmer/pull/4011) Move Artifact Register FrameInfo + - [#4042](https://github.com/wasmerio/wasmer/pull/4042) Disable unused cbindgen feature + - [#4044](https://github.com/wasmerio/wasmer/pull/4044) Update tempfile crate + - [#4041](https://github.com/wasmerio/wasmer/pull/4041) Canonicalize host folder on mapdir + - [#4040](https://github.com/wasmerio/wasmer/pull/4040) Removed error message when a deserializzation error occurs in Artifact + - [#4032](https://github.com/wasmerio/wasmer/pull/4032) Make the CLI respect the `--token` flag + - [#4030](https://github.com/wasmerio/wasmer/pull/4030) Remove rustup build dependency + - [#4031](https://github.com/wasmerio/wasmer/pull/4031) Speed up the module cache 6x by removing LZW compression + +## Fixed + + - [#4096](https://github.com/wasmerio/wasmer/pull/4096) Fix benchmark + - [#4050](https://github.com/wasmerio/wasmer/pull/4050) `epoll` with fs fixes + - [#4074](https://github.com/wasmerio/wasmer/pull/4074) fix(wasix): Expose FileSystemMapping struct + - [#4063](https://github.com/wasmerio/wasmer/pull/4063) Fix docstring for MemoryView struct + - [#4047](https://github.com/wasmerio/wasmer/pull/4047) Fixed create exe when zig is not present + - [#3970](https://github.com/wasmerio/wasmer/pull/3970) Fixes for wasmer.sh + - [#4035](https://github.com/wasmerio/wasmer/pull/4035) Fix help text for `use_system_linker` option in `create_exe` CLI command + + + +## 4.0.0 - 22/06/2023 + +Some more behind-the-scene unification and bug fixe since the last beta, mostly on the CLI, but make sure to check all the other changes of the previous beta and alpha release if you came from last stable version. + +## Added + + - [#4013](https://github.com/wasmerio/wasmer/pull/4013) Added a `WasmerEnv` abstraction to the CLI + +## Changed + + - [#4021](https://github.com/wasmerio/wasmer/pull/4021) Put all cached artifacts under the `~/.wasmer/cache/` directory + - [#4018](https://github.com/wasmerio/wasmer/pull/4018) Canonicalize the host path passed to `--mapdir` + - [#4019](https://github.com/wasmerio/wasmer/pull/4019) Make sure `ExecutableTarget::from_file()` for `*.wasm` files checks the module cache + - [#4022](https://github.com/wasmerio/wasmer/pull/4022) [CI] Defined wich nightly to use to avoid not ready error + - [#4001](https://github.com/wasmerio/wasmer/pull/4001) deps: Bump edge CLI + - [#4012](https://github.com/wasmerio/wasmer/pull/4012) update links in Wasmer's README + - [#3985](https://github.com/wasmerio/wasmer/pull/3985) Display human-friendly progress when starting `wasmer run` + - [#4004](https://github.com/wasmerio/wasmer/pull/4004) Ignoring archived package versions when querying the registry + - [#3980](https://github.com/wasmerio/wasmer/pull/3980) Re-work logging and command-line argument parsing + - [#3983](https://github.com/wasmerio/wasmer/pull/3983) Introduce WAPM query caching to reduce `wasmer run`'s startup delay + - [#4002](https://github.com/wasmerio/wasmer/pull/4002) [RISCV64][CI] Use simpler way to install riscv64 packages + +## Fixed + + + + +## 4.0.0-beta.3 - 15/06/2023 + +More fixes and improvement to the virtual filesystem, and behind the scene work to unify wasmer tools for this new beta. +RISC-V will now be built on newest Bookworm Debian release. + +## Added + + - [#3990](https://github.com/wasmerio/wasmer/pull/3990) fix(cli): Fix deploy command and add some aliases + +## Changed + + - [#3998](https://github.com/wasmerio/wasmer/pull/3998) deps: Upgrade edge cli + - [#3989](https://github.com/wasmerio/wasmer/pull/3989) OverlayFS now has COW + - [#3991](https://github.com/wasmerio/wasmer/pull/3991) Renamed most appearances of Wapm to Wasmer + - [#3992](https://github.com/wasmerio/wasmer/pull/3992) [CI][RISCV64] Try to use bookworm debian instead of sid + +## Fixed + + + + +## 4.0.0-beta.2 - 09/06/2023 + +This version added a missing `sock_accept` function to WASI preview1, and also greatly improved the filesystem mappings for the resolver, improving the ease of use of the abilty to run packages with the `wasmer run` command. + +## Added + + - [#3913](https://github.com/wasmerio/wasmer/pull/3913) Add filesystem mappings to the resolver + - [#3971](https://github.com/wasmerio/wasmer/pull/3971) Added the missing sock_accept for preview1 + - [#3957](https://github.com/wasmerio/wasmer/pull/3957) Added a test for small Stack (for #3808) + - [#3956](https://github.com/wasmerio/wasmer/pull/3956) feat(virtual-fs): Add FsError::StorageFull variant + +## Changed + + - [#3710](https://github.com/wasmerio/wasmer/pull/3710) Implemented an asyncify based implementation of asynchronous threading + - [#3950](https://github.com/wasmerio/wasmer/pull/3950) tests: Remove debug_output option from snapshot tests. + - [#3961](https://github.com/wasmerio/wasmer/pull/3961) chore: Rename wasi dir to wasiX + - [#3967](https://github.com/wasmerio/wasmer/pull/3967) Use logs instead of timing to determine if the package cache was hit + - [#3955](https://github.com/wasmerio/wasmer/pull/3955) chore(cli): Remove redundant CLI feature flags + - [#3953](https://github.com/wasmerio/wasmer/pull/3953) feat: Allow custom manifest file paths in "wasmer publish" + - [#3433](https://github.com/wasmerio/wasmer/pull/3433) Module.deserialize - accept AsEngineRef + - [#3946](https://github.com/wasmerio/wasmer/pull/3946) Allow deserialized WAPM JSON even if some version have some NULL infos + - [#3944](https://github.com/wasmerio/wasmer/pull/3944) Port create-exe and the wasmer C API over to the webc compatibility layer + +## Fixed + + - [#3975](https://github.com/wasmerio/wasmer/pull/3975) CLI: Fix deploy Command + - [#3969](https://github.com/wasmerio/wasmer/pull/3969) fix: Fix incorrect archive construction in wasmer publish + + + +## 4.0.0-beta.1 - 01/06/2023 + +This version introduce WASIX! The superset of WASI. Go to https://wasix.org for more details. +This version also merged `wasmer run` and `wasmer run-unstable`. Both command still exists but are now exactly the same. `run-unstable` will be removed later, so switch to `wasmer run` if you were using it. + +## Added + + - [#3818](https://github.com/wasmerio/wasmer/pull/3818) Added a quickfix for mounting relative directories + +## Changed + + - [#3924](https://github.com/wasmerio/wasmer/pull/3924) Rename "wasmer run-unstable" to "wasmer run" + - [#3938](https://github.com/wasmerio/wasmer/pull/3938) Use unchecked deserialization for `_unchecked` module functions + - [#3918](https://github.com/wasmerio/wasmer/pull/3918) Workspace metadata + - [#3919](https://github.com/wasmerio/wasmer/pull/3919) Handle non-200 status codes when downloading packages + - [#3917](https://github.com/wasmerio/wasmer/pull/3917) Rename WasiRuntime to Runtime + +## Fixed + + - [#3933](https://github.com/wasmerio/wasmer/pull/3933) Fix deserialize calls in doc comments + - [#3936](https://github.com/wasmerio/wasmer/pull/3936) Fix for excessive allocation + - [#3920](https://github.com/wasmerio/wasmer/pull/3920) Fix filesystem access when running python with `wasmer run-unstable .` + - [#3867](https://github.com/wasmerio/wasmer/pull/3867) lib/wasi: try quick fix for WasiFS::get_inode_at_path + + + +## 4.0.0-alpha.1 - 25/05/2023 + +A new major release, with a few breaking changes and the removal of many deprecated functions. Also, some methods previously available from on `Engine` has been moved to the `NativeEngineExt` trait. +Deserialize has changed, with the default version using artifact layout validation. The old function has been renamed to `_unchecked` variant. The speed impact is usually negligeable , but the old function are still present if needed +Many bugfixes and improvements on `wasmer run` and `wasmer run-unstable` (that will be merged into just `wasmer run` soon). `--allow-multiple-wasi-versions` CLI flag is now removed that is now removed and active by default. Logic for `wasmer config set registry.url` that has been fixed to work with `localhost:1234` + +## Added + + - [#3879](https://github.com/wasmerio/wasmer/pull/3879) Add compiler features to wai-bindgen-wasmer + - [#1212](https://github.com/wasmerio/wasmer/pull/1212) Add support for GDB JIT debugging + +## Changed + + - [#3852](https://github.com/wasmerio/wasmer/pull/3852) Re-work package resolution and make runners use it + - [#3910](https://github.com/wasmerio/wasmer/pull/3910) Removed all deprecated functions + - [#3900](https://github.com/wasmerio/wasmer/pull/3900) lib/virtual-net: tokio::net::lookup_host requires host:port format + - [#3906](https://github.com/wasmerio/wasmer/pull/3906) WASIX updates after superset changes + - [#3907](https://github.com/wasmerio/wasmer/pull/3907) Choose subversion of tracing crate in wasix + - [#3873](https://github.com/wasmerio/wasmer/pull/3873) Initial implementation for poll on pipe + - [#3904](https://github.com/wasmerio/wasmer/pull/3904) Changed tunables to have a VMConfig settings + - [#3902](https://github.com/wasmerio/wasmer/pull/3902) TmpFs Memory Usage Tracking and Limiting + - [#3894](https://github.com/wasmerio/wasmer/pull/3894) Rework Module deserialization functions + - [#3899](https://github.com/wasmerio/wasmer/pull/3899) enable integration tests with debug binary + - [#3889](https://github.com/wasmerio/wasmer/pull/3889) Bump llvm + - [#3881](https://github.com/wasmerio/wasmer/pull/3881) Use `js-default` feature in wai-bindgen-wasmer + - [#3874](https://github.com/wasmerio/wasmer/pull/3874) refactor!: Memory API Modifications (try_clone/copy, duplicate_in_store) + - [#3763](https://github.com/wasmerio/wasmer/pull/3763) Implement NativeWasmTypeInto for u32 and u64 + - [#3866](https://github.com/wasmerio/wasmer/pull/3866) Align vfs and vnet directories with crate names + - [#3864](https://github.com/wasmerio/wasmer/pull/3864) feat(cli): Integrate deploy commands + - [#3771](https://github.com/wasmerio/wasmer/pull/3771) Asynchronous threading phase2 + - [#3710](https://github.com/wasmerio/wasmer/pull/3710) Implemented an asyncify based implementation of asynchronous threading + - [#3859](https://github.com/wasmerio/wasmer/pull/3859) Support static linking on Windows + - [#3856](https://github.com/wasmerio/wasmer/pull/3856) Switch FileSystemCache to use checked artifact deserialization + - [#3854](https://github.com/wasmerio/wasmer/pull/3854) deps: Upgrade clap to v4 + - [#3849](https://github.com/wasmerio/wasmer/pull/3849) Support MinGW + - [#3841](https://github.com/wasmerio/wasmer/pull/3841) Introduce a module cache abstraction + - [#3400](https://github.com/wasmerio/wasmer/pull/3400) Use the wasm_bindgen_downcast crate for downcasting JsValues + - [#3831](https://github.com/wasmerio/wasmer/pull/3831) Introduce a package resolver + - [#3843](https://github.com/wasmerio/wasmer/pull/3843) Adapted the publishing script + +## Fixed + + - [#3867](https://github.com/wasmerio/wasmer/pull/3867) lib/wasi: try quick fix for WasiFS::get_inode_at_path + - [#3891](https://github.com/wasmerio/wasmer/pull/3891) Fix typos in docs + - [#3898](https://github.com/wasmerio/wasmer/pull/3898) fix: Use fallback home directory detection in config commands. + - [#3861](https://github.com/wasmerio/wasmer/pull/3861) fuzz: fix build error + - [#3853](https://github.com/wasmerio/wasmer/pull/3853) Fix cargo update + + + +## 3.3.0 - 03/05/2023 + +Along a few important bugfixes, this version introduce JavaScriptCore support, with full WASI support. + +## Added + + - [#3825](https://github.com/wasmerio/wasmer/pull/3825) Added support for JavascriptCore + - [#3837](https://github.com/wasmerio/wasmer/pull/3837) Cleaned up the GraphQL endpoint logic and added tests + - [#3833](https://github.com/wasmerio/wasmer/pull/3833) Fix Missing WaiterError export + Add notify/wait to fd_mmap memory + - [#3819](https://github.com/wasmerio/wasmer/pull/3819) ci: add docs.ts test builds + - [#3782](https://github.com/wasmerio/wasmer/pull/3782) Add FreeBSD x86 support + +## Changed + + - [#3838](https://github.com/wasmerio/wasmer/pull/3838) Re-export `wasmer::EngineRef` + - [#3820](https://github.com/wasmerio/wasmer/pull/3820) fd_write: Flush on every write to a file + - [#3813](https://github.com/wasmerio/wasmer/pull/3813) Better errors + +## Fixed + + - [#3796](https://github.com/wasmerio/wasmer/pull/3796) Fix returning negative i64 values on web target + - [#3836](https://github.com/wasmerio/wasmer/pull/3836) Fixed Chinese README example + - [#3827](https://github.com/wasmerio/wasmer/pull/3827) Made test-build-docs-rs tests docs generation for all crates under libs, and fixed broken ones + - [#3796](https://github.com/wasmerio/wasmer/pull/3796) Fix returning negative i64 values on web target + + + +## 3.2.1 - 21/04/2023 + +Some quick fixes for this maintenance release, mainly oriented towards Docs. + +## Added + + - [#3782](https://github.com/wasmerio/wasmer/pull/3782) Add FreeBSD x86 support + +## Changed + + - [#3786](https://github.com/wasmerio/wasmer/pull/3786) Made Stack Size parametrable (for #3760) + - [#3805](https://github.com/wasmerio/wasmer/pull/3805) Wasi runner args + - [#3795](https://github.com/wasmerio/wasmer/pull/3795) Make sure the compile function passed to runners is Send+Sync + - [#3777](https://github.com/wasmerio/wasmer/pull/3777) Use webc's compat layer in more places + +## Fixed + + - [#3806](https://github.com/wasmerio/wasmer/pull/3806) Fixed FunctionEnv migration doc to 3.0 + - [#3804](https://github.com/wasmerio/wasmer/pull/3804) Fixed doc comment in memory_view.rs + - [#3791](https://github.com/wasmerio/wasmer/pull/3791) Fix doc api + + + +## 3.2.0 - 18/04/2023 + +A lot of new features since last stable version: +RISCV Support, new Runners (WCGI), API convergence for JS/SYS, and WASI eXtended. + + * RISCV support, on both Cranelift and LLVM Compiler. + * New Runners, with WCGI as a new one + * Most WAPM command are now available on the wasmer CLI directly + * Using Wasmer on `sys` or `js` backend is more transparent now, with now support for running wasmer on`JavaScriptCore` + + * The WASI implementation has undergone a major refactoring, and will continue evolve significantly over the coming months. + - The old `wasmer_wasi` crate was deprecated. + + - To continue using WASI, please switch to the new `wasmer_wasix` crate, which follows a different versioning scheme than the main Wasmer releases. + Major changes: + - An async runtime is now required. The runtime is pluggable, but only tokio is officially supported at the moment. + - The virtual file system layer was renamed from `wasmer-vfs` to `virtual-fs`, and now is built around an async interface that builds on top of `tokio::{AsyncRead/Write}` + + This refactor will unlock many exciting new features that will be announced soon! + Just be aware that you will have to expect some instability and frequent releases with potential breaking changes until our new implementation settles. down. + +## Added + + - [#3706](https://github.com/wasmerio/wasmer/pull/3706) [CI] Add RISCV in test and build + - [#3765](https://github.com/wasmerio/wasmer/pull/3765) Added basic support to inode in filestat_get (for #3583 and #3239) + - [#3751](https://github.com/wasmerio/wasmer/pull/3751) Added some unit testing to singlepass compiler + - [#3752](https://github.com/wasmerio/wasmer/pull/3752) Added new snapshots tests that use a fixed MIO and TOKIO + - [#3759](https://github.com/wasmerio/wasmer/pull/3759) Added the wasmer.sh website to the main repo and a CI/CD build test + - [#3747](https://github.com/wasmerio/wasmer/pull/3747) Added missing crate version bump + +## Changed + + - [#3778](https://github.com/wasmerio/wasmer/pull/3778) Upgrade to webc v5.0.0 + - [#3775](https://github.com/wasmerio/wasmer/pull/3775) Ran Cargo update before release + - [#3774](https://github.com/wasmerio/wasmer/pull/3774) Wasi threads + - [#3772](https://github.com/wasmerio/wasmer/pull/3772) [DOC] Removed paragraph about default-compiler, as it's doesn't exist anymore + - [#3766](https://github.com/wasmerio/wasmer/pull/3766) deps: Upgrade memoffset + - [#3690](https://github.com/wasmerio/wasmer/pull/3690) Introduce a WebcVolumeFileSystem that works for any WEBC version + - [#3424](https://github.com/wasmerio/wasmer/pull/3424) Use "wasmer --version --verbose" in the issue template + - [#3757](https://github.com/wasmerio/wasmer/pull/3757) WASI without VM internals + - [#3758](https://github.com/wasmerio/wasmer/pull/3758) More and better snapshot tests + - [#3756](https://github.com/wasmerio/wasmer/pull/3756) Aarch64 stackprobe + - [#3753](https://github.com/wasmerio/wasmer/pull/3753) Upgrade webc from webc 5.0.0-rc.5 to webc 5.0.0-rc.6 + +## Fixed + + - [#3767](https://github.com/wasmerio/wasmer/pull/3767) Fix Snapshot0 fd_filestat_get and path_filestat_get + - [#3723](https://github.com/wasmerio/wasmer/pull/3723) Fix Wait/Notify opcode, the waiters hashmap is now on the Memory itself + - [#3749](https://github.com/wasmerio/wasmer/pull/3749) Fixed publish script with latest needed changes + - [#3750](https://github.com/wasmerio/wasmer/pull/3750) Fixes Chinese docs links (for #3746) + - [#3761](https://github.com/wasmerio/wasmer/pull/3761) Fix error in Korean translation + + + +## 3.2.0-beta.2 - 05/04/2023 + +Bug fixes for this beta release, and some exciting new possibilities with the `run-unstable` function from the CLI tools. + - New wcgi runner + - Caching of runners + - Many fixes to wasix + +Known issue: RISC-V build is currently broken and will be fixes on next version + +## Added + + - [#3731](https://github.com/wasmerio/wasmer/pull/3731) Added missing ASM instructions for wasix on singlepass + +## Changed + + - [#3743](https://github.com/wasmerio/wasmer/pull/3743) Bumped crates version pre-beta.2 release + - [#3742](https://github.com/wasmerio/wasmer/pull/3742) Update versions of wcgi + - [#3739](https://github.com/wasmerio/wasmer/pull/3739) Use cache in runners + - [#3735](https://github.com/wasmerio/wasmer/pull/3735) [CI] More WAPM_DEV_TOKEN check for CI + - [#3734](https://github.com/wasmerio/wasmer/pull/3734) Updated spin to 0.9.8 + - [#3733](https://github.com/wasmerio/wasmer/pull/3733) [CI] Use early exit for wasmer_init_works_1 and wasmer_init_works_2 when WAPM_DEV_TOKEN is empty + - [#3693](https://github.com/wasmerio/wasmer/pull/3693) Validate Module Artifacts (CLI + WASIX ModuleCache) + - [#3724](https://github.com/wasmerio/wasmer/pull/3724) ci: Cancel previous workflow runs when new commits are pushed + - [#3722](https://github.com/wasmerio/wasmer/pull/3722) Revert #3715 + - [#3718](https://github.com/wasmerio/wasmer/pull/3718) Apply CGI environment variables in the correct order + - [#3716](https://github.com/wasmerio/wasmer/pull/3716) Move Package Build/Publish Logic to wasmer-registry + +## Fixed + + - [#3740](https://github.com/wasmerio/wasmer/pull/3740) Fix fuzz build + - [#3741](https://github.com/wasmerio/wasmer/pull/3741) Fixed an issue where blocking polls were being treated as nonblocking + - [#3737](https://github.com/wasmerio/wasmer/pull/3737) Fixed FileSystem createdir when parent_inode is an ArcDirectory + - [#3736](https://github.com/wasmerio/wasmer/pull/3736) Fixed Vectored IO when a partial operation occurs + - [#3726](https://github.com/wasmerio/wasmer/pull/3726) fix CI-Badge on README.md + - [#3687](https://github.com/wasmerio/wasmer/pull/3687) Master with fixes + - [#3719](https://github.com/wasmerio/wasmer/pull/3719) Fixes a bug which causes libc to thrash the futex_wake_all + - [#3715](https://github.com/wasmerio/wasmer/pull/3715) Fix annotation deserializing for the Wasi and Wcgi runners + - [#3704](https://github.com/wasmerio/wasmer/pull/3704) Fix the absolute/relative path weirdness when setting up WASI-based runner filesystems + - [#3705](https://github.com/wasmerio/wasmer/pull/3705) Fixes post 3.2.0-beta.1 release (but needed for the crates publication) + + + +## 3.2.0-beta.1 - 22/03/2023 + +Lots of new things in the release! + + - WASIX, the new WASI eXtended library, that alows WASM to use Threads, Network, and more in both Runtime and inside a Browser. + - RISC-V support, on both LLVM and Cranelift compiler. + - Improved CLI, with more commands and more WAPM integration. + - Lots of behind the scene refactoring, with sys/js convergence on the API side, and a lot of crate changes. + +## Added + + - [#3700](https://github.com/wasmerio/wasmer/pull/3700) Added back all removed function for Engine to avoid API breaking changes + - [#3654](https://github.com/wasmerio/wasmer/pull/3654) [SINGLEPASS] Add more ROR emitter to ARM64 backend (for #3647) + - [#3664](https://github.com/wasmerio/wasmer/pull/3664) feat(wasi): Add CapabilityThreading with max thread count + - [#3556](https://github.com/wasmerio/wasmer/pull/3556) Use standard API for js and sys for Module. Added Engine in js + - [#3635](https://github.com/wasmerio/wasmer/pull/3635) Added some fixes for the browser version to work again + - [#3626](https://github.com/wasmerio/wasmer/pull/3626) add basic coredump generation + - [#3631](https://github.com/wasmerio/wasmer/pull/3631) [CI] Add opened type for pull_request on test.yaml + - [#3612](https://github.com/wasmerio/wasmer/pull/3612) Added conveniance function FunctionEnvMut::data_and_store_mut + - [#3536](https://github.com/wasmerio/wasmer/pull/3536) Add documentation for create-exe syntax + - [#3512](https://github.com/wasmerio/wasmer/pull/3512) Add unit test for verifying that caching works when running packages twice + - [#3490](https://github.com/wasmerio/wasmer/pull/3490) Fix JS sample code by adding "&mut store" + +## Changed + + - [#3244](https://github.com/wasmerio/wasmer/pull/3244) Feat riscv llvm and cranelift + - [#3650](https://github.com/wasmerio/wasmer/pull/3650) Wasmer run-unstable + - [#3692](https://github.com/wasmerio/wasmer/pull/3692) PluggableRuntime Cleanup + - [#3681](https://github.com/wasmerio/wasmer/pull/3681) (feat) Ability to supply your own shared tokio runtime + - [#3689](https://github.com/wasmerio/wasmer/pull/3689) Increased CURRENT_VERSION to 3 + - [#3691](https://github.com/wasmerio/wasmer/pull/3691) Don't error out on webc/wasi run when exiting normaly + - [#3683](https://github.com/wasmerio/wasmer/pull/3683) Renamed some crates and changed their version to 0.1.0 + - [#3677](https://github.com/wasmerio/wasmer/pull/3677) Overlay FileSystem + - [#3580](https://github.com/wasmerio/wasmer/pull/3580) Make memoryView lifetime linked to StoreRef instead of Memory + - [#3682](https://github.com/wasmerio/wasmer/pull/3682) Update wasmparser to v0.95 + - [#3676](https://github.com/wasmerio/wasmer/pull/3676) Switch `wasmer_vfs::host_fs` tests over to using temp directories + - [#3670](https://github.com/wasmerio/wasmer/pull/3670) Bumped cranelift to 0.91.1 following a critical security alert + - [#3666](https://github.com/wasmerio/wasmer/pull/3666) refactor(wasi): Move capabilities into root submodule + - [#3644](https://github.com/wasmerio/wasmer/pull/3644) Improvements to the tracing and logging in wasmer + - [#3656](https://github.com/wasmerio/wasmer/pull/3656) Re-enable (most) WASIX Snapshot Tests + - [#3658](https://github.com/wasmerio/wasmer/pull/3658) [CI] This should setup LLVM on Windows correctly this time + - [#3655](https://github.com/wasmerio/wasmer/pull/3655) [CI] Manually setup LLVM because Custom build doesn't include llvm-config.exe + - [#3599](https://github.com/wasmerio/wasmer/pull/3599) Move the `wcgi-runner` into Wasmer + - [#3646](https://github.com/wasmerio/wasmer/pull/3646) deps: Remove tempdir dependency + - [#3641](https://github.com/wasmerio/wasmer/pull/3641) Upgrade LLVM to 14.0 + - [#3640](https://github.com/wasmerio/wasmer/pull/3640) Zero memory copies on IO + - [#3636](https://github.com/wasmerio/wasmer/pull/3636) Removed choice for object-format in create-exe and create-obj commands + - [#3637](https://github.com/wasmerio/wasmer/pull/3637) Remove unused import in lib.rs example + - [#3632](https://github.com/wasmerio/wasmer/pull/3632) [CI] Another attempt to get Test on external PR + - [#3628](https://github.com/wasmerio/wasmer/pull/3628) Registry: Implement App Publishing and Token Generation + - [#3627](https://github.com/wasmerio/wasmer/pull/3627) wasi: Remove vbus leftovers and improve task joining + - [#3620](https://github.com/wasmerio/wasmer/pull/3620) ci: Remove deleted vbus crate from build workflow + - [#3422](https://github.com/wasmerio/wasmer/pull/3422) WASIX + - [#3539](https://github.com/wasmerio/wasmer/pull/3539) Document release process and update release script + - [#3593](https://github.com/wasmerio/wasmer/pull/3593) Update rkyv 0.7.40 and prettytable-rs to 0.10.0 + - [#3585](https://github.com/wasmerio/wasmer/pull/3585) CI: Purge Cache with Scheduled Action + - [#3590](https://github.com/wasmerio/wasmer/pull/3590) Optimize getting byteLength in MemoryView::new + - [#3586](https://github.com/wasmerio/wasmer/pull/3586) Make AsStoreRef and friends work for anything that implements Deref + - [#3582](https://github.com/wasmerio/wasmer/pull/3582) bump inkwell to 0.1.1 + - [#3577](https://github.com/wasmerio/wasmer/pull/3577) Use Cloudflare as CI Cache + - [#3353](https://github.com/wasmerio/wasmer/pull/3353) Speed up CI + - [#3564](https://github.com/wasmerio/wasmer/pull/3564) Updated Cranelift to 0.91 + - [#3537](https://github.com/wasmerio/wasmer/pull/3537) bump inkwell to 0.1.0 + - [#3562](https://github.com/wasmerio/wasmer/pull/3562) Improved handling of wasmer-headless generation and use on local run + - [#3563](https://github.com/wasmerio/wasmer/pull/3563) Revert "bump inkwell to 0.1.0" + - [#3558](https://github.com/wasmerio/wasmer/pull/3558) Reduce libwasmer-headless size + - [#3552](https://github.com/wasmerio/wasmer/pull/3552) create-exe: link with libwasmer-headless + - [#3547](https://github.com/wasmerio/wasmer/pull/3547) Remove setting memory for WASI compatibility + - [#3531](https://github.com/wasmerio/wasmer/pull/3531) Reenable create-exe tests + - [#3534](https://github.com/wasmerio/wasmer/pull/3534) Use JS VM store instead of artificially replicate it + - [#3532](https://github.com/wasmerio/wasmer/pull/3532) VMInstance cannot be Clone + +## Fixed + + - [#3663](https://github.com/wasmerio/wasmer/pull/3663) Dash fixes and pthreads + - [#3684](https://github.com/wasmerio/wasmer/pull/3684) Fix: module cache delta overflow + - [#3680](https://github.com/wasmerio/wasmer/pull/3680) Fix for the vectored IO + - [#3668](https://github.com/wasmerio/wasmer/pull/3668) Fix doc build + - [#3665](https://github.com/wasmerio/wasmer/pull/3665) Fix doc, threads are enabled by default now + - [#3662](https://github.com/wasmerio/wasmer/pull/3662) Why is it so difficult to type LLVM_SYS_140_PREFIX + - [#3661](https://github.com/wasmerio/wasmer/pull/3661) [CI] New attempt at fixing the Windows build on the CI + - [#3659](https://github.com/wasmerio/wasmer/pull/3659) Fixed building with with just the sys feature + - [#3648](https://github.com/wasmerio/wasmer/pull/3648) Fix CI and llvm detection + - [#3643](https://github.com/wasmerio/wasmer/pull/3643) fix(wasi): Memory leak due to cyclical WasiControlPlane references + - [#3639](https://github.com/wasmerio/wasmer/pull/3639) wasi: Thread Lifecycle Fix + - [#3630](https://github.com/wasmerio/wasmer/pull/3630) Fix linter + - [#3591](https://github.com/wasmerio/wasmer/pull/3591) fix: experimental io + - [#3629](https://github.com/wasmerio/wasmer/pull/3629) Webc Inheritance Fixes + - [#3598](https://github.com/wasmerio/wasmer/pull/3598) Wasix multithreading fix + - [#3601](https://github.com/wasmerio/wasmer/pull/3601) Update CI script to fix deprecation notices + - [#3623](https://github.com/wasmerio/wasmer/pull/3623) ci: Fix build.yaml workflow + - [#3581](https://github.com/wasmerio/wasmer/pull/3581) Fix WASI example that initialized wasi_config 2 times + - [#3584](https://github.com/wasmerio/wasmer/pull/3584) Wasix major fixes and tweaks + - [#3557](https://github.com/wasmerio/wasmer/pull/3557) Fix make-test-integration CLI producing files when running locally + - [#3566](https://github.com/wasmerio/wasmer/pull/3566) Fix create-exe with create-obj when using dashes in atom names + - [#3573](https://github.com/wasmerio/wasmer/pull/3573) Fix/compile not in memory + - [#3569](https://github.com/wasmerio/wasmer/pull/3569) Fix error message due to wapm.dev backend change + - [#3551](https://github.com/wasmerio/wasmer/pull/3551) Fix markup in Japanese README + - [#3493](https://github.com/wasmerio/wasmer/pull/3493) Fixed some memory leak issue with InstanceHandle + - [#3523](https://github.com/wasmerio/wasmer/pull/3523) Fix Windows-GNU distribution + + + +## 3.2.0-alpha.1 - 23/01/2023 + +## Added + + - [#3477](https://github.com/wasmerio/wasmer/pull/3477) Added support for Wasm Module custom sections in js + - [#3462](https://github.com/wasmerio/wasmer/pull/3462) [SINGLEPASS] Added a special case on SSE4.2 backend when dst == src1 + +## Changed + + - [#3511](https://github.com/wasmerio/wasmer/pull/3511) Incremented CURRENT_VERSION, so all cache will be invalidate and be rebuilt with the 3.2 version + - [#3498](https://github.com/wasmerio/wasmer/pull/3498) Wasix Control Plane - Thread Limit + Cleanup + - [#3465](https://github.com/wasmerio/wasmer/pull/3465) Remove assert in sse_round_fn and handle case where src2 is in memory + - [#3494](https://github.com/wasmerio/wasmer/pull/3494) Update wasmer-toml version + - [#3426](https://github.com/wasmerio/wasmer/pull/3426) WASIX Preparation + - [#3480](https://github.com/wasmerio/wasmer/pull/3480) Ignore Create-exe with serialize test, something is wrong with the generated exe + - [#3471](https://github.com/wasmerio/wasmer/pull/3471) Rename `WasiState::new()` to `WasiState::builder()` + - [#3430](https://github.com/wasmerio/wasmer/pull/3430) Implement support for multiple commands in one native executable + - [#3455](https://github.com/wasmerio/wasmer/pull/3455) Remove hardcoded rust-toolchain and use panic=abort on windows-gnu + - [#3353](https://github.com/wasmerio/wasmer/pull/3353) Speed up CI + - [#3433](https://github.com/wasmerio/wasmer/pull/3433) Module.deserialize - accept AsEngineRef + - [#3428](https://github.com/wasmerio/wasmer/pull/3428) Implement wasmer config + - [#3432](https://github.com/wasmerio/wasmer/pull/3432) Amend changes to wasmer init + - [#3439](https://github.com/wasmerio/wasmer/pull/3439) Use GNU/Linux frame registration code for FreeBSD too + - [#3324](https://github.com/wasmerio/wasmer/pull/3324) Implement wasmer init and wasmer publish + - [#3431](https://github.com/wasmerio/wasmer/pull/3431) Revert "Implement wasmer init and wasmer publish" + +## Fixed + + - [#3483](https://github.com/wasmerio/wasmer/pull/3483) Fix feature flags for make-build-wasmer-headless + - [#3496](https://github.com/wasmerio/wasmer/pull/3496) Fixed create-exe tests for object-format serialized + - [#3479](https://github.com/wasmerio/wasmer/pull/3479) This should fix CI build of CAPI Headless + - [#3473](https://github.com/wasmerio/wasmer/pull/3473) Fix wasm publish validation + - [#3467](https://github.com/wasmerio/wasmer/pull/3467) Fix wasmer-wasi-js compilation + - [#3443](https://github.com/wasmerio/wasmer/pull/3443) Fix fuzz errors + - [#3456](https://github.com/wasmerio/wasmer/pull/3456) Fix wasmer-rust readme example + - [#3440](https://github.com/wasmerio/wasmer/pull/3440) Fix CI for external collaborator PRs + - [#3427](https://github.com/wasmerio/wasmer/pull/3427) Fix cargo-deny failing on webc crate + - [#3423](https://github.com/wasmerio/wasmer/pull/3423) Fix minor typo + - [#3419](https://github.com/wasmerio/wasmer/pull/3419) Fix CHANGELOG generation to list by PR merged date, not created date + + + +## Fixed + + - [#3439](https://github.com/wasmerio/wasmer/pull/3439) Use GNU/Linux frame registration code for FreeBSD too + - [#3448](https://github.com/wasmerio/wasmer/pull/3448) Fix `make install-capi-lib` install paths + +## 3.1.0 - 12/12/2022 + +## Added + + - [#3403](https://github.com/wasmerio/wasmer/pull/3403) Add wasm_importtype_copy to C API + +## Changed + + - [#3416](https://github.com/wasmerio/wasmer/pull/3416) Download and install packages via .tar.gz URLs and improve installation error message + - [#3402](https://github.com/wasmerio/wasmer/pull/3402) Do not run first command of wapm file and print all commands instead + - [#3400](https://github.com/wasmerio/wasmer/pull/3400) Use the wasm_bindgen_downcast crate for downcasting JsValues + - [#3363](https://github.com/wasmerio/wasmer/pull/3363) Store Used CpuFeature in Artifact instead of Present CpuFeatures for Singlepass + - [#3378](https://github.com/wasmerio/wasmer/pull/3378) Introduced EngineRef and AsEngineRef trait + - [#3386](https://github.com/wasmerio/wasmer/pull/3386) Restore Support For All Wasi Clock Types + - [#3153](https://github.com/wasmerio/wasmer/pull/3153) SharedMemory & Atomics + +## Fixed + + - [#3415](https://github.com/wasmerio/wasmer/pull/3415) Fix singlepass for Aarch64 + - [#3395](https://github.com/wasmerio/wasmer/pull/3395) Fix create-exe to be able to cross-compile on Windows + - [#3396](https://github.com/wasmerio/wasmer/pull/3396) Fix build doc and minimum-sys build + +## 3.0.2 - 25/11/2022 + +## Added + + - [#3364](https://github.com/wasmerio/wasmer/pull/3364) Added the actual LZCNT / TZCNT implementation + +## Changed + + - [#3365](https://github.com/wasmerio/wasmer/pull/3365) Improve FreeBSD support + - [#3368](https://github.com/wasmerio/wasmer/pull/3368) Remove wasi conditional compilation from wasmer-registry + - [#3367](https://github.com/wasmerio/wasmer/pull/3367) Change LLVM detection in Makefile + +## Fixed + + - [#3370](https://github.com/wasmerio/wasmer/pull/3370) Fix wasmer run not interpreting URLs correctly + display fixes + - [#3371](https://github.com/wasmerio/wasmer/pull/3371) Fix cargo binstall + + +## 3.0.1 - 23/11/2022 + +## Added + + - [#3361](https://github.com/wasmerio/wasmer/pull/3361) Give users feedback when they are running "wasmer add ..." + +## Changed + + - [#3360](https://github.com/wasmerio/wasmer/pull/3360) Introduce a "wasmer_registry::queries" module with all GraphQL queries + - [#3355](https://github.com/wasmerio/wasmer/pull/3355) Fetch the pirita download URL + - [#3344](https://github.com/wasmerio/wasmer/pull/3344) Revert #3145 + - [#3302](https://github.com/wasmerio/wasmer/pull/3302) Some Refactor of Singlepass compiler to have better error and cpu features handling + - [#3296](https://github.com/wasmerio/wasmer/pull/3296) Use the right collection when parsing type section + - [#3292](https://github.com/wasmerio/wasmer/pull/3292) Precompute offsets in VMOffsets + - [#3290](https://github.com/wasmerio/wasmer/pull/3290) Limit the use of clone when handling Compilation object + - [#3316](https://github.com/wasmerio/wasmer/pull/3316) Implement wasmer whoami + - [#3341](https://github.com/wasmerio/wasmer/pull/3341) Update CHANGELOG.md + +## Fixed + + - [#3342](https://github.com/wasmerio/wasmer/pull/3342) Fixes for 3.0.0 release + +## 3.0.0 - 20/11/2022 + +## Added + + - [#3339](https://github.com/wasmerio/wasmer/pull/3339) Fixes for wasmer login / wasmer add + - [#3337](https://github.com/wasmerio/wasmer/pull/3337) Add automation script to automate deploying releases on GitHub + - [#3338](https://github.com/wasmerio/wasmer/pull/3338) Re-add codecov to get coverage reports + +## Changed + + - [#3295](https://github.com/wasmerio/wasmer/pull/3295) Implement wasmer run {url} + +## Fixed + + +## 3.0.0-rc.3 - 2022/11/18 + +## Added + + - [#3314](https://github.com/wasmerio/wasmer/pull/3314) Add windows-gnu workflow + +## Changed + + - [#3317](https://github.com/wasmerio/wasmer/pull/3317) Port "wapm install" to Wasmer + - [#3318](https://github.com/wasmerio/wasmer/pull/3318) Bump the MSRV to 1.63 + - [#3319](https://github.com/wasmerio/wasmer/pull/3319) Disable 'Test integration CLI' on CI for the Windows platform as it's not working at all + - [#3297](https://github.com/wasmerio/wasmer/pull/3297) Implement wasmer login + - [#3311](https://github.com/wasmerio/wasmer/pull/3311) Export Module::IoCompileError as it's an error returned by an exported function + - [#2800](https://github.com/wasmerio/wasmer/pull/2800) RISC-V support + - [#3293](https://github.com/wasmerio/wasmer/pull/3293) Removed call to to_vec() on assembler.finalise() + - [#3288](https://github.com/wasmerio/wasmer/pull/3288) Rollback all the TARGET_DIR changes + - [#3284](https://github.com/wasmerio/wasmer/pull/3284) Makefile now handle TARGET_DIR env. var. for build too + - [#3276](https://github.com/wasmerio/wasmer/pull/3276) Remove unnecessary checks to test internet connection + - [#3266](https://github.com/wasmerio/wasmer/pull/3266) Return ENotCapable error when accessing unknown files on root (for #3263 and #3264) + - [#3275](https://github.com/wasmerio/wasmer/pull/3275) Disable printing "local package ... not found" in release mode + - [#3273](https://github.com/wasmerio/wasmer/pull/3273) Undo Makefile commit + +## Fixed + + - [#3299](https://github.com/wasmerio/wasmer/pull/3299) Fix "create-exe" for windows-x86_64 target + - [#3294](https://github.com/wasmerio/wasmer/pull/3294) Fix test sys yaml syntax + - [#3287](https://github.com/wasmerio/wasmer/pull/3287) Fix Makefile with TARGET_DIR end with release folder, removing it + - [#3286](https://github.com/wasmerio/wasmer/pull/3286) Fix Makefile with TARGET_DIR end with release folder + - [#3285](https://github.com/wasmerio/wasmer/pull/3285) Fix CI to setup TARGET_DIR to target/release directly + - [#3277](https://github.com/wasmerio/wasmer/pull/3277) Fix red CI on master + + +## 3.0.0-rc.2 - 2022/11/02 + +## Added + + +## Changed + + - [#3258](https://github.com/wasmerio/wasmer/pull/3258) Migrate pirita / native executables feature from wasmer-private + +## Fixed + + - [#3268](https://github.com/wasmerio/wasmer/pull/3268) Fix fd_right nightly test to avoid foo.txt file leftover + - [#3260](https://github.com/wasmerio/wasmer/pull/3260) Fix bug in wasmer run + - [#3257](https://github.com/wasmerio/wasmer/pull/3257) Fix linux-aarch64 build + + +## 3.0.0-rc.1 - 2022/10/25 + +## Added + + - [#3222](https://github.com/wasmerio/wasmer/pull/3222) Add function to retrieve function name from wasm_frame_t + - [#3240](https://github.com/wasmerio/wasmer/pull/3240) Fix filesystem rights on WASI, add integration test for file permissions + - [#3238](https://github.com/wasmerio/wasmer/pull/3238) Fixed main README ocaml homepage link and added ocaml in other language README + - [#3145](https://github.com/wasmerio/wasmer/pull/3145) C-API: add functions to overwrite stdin / stdout / stderr handlers + +## Changed + + - [#3215](https://github.com/wasmerio/wasmer/pull/3215) Update wasmer --version logic, integrate wapm-cli + - [#3248](https://github.com/wasmerio/wasmer/pull/3248) Move loupe CHANGELOG entry from 2.3.0 to 3.x + - [#3230](https://github.com/wasmerio/wasmer/pull/3230) Remove test if dest file exist on path_rename wasi syscall (for #3228) + - [#3061](https://github.com/wasmerio/wasmer/pull/3061) Removed trailing zero in WASI::fd_prestat_dir_name name return (for #3025) + - [#3223](https://github.com/wasmerio/wasmer/pull/3223) Delete lib/wasi-types-generated directory + - [#3178](https://github.com/wasmerio/wasmer/pull/3178) Feat enhanced tinytunable test + - [#3177](https://github.com/wasmerio/wasmer/pull/3177) Auto-generate wasi-types from .wit files + - [#3218](https://github.com/wasmerio/wasmer/pull/3218) Seal `HostFunctionKind` + +## Fixed + + - [#3221](https://github.com/wasmerio/wasmer/pull/3221) Fix #3197 + - [#3229](https://github.com/wasmerio/wasmer/pull/3229) Fixed version to nightly-2022-10-09 for the CI build Minimal Wasmer Headless again + - [#3227](https://github.com/wasmerio/wasmer/pull/3227) Fixed version to nightly-2022-10-09 for the CI build Minimal Wasmer Headless + - [#3226](https://github.com/wasmerio/wasmer/pull/3226) Fixed version to nightly-2002-10-09 for the CI build Minimal Wasmer Headless + - [#3211](https://github.com/wasmerio/wasmer/pull/3211) fix popcnt for aarch64 + - [#3204](https://github.com/wasmerio/wasmer/pull/3204) Fixed a typo in README + + +## 3.0.0-beta.2 - 2022/09/26 + +## Added + + - [#3176](https://github.com/wasmerio/wasmer/pull/3176) Add support for `cargo-binstall` + - [#3141](https://github.com/wasmerio/wasmer/pull/3141) The API breaking changes from future WASIX/Network/Threading addition + - [#3119](https://github.com/wasmerio/wasmer/pull/3119) Added LinearMemory trait + - [#3117](https://github.com/wasmerio/wasmer/pull/3117) Add tests for wasmer-cli create-{exe,obj} commands + - [#3101](https://github.com/wasmerio/wasmer/pull/3101) CI/build.yaml: add libwasmer headless in default distribution + - [#3090](https://github.com/wasmerio/wasmer/pull/3090) Added version to the wasmer cli + - [#3089](https://github.com/wasmerio/wasmer/pull/3089) Add wasi_* C-API function changes in migration guide for 3.0.0 + - [#3076](https://github.com/wasmerio/wasmer/pull/3076) Add support for cross-compiling in create-exe with zig cc WIP + - [#3072](https://github.com/wasmerio/wasmer/pull/3072) Add back `Function::*_with_env(â€Ļ)` + - [#3048](https://github.com/wasmerio/wasmer/pull/3048) Add cloudcompiler.yaml + - [#3068](https://github.com/wasmerio/wasmer/pull/3068) create-{exe,obj}: add documentations and header file generation for create-obj + - [#3065](https://github.com/wasmerio/wasmer/pull/3065) Added '.' and '..' special folder t WASI fd_readdir return (for #3033) + +## Changed + + - [#3184](https://github.com/wasmerio/wasmer/pull/3184) Test libwasmer.dll on Windows + - [#3164](https://github.com/wasmerio/wasmer/pull/3164) Synchronize between -sys and -js tests + - [#3165](https://github.com/wasmerio/wasmer/pull/3165) Initial port of make test-js-core (port wasmer API to core) + - [#3138](https://github.com/wasmerio/wasmer/pull/3138) Js imports revamp + - [#3142](https://github.com/wasmerio/wasmer/pull/3142) Bump rust toolchain + - [#3116](https://github.com/wasmerio/wasmer/pull/3116) Multithreading, full networking and RPC for WebAssembly + - [#3130](https://github.com/wasmerio/wasmer/pull/3130) Remove panics from Artifact::deserialize + - [#3134](https://github.com/wasmerio/wasmer/pull/3134) Bring libwasmer-headless.a from 22MiB to 7.2MiB (on my machine) + - [#3131](https://github.com/wasmerio/wasmer/pull/3131) Update for migration-to-3.0.0 for MemoryView changes + - [#3123](https://github.com/wasmerio/wasmer/pull/3123) Lower libwasmer headless size + - [#3132](https://github.com/wasmerio/wasmer/pull/3132) Revert "Lower libwasmer headless size" + - [#3128](https://github.com/wasmerio/wasmer/pull/3128) scripts/publish.py: validate crates version before publishing + - [#3126](https://github.com/wasmerio/wasmer/pull/3126) scripts/publish.py: replace toposort dependency with python std graphlib module + - [#3122](https://github.com/wasmerio/wasmer/pull/3122) Update Cargo.lock dependencies + - [#3118](https://github.com/wasmerio/wasmer/pull/3118) Refactor Artifact enum into a struct + - [#3114](https://github.com/wasmerio/wasmer/pull/3114) Implemented shared memory for Wasmer in preparation for multithreading + - [#3104](https://github.com/wasmerio/wasmer/pull/3104) Re-enabled ExternRef tests + - [#3103](https://github.com/wasmerio/wasmer/pull/3103) create-exe: prefer libwasmer headless when cross-compiling + - [#3097](https://github.com/wasmerio/wasmer/pull/3097) MemoryView lifetime tied to memory and not StoreRef + - [#3095](https://github.com/wasmerio/wasmer/pull/3095) create-exe: list supported cross-compilation target triples in help â€Ļ + - [#3096](https://github.com/wasmerio/wasmer/pull/3096) create-exe: use cached wasmer tarballs for network fetches + - [#3083](https://github.com/wasmerio/wasmer/pull/3083) Disable wasm build in build CI + - [#3081](https://github.com/wasmerio/wasmer/pull/3081) 3.0.0-beta release + - [#3079](https://github.com/wasmerio/wasmer/pull/3079) Migrate to clap from structopt + - [#3075](https://github.com/wasmerio/wasmer/pull/3075) Remove __wbindgen_thread_id + - [#3074](https://github.com/wasmerio/wasmer/pull/3074) Update chrono to 0.4.20, avoiding RUSTSEC-2020-0159 + - [#3070](https://github.com/wasmerio/wasmer/pull/3070) wasmer-cli: Allow create-exe to receive a static object as input + - [#3069](https://github.com/wasmerio/wasmer/pull/3069) Remove native feature entry from docs.rs metadata + - [#3057](https://github.com/wasmerio/wasmer/pull/3057) wasmer-cli: create-obj command + - [#3060](https://github.com/wasmerio/wasmer/pull/3060) CI: Unset rustup override after usage instead of setting it to stable + +## Fixed + + - [#3192](https://github.com/wasmerio/wasmer/pull/3192) fix the typos + - [#3185](https://github.com/wasmerio/wasmer/pull/3185) Fix `wasmer compile` command for non-x86 target + - [#3129](https://github.com/wasmerio/wasmer/pull/3129) Fix differences between -sys and -js API + - [#3137](https://github.com/wasmerio/wasmer/pull/3137) Fix cache path not being present during installation of cross-tarball + - [#3115](https://github.com/wasmerio/wasmer/pull/3115) Fix static object signature deserialization + - [#3093](https://github.com/wasmerio/wasmer/pull/3093) Fixed a potential issue when renaming a file + - [#3088](https://github.com/wasmerio/wasmer/pull/3088) Fixed an issue when renaming a file from a preopened dir directly (for 3084) + - [#3078](https://github.com/wasmerio/wasmer/pull/3078) Fix errors from "make lint" + - [#3052](https://github.com/wasmerio/wasmer/pull/3052) Fixed a memory corruption issue with JS memory operations that were râ€Ļ + - [#3058](https://github.com/wasmerio/wasmer/pull/3058) Fix trap tracking + + +## 3.0.0-alpha.4 - 2022/07/28 + +## Added + + - [#3035](https://github.com/wasmerio/wasmer/pull/3035) Added a simple divide by zero trap wast test (for #1899) + - [#3008](https://github.com/wasmerio/wasmer/pull/3008) Add check-public-api.yaml workflow + - [#3021](https://github.com/wasmerio/wasmer/pull/3021) Added back some needed relocation for arm64 llvm compiler + - [#2982](https://github.com/wasmerio/wasmer/pull/2982) Add a `rustfmt.toml` file to the repository + - [#2953](https://github.com/wasmerio/wasmer/pull/2953) Makefile: add `check` target + - [#2952](https://github.com/wasmerio/wasmer/pull/2952) CI: add make build-wasmer-wasm test + +## Changed + + - [#3051](https://github.com/wasmerio/wasmer/pull/3051) Updated Crenelift to v0.86.1 + - [#3038](https://github.com/wasmerio/wasmer/pull/3038) Re-introduce create-exe to wasmer-cli v3.0 + - [#3049](https://github.com/wasmerio/wasmer/pull/3049) Disable traps::trap_display_multi_module test for Windows+singlepass + - [#3047](https://github.com/wasmerio/wasmer/pull/3047) Improved EngineBuilder API + - [#3046](https://github.com/wasmerio/wasmer/pull/3046) Merge Backend into EngineBuilder and refactor feature flags + - [#3039](https://github.com/wasmerio/wasmer/pull/3039) Improved hashing/ids of function envs + - [#3029](https://github.com/wasmerio/wasmer/pull/3029) Remove Engine, Artifact traits, merge all Engines into one, make everything rkyv serialazable + - [#2892](https://github.com/wasmerio/wasmer/pull/2892) Implement new Context API for Wasmer 3.0 + - [#3031](https://github.com/wasmerio/wasmer/pull/3031) Update docs/migration_to_3.0.0.md + - [#3030](https://github.com/wasmerio/wasmer/pull/3030) Remove cranelift dependency from wasmer-wasi + - [#3028](https://github.com/wasmerio/wasmer/pull/3028) Ctx store rename + - [#3023](https://github.com/wasmerio/wasmer/pull/3023) Changed CI rust install action to dtolnay one + - [#3013](https://github.com/wasmerio/wasmer/pull/3013) Context api refactor + - [#2999](https://github.com/wasmerio/wasmer/pull/2999) Support --invoke option for emscripten files without _start function + - [#3003](https://github.com/wasmerio/wasmer/pull/3003) Remove RuntimeError::raise from public API + - [#3000](https://github.com/wasmerio/wasmer/pull/3000) Allow debugging of EXC_BAD_INSTRUCTION on macOS + - [#2946](https://github.com/wasmerio/wasmer/pull/2946) Removing dylib and staticlib engines in favor of a single Universal Engine + - [#2996](https://github.com/wasmerio/wasmer/pull/2996) Migrated al examples to new Context API + - [#2973](https://github.com/wasmerio/wasmer/pull/2973) Port C API to new Context API + - [#2974](https://github.com/wasmerio/wasmer/pull/2974) Context api tests + - [#2988](https://github.com/wasmerio/wasmer/pull/2988) Have make targets install-capi-lib,install-pkgconfig work without building the wasmer binary + - [#2976](https://github.com/wasmerio/wasmer/pull/2976) Upgrade enumset minimum version to one that compiles + - [#2969](https://github.com/wasmerio/wasmer/pull/2969) Port JS API to new Context API + - [#2966](https://github.com/wasmerio/wasmer/pull/2966) Singlepass nopanic + - [#2949](https://github.com/wasmerio/wasmer/pull/2949) Switch back to using custom LLVM builds on CI + - [#2963](https://github.com/wasmerio/wasmer/pull/2963) Remove libxcb and libwayland dependencies from wasmer-cli release build + - [#2957](https://github.com/wasmerio/wasmer/pull/2957) Enable multi-value handling in Singlepass compiler + - [#2941](https://github.com/wasmerio/wasmer/pull/2941) Implementation of WASIX and a fully networking for Web Assembly + - [#2947](https://github.com/wasmerio/wasmer/pull/2947) - Converted the WASI js test into a generic stdio test that works forâ€Ļ + - [#2940](https://github.com/wasmerio/wasmer/pull/2940) Merge `wasmer3` back to `master` branch + - [#2939](https://github.com/wasmerio/wasmer/pull/2939) Rename NativeFunc to TypedFunction + +## Fixed + + - [#3045](https://github.com/wasmerio/wasmer/pull/3045) Fixed WASI fd_read syscall when reading multiple iovs and read is partial (for #2904) + - [#2997](https://github.com/wasmerio/wasmer/pull/2997) Fix "run --invoke [function]" to behave the same as "run" + - [#3027](https://github.com/wasmerio/wasmer/pull/3027) Fixed residual package-doc issues + - [#3026](https://github.com/wasmerio/wasmer/pull/3026) test-js.yaml: fix typo + - [#3017](https://github.com/wasmerio/wasmer/pull/3017) Fixed translation in README.md + - [#3001](https://github.com/wasmerio/wasmer/pull/3001) Fix context capi ci errors + - [#2967](https://github.com/wasmerio/wasmer/pull/2967) Fix singlepass on arm64 that was trying to emit a sub opcode with a constant as destination (for #2959) + - [#2954](https://github.com/wasmerio/wasmer/pull/2954) Some fixes to x86_64 Singlepass compiler, when using atomics + - [#2950](https://github.com/wasmerio/wasmer/pull/2950) compiler-cranelift: Fix typo in enum variant + - [#2948](https://github.com/wasmerio/wasmer/pull/2948) Fix regression on gen_import_call_trampoline_arm64() + - [#2943](https://github.com/wasmerio/wasmer/pull/2943) Fix build error on some archs by using c_char instead of i8 + - [#2944](https://github.com/wasmerio/wasmer/pull/2944) Fix duplicate entries in the CHANGELOG + - [#2942](https://github.com/wasmerio/wasmer/pull/2942) Fix clippy lints + +## 2.3.0 - 2022/06/06 + +### Added +- [#2862](https://github.com/wasmerio/wasmer/pull/2862) Added CI builds for linux-aarch64 target. +- [#2811](https://github.com/wasmerio/wasmer/pull/2811) Added support for EH Frames in singlepass +- [#2851](https://github.com/wasmerio/wasmer/pull/2851) Allow Wasmer to compile to Wasm/WASI + +### Changed +- [#2807](https://github.com/wasmerio/wasmer/pull/2807) Run Wasm code in a separate stack +- [#2802](https://github.com/wasmerio/wasmer/pull/2802) Support Dylib engine with Singlepass +- [#2836](https://github.com/wasmerio/wasmer/pull/2836) Improve TrapInformation data stored at runtime +- [#2864](https://github.com/wasmerio/wasmer/pull/2864) `wasmer-cli`: remove wasi-experimental-io-devices from default builds +- [#2933](https://github.com/wasmerio/wasmer/pull/2933) Rename NativeFunc to TypedFunction. + +### Fixed +- [#2829](https://github.com/wasmerio/wasmer/pull/2829) Improve error message oriented from JS object. +- [#2828](https://github.com/wasmerio/wasmer/pull/2828) Fix JsImportObject resolver. +- [#2872](https://github.com/wasmerio/wasmer/pull/2872) Fix `WasmerEnv` finalizer +- [#2821](https://github.com/wasmerio/wasmer/pull/2821) Opt in `sys` feature + +## 2.2.1 - 2022/03/15 + +### Fixed +- [#2812](https://github.com/wasmerio/wasmer/pull/2812) Fixed another panic due to incorrect drop ordering. + +## 2.2.0 - 2022/02/28 + +### Added +- [#2775](https://github.com/wasmerio/wasmer/pull/2775) Added support for SSE 4.2 in the Singlepass compiler as an alternative to AVX. +- [#2805](https://github.com/wasmerio/wasmer/pull/2805) Enabled WASI experimental I/O devices by default in releases. + +### Fixed +- [#2795](https://github.com/wasmerio/wasmer/pull/2795) Fixed a bug in the Singlepass compiler introduced in #2775. +- [#2806](https://github.com/wasmerio/wasmer/pull/2806) Fixed a panic due to incorrect drop ordering of `Module` fields. + +## 2.2.0-rc2 - 2022/02/15 + +### Fixed +- [#2778](https://github.com/wasmerio/wasmer/pull/2778) Fixed f32_load/f64_load in Singlepass. Also fixed issues with out-of-range conditional branches. +- [#2786](https://github.com/wasmerio/wasmer/pull/2786) Fixed a potential integer overflow in WasmPtr memory access methods. +- [#2787](https://github.com/wasmerio/wasmer/pull/2787) Fixed a codegen regression in the Singlepass compiler due to non-determinism of `HashSet` iteration. + +## 2.2.0-rc1 - 2022/01/28 + +### Added +- [#2750](https://github.com/wasmerio/wasmer/pull/2750) Added Aarch64 support to Singlepass (both Linux and macOS). +- [#2753](https://github.com/wasmerio/wasmer/pull/2753) Re-add "dylib" to the list of default features. + +### Changed +- [#2747](https://github.com/wasmerio/wasmer/pull/2747) Use a standard header for metadata in all serialized modules. +- [#2759](https://github.com/wasmerio/wasmer/pull/2759) Use exact version for Wasmer crate dependencies. + +### Fixed +- [#2769](https://github.com/wasmerio/wasmer/pull/2769) Fixed deadlock in emscripten dynamic calls. +- [#2742](https://github.com/wasmerio/wasmer/pull/2742) Fixed WASMER_METADATA alignment in the dylib engine. +- [#2746](https://github.com/wasmerio/wasmer/pull/2746) Fixed invoking `wasmer binfmt register` from `$PATH`. +- [#2748](https://github.com/wasmerio/wasmer/pull/2748) Use trampolines for all libcalls in engine-universal and engine-dylib. +- [#2766](https://github.com/wasmerio/wasmer/pull/2766) Remove an attempt to reserve a GPR when no GPR clobbering is occurring. +- [#2768](https://github.com/wasmerio/wasmer/pull/2768) Fixed serialization of FrameInfo on Dylib engine. + +## 2.1.1 - 2021/12/20 + +### Added +- [#2726](https://github.com/wasmerio/wasmer/pull/2726) Added `externs_vec` method to `ImportObject`. +- [#2724](https://github.com/wasmerio/wasmer/pull/2724) Added access to the raw `Instance` JS object in Wsasmer-js. + +### CHanged +- [#2711](https://github.com/wasmerio/wasmer/pull/2711) Make C-API and Wasi dependencies more lean +- [#2706](https://github.com/wasmerio/wasmer/pull/2706) Refactored the Singlepass compiler in preparation for AArch64 support (no user visible changes). +### Fixed +- [#2717](https://github.com/wasmerio/wasmer/pull/2717) Allow `Exports` to be modified after being cloned. +- [#2719](https://github.com/wasmerio/wasmer/pull/2719) Fixed `wasm_importtype_new`'s Rust signature to not assume boxed vectors. +- [#2723](https://github.com/wasmerio/wasmer/pull/2723) Fixed a bug in parameter passing in the Singlepass compiler. +- [#2768](https://github.com/wasmerio/wasmer/pull/2768) Fixed issue with Frame Info on dylib engine. + +## 2.1.0 - 2021/11/30 + +### Added +- [#2574](https://github.com/wasmerio/wasmer/pull/2574) Added Windows support to Singlepass. +- [#2535](https://github.com/wasmerio/wasmer/pull/2435) Added iOS support for Wasmer. This relies on the `dylib-engine`. +- [#2460](https://github.com/wasmerio/wasmer/pull/2460) Wasmer can now compile to Javascript via `wasm-bindgen`. Use the `js-default` (and no default features) feature to try it!. +- [#2491](https://github.com/wasmerio/wasmer/pull/2491) Added support for WASI to Wasmer-js. +- [#2436](https://github.com/wasmerio/wasmer/pull/2436) Added the x86-32 bit variant support to LLVM compiler. +- [#2499](https://github.com/wasmerio/wasmer/pull/2499) Added a subcommand to linux wasmer-cli to register wasmer with binfmt_misc +- [#2511](https://github.com/wasmerio/wasmer/pull/2511) Added support for calling dynamic functions defined on the host +- [#2491](https://github.com/wasmerio/wasmer/pull/2491) Added support for WASI in Wasmer-js +- [#2592](https://github.com/wasmerio/wasmer/pull/2592) Added `ImportObject::get_namespace_exports` to allow modifying the contents of an existing namespace in an `ImportObject`. +- [#2694](https://github.com/wasmerio/wasmer/pull/2694) wasmer-js: Allow an `ImportObject` to be extended with a JS object. +- [#2698](https://github.com/wasmerio/wasmer/pull/2698) Provide WASI imports when invoking an explicit export from the CLI. +- [#2701](https://github.com/wasmerio/wasmer/pull/2701) Improved VFS API for usage from JS + +### Changed +- [#2460](https://github.com/wasmerio/wasmer/pull/2460) **breaking change** `wasmer` API usage with `no-default-features` requires now the `sys` feature to preserve old behavior. +- [#2476](https://github.com/wasmerio/wasmer/pull/2476) Removed unncessary abstraction `ModuleInfoTranslate` from `wasmer-compiler`. +- [#2442](https://github.com/wasmerio/wasmer/pull/2442) **breaking change** Improved `WasmPtr`, added `WasmCell` for host/guest interaction. `WasmPtr::deref` will now return `WasmCell<'a, T>` instead of `&'a Cell`, `WasmPtr::deref_mut` is now deleted from the API. +- [#2427](https://github.com/wasmerio/wasmer/pull/2427) Update `loupe` to 0.1.3. +- [#2685](https://github.com/wasmerio/wasmer/pull/2685) The minimum LLVM version for the LLVM compiler is now 12. LLVM 13 is used by default. +- [#2569](https://github.com/wasmerio/wasmer/pull/2569) Add `Send` and `Sync` to uses of the `LikeNamespace` trait object. +- [#2692](https://github.com/wasmerio/wasmer/pull/2692) Made module serialization deterministic. +- [#2693](https://github.com/wasmerio/wasmer/pull/2693) Validate CPU features when loading a deserialized module. + +### Fixed +- [#2599](https://github.com/wasmerio/wasmer/pull/2599) Fixed Universal engine for Linux/Aarch64 target. +- [#2587](https://github.com/wasmerio/wasmer/pull/2587) Fixed deriving `WasmerEnv` when aliasing `Result`. +- [#2518](https://github.com/wasmerio/wasmer/pull/2518) Remove temporary file used to creating an artifact when creating a Dylib engine artifact. +- [#2494](https://github.com/wasmerio/wasmer/pull/2494) Fixed `WasmerEnv` access when using `call_indirect` with the Singlepass compiler. +- [#2479](https://github.com/wasmerio/wasmer/pull/2479) Improved `wasmer validate` error message on non-wasm inputs. +- [#2454](https://github.com/wasmerio/wasmer/issues/2454) Won't set `WASMER_CACHE_DIR` for Windows. +- [#2426](https://github.com/wasmerio/wasmer/pull/2426) Fix the `wax` script generation. +- [#2635](https://github.com/wasmerio/wasmer/pull/2635) Fix cross-compilation for singlepass. +- [#2672](https://github.com/wasmerio/wasmer/pull/2672) Use `ENOENT` instead of `EINVAL` in some WASI syscalls for a non-existent file +- [#2547](https://github.com/wasmerio/wasmer/pull/2547) Delete temporary files created by the dylib engine. +- [#2548](https://github.com/wasmerio/wasmer/pull/2548) Fix stack probing on x86_64 linux with the cranelift compiler. +- [#2557](https://github.com/wasmerio/wasmer/pull/2557) [#2559](https://github.com/wasmerio/wasmer/pull/2559) Fix WASI dir path renaming. +- [#2560](https://github.com/wasmerio/wasmer/pull/2560) Fix signal handling on M1 MacOS. +- [#2474](https://github.com/wasmerio/wasmer/pull/2474) Fix permissions on `WASMER_CACHE_DIR` on Windows. +- [#2528](https://github.com/wasmerio/wasmer/pull/2528) [#2525](https://github.com/wasmerio/wasmer/pull/2525) [#2523](https://github.com/wasmerio/wasmer/pull/2523) [#2522](https://github.com/wasmerio/wasmer/pull/2522) [#2545](https://github.com/wasmerio/wasmer/pull/2545) [#2550](https://github.com/wasmerio/wasmer/pull/2550) [#2551](https://github.com/wasmerio/wasmer/pull/2551) Fix various bugs in the new VFS implementation. +- [#2552](https://github.com/wasmerio/wasmer/pull/2552) Fix stack guard handling on Windows. +- [#2585](https://github.com/wasmerio/wasmer/pull/2585) Fix build with 64-bit MinGW toolchain. +- [#2587](https://github.com/wasmerio/wasmer/pull/2587) Fix absolute import of `Result` in derive. +- [#2599](https://github.com/wasmerio/wasmer/pull/2599) Fix AArch64 support in the LLVM compiler. +- [#2655](https://github.com/wasmerio/wasmer/pull/2655) Fix argument parsing of `--dir` and `--mapdir`. +- [#2666](https://github.com/wasmerio/wasmer/pull/2666) Fix performance on Windows by using static memories by default. +- [#2667](https://github.com/wasmerio/wasmer/pull/2667) Fix error code for path_rename of a non-existant file +- [#2672](https://github.com/wasmerio/wasmer/pull/2672) Fix error code returned by some wasi fs syscalls for a non-existent file +- [#2673](https://github.com/wasmerio/wasmer/pull/2673) Fix BrTable codegen on the LLVM compiler +- [#2674](https://github.com/wasmerio/wasmer/pull/2674) Add missing `__WASI_RIGHT_FD_DATASYNC` for preopened directories +- [#2677](https://github.com/wasmerio/wasmer/pull/2677) Support 32-bit memories with 65536 pages +- [#2681](https://github.com/wasmerio/wasmer/pull/2681) Fix slow compilation in singlepass by using dynasm's `VecAssembler`. +- [#2690](https://github.com/wasmerio/wasmer/pull/2690) Fix memory leak when obtaining the stack bounds of a thread +- [#2699](https://github.com/wasmerio/wasmer/pull/2699) Partially fix unbounded memory leak from the FuncDataRegistry + +## 2.0.0 - 2021/06/16 + +### Added +- [#2411](https://github.com/wasmerio/wasmer/pull/2411) Extract types from `wasi` to a new `wasi-types` crate. +- [#2390](https://github.com/wasmerio/wasmer/pull/2390) Make `wasmer-vm` to compile on Windows 32bits. +- [#2402](https://github.com/wasmerio/wasmer/pull/2402) Add more examples and more doctests for `wasmer-middlewares`. + +### Changed +- [#2399](https://github.com/wasmerio/wasmer/pull/2399) Add the Dart integration in the `README.md`. + +### Fixed +- [#2386](https://github.com/wasmerio/wasmer/pull/2386) Handle properly when a module has no exported functions in the CLI. + +## 2.0.0-rc2 - 2021/06/03 + +### Fixed +- [#2383](https://github.com/wasmerio/wasmer/pull/2383) Fix bugs in the Wasmer CLI tool with the way `--version` and the name of the CLI tool itself were printed. + +## 2.0.0-rc1 - 2021/06/02 + +### Added +- [#2348](https://github.com/wasmerio/wasmer/pull/2348) Make Wasmer available on `aarch64-linux-android`. +- [#2315](https://github.com/wasmerio/wasmer/pull/2315) Make the Cranelift compiler working with the Native engine. +- [#2306](https://github.com/wasmerio/wasmer/pull/2306) Add support for the latest version of the Wasm SIMD proposal to compiler LLVM. +- [#2296](https://github.com/wasmerio/wasmer/pull/2296) Add support for the bulk memory proposal in compiler Singlepass and compiler LLVM. +- [#2291](https://github.com/wasmerio/wasmer/pull/2291) Type check tables when importing. +- [#2262](https://github.com/wasmerio/wasmer/pull/2262) Make parallelism optional for the Singlepass compiler. +- [#2249](https://github.com/wasmerio/wasmer/pull/2249) Make Cranelift unwind feature optional. +- [#2208](https://github.com/wasmerio/wasmer/pull/2208) Add a new CHANGELOG.md specific to our C API to make it easier for users primarily consuming our C API to keep up to date with changes that affect them. +- [#2154](https://github.com/wasmerio/wasmer/pull/2154) Implement Reference Types in the LLVM compiler. +- [#2003](https://github.com/wasmerio/wasmer/pull/2003) Wasmer works with musl, and is built, tested and packaged for musl. +- [#2250](https://github.com/wasmerio/wasmer/pull/2250) Use `rkyv` for the JIT/Universal engine. +- [#2190](https://github.com/wasmerio/wasmer/pull/2190) Use `rkyv` to read native `Module` artifact. +- [#2186](https://github.com/wasmerio/wasmer/pull/2186) Update and improve the Fuzz Testing infrastructure. +- [#2161](https://github.com/wasmerio/wasmer/pull/2161) Make NaN canonicalization configurable. +- [#2116](https://github.com/wasmerio/wasmer/pull/2116) Add a package for Windows that is not an installer, but all the `lib` and `include` files as for macOS and Linux. +- [#2123](https://github.com/wasmerio/wasmer/pull/2123) Use `ENABLE_{{compiler_name}}=(0|1)` to resp. force to disable or enable a compiler when running the `Makefile`, e.g. `ENABLE_LLVM=1 make build-wasmer`. +- [#2123](https://github.com/wasmerio/wasmer/pull/2123) `libwasmer` comes with all available compilers per target instead of Cranelift only. +- [#2135](https://github.com/wasmerio/wasmer/pull/2135) [Documentation](./PACKAGING.md) for Linux distribution maintainers +- [#2104](https://github.com/wasmerio/wasmer/pull/2104) Update WAsm core spectests and wasmparser. + +### Changed +- [#2369](https://github.com/wasmerio/wasmer/pull/2369) Remove the deprecated `--backend` option in the CLI. +- [#2368](https://github.com/wasmerio/wasmer/pull/2368) Remove the deprecated code in the `wasmer-wasi` crate. +- [#2367](https://github.com/wasmerio/wasmer/pull/2367) Remove the `deprecated` features and associated code in the `wasmer` crate. +- [#2366](https://github.com/wasmerio/wasmer/pull/2366) Remove the deprecated crates. +- [#2364](https://github.com/wasmerio/wasmer/pull/2364) Rename `wasmer-engine-object-file` to `wasmer-engine-staticlib`. +- [#2356](https://github.com/wasmerio/wasmer/pull/2356) Rename `wasmer-engine-native` to `wasmer-engine-dylib`. +- [#2340](https://github.com/wasmerio/wasmer/pull/2340) Rename `wasmer-engine-jit` to `wasmer-engine-universal`. +- [#2307](https://github.com/wasmerio/wasmer/pull/2307) Update Cranelift, implement low hanging fruit SIMD opcodes. +- [#2305](https://github.com/wasmerio/wasmer/pull/2305) Clean up and improve the trap API, more deterministic errors etc. +- [#2299](https://github.com/wasmerio/wasmer/pull/2299) Unused trap codes (due to Wasm spec changes), `HeapSetterOutOfBounds` and `TableSetterOutOfBounds` were removed from `wasmer_vm::TrapCode` and the numbering of the remaining variants has been adjusted. +- [#2293](https://github.com/wasmerio/wasmer/pull/2293) The `Memory::ty` trait method now returns `MemoryType` by value. `wasmer_vm::LinearMemory` now recomputes `MemoryType`'s `minimum` field when accessing its type. This behavior is what's expected by the latest spectests. `wasmer::Memory::ty` has also been updated to follow suit, it now returns `MemoryType` by value. +- [#2286](https://github.com/wasmerio/wasmer/pull/2286) Replace the `goblin` crate by the `object` crate. +- [#2281](https://github.com/wasmerio/wasmer/pull/2281) Refactor the `wasmer_vm` crate to remove unnecessary structs, reuse data when available etc. +- [#2251](https://github.com/wasmerio/wasmer/pull/2251) Wasmer CLI will now execute WASI modules with multiple WASI namespaces in them by default. Use `--allow-multiple-wasi-versions` to suppress the warning and use `--deny-multiple-wasi-versions` to make it an error. +- [#2201](https://github.com/wasmerio/wasmer/pull/2201) Implement `loupe::MemoryUsage` for `wasmer::Instance`. +- [#2200](https://github.com/wasmerio/wasmer/pull/2200) Implement `loupe::MemoryUsage` for `wasmer::Module`. +- [#2199](https://github.com/wasmerio/wasmer/pull/2199) Implement `loupe::MemoryUsage` for `wasmer::Store`. +- [#2195](https://github.com/wasmerio/wasmer/pull/2195) Remove dependency to `cranelift-entity`. +- [#2140](https://github.com/wasmerio/wasmer/pull/2140) Reduce the number of dependencies in the `wasmer.dll` shared library by statically compiling CRT. +- [#2113](https://github.com/wasmerio/wasmer/pull/2113) Bump minimum supported Rust version to 1.49 +- [#2144](https://github.com/wasmerio/wasmer/pull/2144) Bump cranelift version to 0.70 +- [#2149](https://github.com/wasmerio/wasmer/pull/2144) `wasmer-engine-native` looks for clang-11 instead of clang-10. +- [#2157](https://github.com/wasmerio/wasmer/pull/2157) Simplify the code behind `WasmPtr` + +### Fixed +- [#2397](https://github.com/wasmerio/wasmer/pull/2397) Fix WASI rename temporary file issue. +- [#2391](https://github.com/wasmerio/wasmer/pull/2391) Fix Singlepass emit bug, [#2347](https://github.com/wasmerio/wasmer/issues/2347) and [#2159](https://github.com/wasmerio/wasmer/issues/2159) +- [#2327](https://github.com/wasmerio/wasmer/pull/2327) Fix memory leak preventing internal instance memory from being freed when a WasmerEnv contained an exported extern (e.g. Memory, etc.). +- [#2247](https://github.com/wasmerio/wasmer/pull/2247) Internal WasiFS logic updated to be closer to what WASI libc does when finding a preopened fd for a path. +- [#2241](https://github.com/wasmerio/wasmer/pull/2241) Fix Undefined Behavior in setting memory in emscripten `EmEnv`. +- [#2224](https://github.com/wasmerio/wasmer/pull/2224) Enable SIMD based on actual Wasm features in the Cranelift compiler. +- [#2217](https://github.com/wasmerio/wasmer/pull/2217) Fix bug in `i64.rotr X 0` in the LLVM compiler. +- [#2290](https://github.com/wasmerio/wasmer/pull/2290) Handle Wasm modules with no imports in the CLI. +- [#2108](https://github.com/wasmerio/wasmer/pull/2108) The Object Native Engine generates code that now compiles correctly with C++. +- [#2125](https://github.com/wasmerio/wasmer/pull/2125) Fix RUSTSEC-2021-0023. +- [#2155](https://github.com/wasmerio/wasmer/pull/2155) Fix the implementation of shift and rotate in the LLVM compiler. +- [#2101](https://github.com/wasmerio/wasmer/pull/2101) cflags emitted by `wasmer config --pkg-config` are now correct. + +## 1.0.2 - 2021-02-04 + +### Added +- [#2053](https://github.com/wasmerio/wasmer/pull/2053) Implement the non-standard `wasi_get_unordered_imports` function in the C API. +- [#2072](https://github.com/wasmerio/wasmer/pull/2072) Add `wasm_config_set_target`, along with `wasm_target_t`, `wasm_triple_t` and `wasm_cpu_features_t` in the unstable C API. +- [#2059](https://github.com/wasmerio/wasmer/pull/2059) Ability to capture `stdout` and `stderr` with WASI in the C API. +- [#2040](https://github.com/wasmerio/wasmer/pull/2040) Add `InstanceHandle::vmoffsets` to expose the offsets of the `vmctx` region. +- [#2026](https://github.com/wasmerio/wasmer/pull/2026) Expose trap code of a `RuntimeError`, if it's a `Trap`. +- [#2054](https://github.com/wasmerio/wasmer/pull/2054) Add `wasm_config_delete` to the Wasm C API. +- [#2072](https://github.com/wasmerio/wasmer/pull/2072) Added cross-compilation to Wasm C API. + +### Changed +- [#2085](https://github.com/wasmerio/wasmer/pull/2085) Update to latest inkwell and LLVM 11. +- [#2037](https://github.com/wasmerio/wasmer/pull/2037) Improved parallelism of LLVM with the Native/Object engine +- [#2012](https://github.com/wasmerio/wasmer/pull/2012) Refactor Singlepass init stack assembly (more performant now) +- [#2036](https://github.com/wasmerio/wasmer/pull/2036) Optimize memory allocated for Function type definitions +- [#2083](https://github.com/wasmerio/wasmer/pull/2083) Mark `wasi_env_set_instance` and `wasi_env_set_memory` as deprecated. You may simply remove the calls with no side-effect. +- [#2056](https://github.com/wasmerio/wasmer/pull/2056) Change back to depend on the `enumset` crate instead of `wasmer_enumset` + +### Fixed +- [#2066](https://github.com/wasmerio/wasmer/pull/2066) Include 'extern "C"' in our C headers when included by C++ code. +- [#2090](https://github.com/wasmerio/wasmer/pull/2090) `wasi_env_t` needs to be freed with `wasi_env_delete` in the C API. +- [#2084](https://github.com/wasmerio/wasmer/pull/2084) Avoid calling the function environment finalizer more than once when the environment has been cloned in the C API. +- [#2069](https://github.com/wasmerio/wasmer/pull/2069) Use the new documentation for `include/README.md` in the Wasmer package. +- [#2042](https://github.com/wasmerio/wasmer/pull/2042) Parse more exotic environment variables in `wasmer run`. +- [#2041](https://github.com/wasmerio/wasmer/pull/2041) Documentation diagrams now have a solid white background rather than a transparent background. +- [#2070](https://github.com/wasmerio/wasmer/pull/2070) Do not drain the entire captured stream at first read with `wasi_env_read_stdout` or `_stderr` in the C API. +- [#2058](https://github.com/wasmerio/wasmer/pull/2058) Expose WASI versions to C correctly. +- [#2044](https://github.com/wasmerio/wasmer/pull/2044) Do not build C headers on docs.rs. + +## 1.0.1 - 2021-01-12 + +This release includes a breaking change in the API (changing the trait `enumset::EnumsetType` to `wasmer_enumset::EnumSetType` and changing `enumset::EnumSet` in signatures to `wasmer_enumset::EnumSet` to work around a breaking change introduced by `syn`) but is being released as a minor version because `1.0.0` is also in a broken state due to a breaking change introduced by `syn` which affects `enumset` and thus `wasmer`. + +This change is unlikely to affect any users of `wasmer`, but if it does please change uses of the `enumset` crate to the `wasmer_enumset` crate where possible. + +### Added +- [#2010](https://github.com/wasmerio/wasmer/pull/2010) A new, experimental, minified build of `wasmer` called `wasmer-headless` will now be included with releases. `wasmer-headless` is the `wasmer` VM without any compilers attached, so it can only run precompiled Wasm modules. +- [#2005](https://github.com/wasmerio/wasmer/pull/2005) Added the arguments `alias` and `optional` to `WasmerEnv` derive's `export` attribute. + +### Changed +- [#2006](https://github.com/wasmerio/wasmer/pull/2006) Use `wasmer_enumset`, a fork of the `enumset` crate to work around a breaking change in `syn` +- [#1985](https://github.com/wasmerio/wasmer/pull/1985) Bump minimum supported Rust version to 1.48 + +### Fixed +- [#2007](https://github.com/wasmerio/wasmer/pull/2007) Fix packaging of wapm on Windows +- [#2005](https://github.com/wasmerio/wasmer/pull/2005) Emscripten is now working again. + +## 1.0.0 - 2021-01-05 + +### Added + +- [#1969](https://github.com/wasmerio/wasmer/pull/1969) Added D integration to the README + +### Changed +- [#1979](https://github.com/wasmerio/wasmer/pull/1979) `WasmPtr::get_utf8_string` was renamed to `WasmPtr::get_utf8_str` and made `unsafe`. + +### Fixed +- [#1979](https://github.com/wasmerio/wasmer/pull/1979) `WasmPtr::get_utf8_string` now returns a `String`, fixing a soundness issue in certain circumstances. The old functionality is available under a new `unsafe` function, `WasmPtr::get_utf8_str`. + +## 1.0.0-rc1 - 2020-12-23 + +### Added + +* [#1894](https://github.com/wasmerio/wasmer/pull/1894) Added exports `wasmer::{CraneliftOptLevel, LLVMOptLevel}` to allow using `Cranelift::opt_level` and `LLVM::opt_level` directly via the `wasmer` crate + +### Changed + +* [#1941](https://github.com/wasmerio/wasmer/pull/1941) Turn `get_remaining_points`/`set_remaining_points` of the `Metering` middleware into free functions to allow using them in an ahead-of-time compilation setup +* [#1955](https://github.com/wasmerio/wasmer/pull/1955) Set `jit` as a default feature of the `wasmer-wasm-c-api` crate +* [#1944](https://github.com/wasmerio/wasmer/pull/1944) Require `WasmerEnv` to be `Send + Sync` even in dynamic functions. +* [#1963](https://github.com/wasmerio/wasmer/pull/1963) Removed `to_wasm_error` in favour of `impl From for WasmError` +* [#1962](https://github.com/wasmerio/wasmer/pull/1962) Replace `wasmparser::Result<()>` with `Result<(), MiddlewareError>` in middleware, allowing implementors to return errors in `FunctionMiddleware::feed` + +### Fixed + +- [#1949](https://github.com/wasmerio/wasmer/pull/1949) `wasm__vec_delete` functions no longer crash when the given vector is uninitialized, in the Wasmer C API +- [#1949](https://github.com/wasmerio/wasmer/pull/1949) The `wasm_frame_vec_t`, `wasm_functype_vec_t`, `wasm_globaltype_vec_t`, `wasm_memorytype_vec_t`, and `wasm_tabletype_vec_t` are now boxed vectors in the Wasmer C API + +## 1.0.0-beta2 - 2020-12-16 + +### Added + +* [#1916](https://github.com/wasmerio/wasmer/pull/1916) Add the `WASMER_VERSION*` constants with the `wasmer_version*` functions in the Wasmer C API +* [#1867](https://github.com/wasmerio/wasmer/pull/1867) Added `Metering::get_remaining_points` and `Metering::set_remaining_points` +* [#1881](https://github.com/wasmerio/wasmer/pull/1881) Added `UnsupportedTarget` error to `CompileError` +* [#1908](https://github.com/wasmerio/wasmer/pull/1908) Implemented `TryFrom>` for `i32`/`u32`/`i64`/`u64`/`f32`/`f64` +* [#1927](https://github.com/wasmerio/wasmer/pull/1927) Added mmap support in `Engine::deserialize_from_file` to speed up artifact loading +* [#1911](https://github.com/wasmerio/wasmer/pull/1911) Generalized signature type in `Function::new` and `Function::new_with_env` to accept owned and reference `FunctionType` as well as array pairs. This allows users to define signatures as constants. Implemented `From<([Type; $N], [Type; $M])>` for `FunctionType` to support this. + +### Changed + +- [#1865](https://github.com/wasmerio/wasmer/pull/1865) Require that implementors of `WasmerEnv` also implement `Send`, `Sync`, and `Clone`. +- [#1851](https://github.com/wasmerio/wasmer/pull/1851) Improve test suite and documentation of the Wasmer C API +- [#1874](https://github.com/wasmerio/wasmer/pull/1874) Set `CompilerConfig` to be owned (following wasm-c-api) +- [#1880](https://github.com/wasmerio/wasmer/pull/1880) Remove cmake dependency for tests +- [#1924](https://github.com/wasmerio/wasmer/pull/1924) Rename reference implementation `wasmer::Tunables` to `wasmer::BaseTunables`. Export trait `wasmer_engine::Tunables` as `wasmer::Tunables`. + +### Fixed + +- [#1865](https://github.com/wasmerio/wasmer/pull/1865) Fix memory leaks with host function environments. +- [#1870](https://github.com/wasmerio/wasmer/pull/1870) Fixed Trap instruction address maps in Singlepass +* [#1914](https://github.com/wasmerio/wasmer/pull/1914) Implemented `TryFrom for Pages` instead of `From for Pages` to properly handle overflow errors + +## 1.0.0-beta1 - 2020-12-01 + +### Added + +- [#1839](https://github.com/wasmerio/wasmer/pull/1839) Added support for Metering Middleware +- [#1837](https://github.com/wasmerio/wasmer/pull/1837) It is now possible to use exports of an `Instance` even after the `Instance` has been freed +- [#1831](https://github.com/wasmerio/wasmer/pull/1831) Added support for Apple Silicon chips (`arm64-apple-darwin`) +- [#1739](https://github.com/wasmerio/wasmer/pull/1739) Improved function environment setup via `WasmerEnv` proc macro. +- [#1649](https://github.com/wasmerio/wasmer/pull/1649) Add outline of migration to 1.0.0 docs. + +### Changed + +- [#1739](https://github.com/wasmerio/wasmer/pull/1739) Environments passed to host function- must now implement the `WasmerEnv` trait. You can implement it on your existing type with `#[derive(WasmerEnv)]`. +- [#1838](https://github.com/wasmerio/wasmer/pull/1838) Deprecate `WasiEnv::state_mut`: prefer `WasiEnv::state` instead. +- [#1663](https://github.com/wasmerio/wasmer/pull/1663) Function environments passed to host functions now must be passed by `&` instead of `&mut`. This is a breaking change. This change fixes a race condition when a host function is called from multiple threads. If you need mutability in your environment, consider using `std::sync::Mutex` or other synchronization primitives. +- [#1830](https://github.com/wasmerio/wasmer/pull/1830) Minimum supported Rust version bumped to 1.47.0 +- [#1810](https://github.com/wasmerio/wasmer/pull/1810) Make the `state` field of `WasiEnv` public + +### Fixed + +- [#1857](https://github.com/wasmerio/wasmer/pull/1857) Fix dynamic function with new Environment API +- [#1855](https://github.com/wasmerio/wasmer/pull/1855) Fix memory leak when using `wat2wasm` in the C API, the function now takes its output parameter by pointer rather than returning an allocated `wasm_byte_vec_t`. +- [#1841](https://github.com/wasmerio/wasmer/pull/1841) We will now panic when attempting to use a native function with a captured env as a host function. Previously this would silently do the wrong thing. See [#1840](https://github.com/wasmerio/wasmer/pull/1840) for info about Wasmer's support of closures as host functions. +- [#1764](https://github.com/wasmerio/wasmer/pull/1764) Fix bug in WASI `path_rename` allowing renamed files to be 1 directory below a preopened directory. + +## 1.0.0-alpha5 - 2020-11-06 + +### Added + +- [#1761](https://github.com/wasmerio/wasmer/pull/1761) Implement the `wasm_trap_t**` argument of `wasm_instance_new` in the Wasm C API. +- [#1687](https://github.com/wasmerio/wasmer/pull/1687) Add basic table example; fix ownership of local memory and local table metadata in the VM. +- [#1751](https://github.com/wasmerio/wasmer/pull/1751) Implement `wasm_trap_t` inside a function declared with `wasm_func_new_with_env` in the Wasm C API. +- [#1741](https://github.com/wasmerio/wasmer/pull/1741) Implement `wasm_memory_type` in the Wasm C API. +- [#1736](https://github.com/wasmerio/wasmer/pull/1736) Implement `wasm_global_type` in the Wasm C API. +- [#1699](https://github.com/wasmerio/wasmer/pull/1699) Update `wasm.h` to its latest version. +- [#1685](https://github.com/wasmerio/wasmer/pull/1685) Implement `wasm_exporttype_delete` in the Wasm C API. +- [#1725](https://github.com/wasmerio/wasmer/pull/1725) Implement `wasm_func_type` in the Wasm C API. +- [#1715](https://github.com/wasmerio/wasmer/pull/1715) Register errors from `wasm_module_serialize` in the Wasm C API. +- [#1709](https://github.com/wasmerio/wasmer/pull/1709) Implement `wasm_module_name` and `wasm_module_set_name` in the Wasm(er) C API. +- [#1700](https://github.com/wasmerio/wasmer/pull/1700) Implement `wasm_externtype_copy` in the Wasm C API. +- [#1785](https://github.com/wasmerio/wasmer/pull/1785) Add more examples on the Rust API. +- [#1783](https://github.com/wasmerio/wasmer/pull/1783) Handle initialized but empty results in `wasm_func_call` in the Wasm C API. +- [#1780](https://github.com/wasmerio/wasmer/pull/1780) Implement new SIMD zero-extend loads in compiler-llvm. +- [#1754](https://github.com/wasmerio/wasmer/pull/1754) Implement aarch64 ABI for compiler-llvm. +- [#1693](https://github.com/wasmerio/wasmer/pull/1693) Add `wasmer create-exe` subcommand. + +### Changed + +- [#1772](https://github.com/wasmerio/wasmer/pull/1772) Remove lifetime parameter from `NativeFunc`. +- [#1762](https://github.com/wasmerio/wasmer/pull/1762) Allow the `=` sign in a WASI environment variable value. +- [#1710](https://github.com/wasmerio/wasmer/pull/1710) Memory for function call trampolines is now owned by the Artifact. +- [#1781](https://github.com/wasmerio/wasmer/pull/1781) Cranelift upgrade to 0.67. +- [#1777](https://github.com/wasmerio/wasmer/pull/1777) Wasmparser update to 0.65. +- [#1775](https://github.com/wasmerio/wasmer/pull/1775) Improve LimitingTunables implementation. +- [#1720](https://github.com/wasmerio/wasmer/pull/1720) Autodetect llvm regardless of architecture. + +### Fixed + +- [#1718](https://github.com/wasmerio/wasmer/pull/1718) Fix panic in the API in some situations when the memory's min bound was greater than the memory's max bound. +- [#1731](https://github.com/wasmerio/wasmer/pull/1731) In compiler-llvm always load before store, to trigger any traps before any bytes are written. + +## 1.0.0-alpha4 - 2020-10-08 + +### Added +- [#1635](https://github.com/wasmerio/wasmer/pull/1635) Implement `wat2wasm` in the Wasm C API. +- [#1636](https://github.com/wasmerio/wasmer/pull/1636) Implement `wasm_module_validate` in the Wasm C API. +- [#1657](https://github.com/wasmerio/wasmer/pull/1657) Implement `wasm_trap_t` and `wasm_frame_t` for Wasm C API; add examples in Rust and C of exiting early with a host function. + +### Fixed +- [#1690](https://github.com/wasmerio/wasmer/pull/1690) Fix `wasm_memorytype_limits` where `min` and `max` represents pages, not bytes. Additionally, fixes the max limit sentinel value. +- [#1671](https://github.com/wasmerio/wasmer/pull/1671) Fix probestack firing inappropriately, and sometimes over/under allocating stack. +- [#1660](https://github.com/wasmerio/wasmer/pull/1660) Fix issue preventing map-dir aliases starting with `/` from working properly. +- [#1624](https://github.com/wasmerio/wasmer/pull/1624) Add Value::I32/Value::I64 converters from unsigned ints. + +### Changed +- [#1682](https://github.com/wasmerio/wasmer/pull/1682) Improve error reporting when making a memory with invalid settings. +- [#1691](https://github.com/wasmerio/wasmer/pull/1691) Bump minimum supported Rust version to 1.46.0 +- [#1645](https://github.com/wasmerio/wasmer/pull/1645) Move the install script to https://github.com/wasmerio/wasmer-install + +## 1.0.0-alpha3 - 2020-09-14 + +### Fixed + +- [#1620](https://github.com/wasmerio/wasmer/pull/1620) Fix bug causing the Wapm binary to not be packaged with the release +- [#1619](https://github.com/wasmerio/wasmer/pull/1619) Improve error message in engine-native when C compiler is missing + +## 1.0.0-alpha02.0 - 2020-09-11 + +### Added + +- [#1566](https://github.com/wasmerio/wasmer/pull/1566) Add support for opening special Unix files to the WASI FS + +### Fixed + +- [#1602](https://github.com/wasmerio/wasmer/pull/1602) Fix panic when calling host functions with negative numbers in certain situations +- [#1590](https://github.com/wasmerio/wasmer/pull/1590) Fix soundness issue in API of vm::Global + +## TODO: 1.0.0-alpha01.0 + +- Wasmer refactor lands + +## 0.17.1 - 2020-06-24 + +### Changed +- [#1439](https://github.com/wasmerio/wasmer/pull/1439) Move `wasmer-interface-types` into its own repository + +### Fixed + +- [#1554](https://github.com/wasmerio/wasmer/pull/1554) Update supported stable Rust version to 1.45.2. +- [#1552](https://github.com/wasmerio/wasmer/pull/1552) Disable `sigint` handler by default. + +## 0.17.0 - 2020-05-11 + +### Added +- [#1331](https://github.com/wasmerio/wasmer/pull/1331) Implement the `record` type and instrutions for WIT +- [#1345](https://github.com/wasmerio/wasmer/pull/1345) Adding ARM testing in Azure Pipelines +- [#1329](https://github.com/wasmerio/wasmer/pull/1329) New numbers and strings instructions for WIT +- [#1285](https://github.com/wasmerio/wasmer/pull/1285) Greatly improve errors in `wasmer-interface-types` +- [#1303](https://github.com/wasmerio/wasmer/pull/1303) NaN canonicalization for singlepass backend. +- [#1313](https://github.com/wasmerio/wasmer/pull/1313) Add new high-level public API through `wasmer` crate. Includes many updates including: + - Minor improvement: `imports!` macro now handles no trailing comma as well as a trailing comma in namespaces and between namespaces. + - New methods on `Module`: `exports`, `imports`, and `custom_sections`. + - New way to get exports from an instance with `let func_name: Func = instance.exports.get("func_name");`. + - Improved `Table` APIs including `set` which now allows setting functions directly. TODO: update this more if `Table::get` gets made public in this PR + - TODO: finish the list of changes here +- [#1305](https://github.com/wasmerio/wasmer/pull/1305) Handle panics from DynamicFunc. +- [#1300](https://github.com/wasmerio/wasmer/pull/1300) Add support for multiple versions of WASI tests: wasitests now test all versions of WASI. +- [#1292](https://github.com/wasmerio/wasmer/pull/1292) Experimental Support for Android (x86_64 and AArch64) + +### Fixed +- [#1283](https://github.com/wasmerio/wasmer/pull/1283) Workaround for floating point arguments and return values in `DynamicFunc`s. + +### Changed +- [#1401](https://github.com/wasmerio/wasmer/pull/1401) Make breaking change to `RuntimeError`: `RuntimeError` is now more explicit about its possible error values allowing for better insight into why a call into Wasm failed. +- [#1382](https://github.com/wasmerio/wasmer/pull/1382) Refactored test infranstructure (part 2) +- [#1380](https://github.com/wasmerio/wasmer/pull/1380) Refactored test infranstructure (part 1) +- [#1357](https://github.com/wasmerio/wasmer/pull/1357) Refactored bin commands into separate files +- [#1335](https://github.com/wasmerio/wasmer/pull/1335) Change mutability of `memory` to `const` in `wasmer_memory_data_length` in the C API +- [#1332](https://github.com/wasmerio/wasmer/pull/1332) Add option to `CompilerConfig` to force compiler IR verification off even when `debug_assertions` are enabled. This can be used to make debug builds faster, which may be important if you're creating a library that wraps Wasmer and depend on the speed of debug builds. +- [#1320](https://github.com/wasmerio/wasmer/pull/1320) Change `custom_sections` field in `ModuleInfo` to be more standards compliant by allowing multiple custom sections with the same name. To get the old behavior with the new API, you can add `.last().unwrap()` to accesses. For example, `module_info.custom_sections["custom_section_name"].last().unwrap()`. +- [#1301](https://github.com/wasmerio/wasmer/pull/1301) Update supported stable Rust version to 1.41.1. + +## 0.16.2 - 2020-03-11 + +### Fixed + +- [#1294](https://github.com/wasmerio/wasmer/pull/1294) Fix bug related to system calls in WASI that rely on reading from WasmPtrs as arrays of length 0. `WasmPtr` will now succeed on length 0 arrays again. + +## 0.16.1 - 2020-03-11 + +### Fixed + +- [#1291](https://github.com/wasmerio/wasmer/pull/1291) Fix installation packaging script to package the `wax` command. + +## 0.16.0 - 2020-03-11 + +### Added +- [#1286](https://github.com/wasmerio/wasmer/pull/1286) Updated Windows Wasmer icons. Add wax +- [#1284](https://github.com/wasmerio/wasmer/pull/1284) Implement string and memory instructions in `wasmer-interface-types` + +### Fixed +- [#1272](https://github.com/wasmerio/wasmer/pull/1272) Fix off-by-one error bug when accessing memory with a `WasmPtr` that contains the last valid byte of memory. Also changes the behavior of `WasmPtr` with a length of 0 and `WasmPtr` where `std::mem::size_of::()` is 0 to always return `None` + +## 0.15.0 - 2020-03-04 + +- [#1263](https://github.com/wasmerio/wasmer/pull/1263) Changed the behavior of some WASI syscalls to now handle preopened directories more properly. Changed default `--debug` logging to only show Wasmer-related messages. +- [#1217](https://github.com/wasmerio/wasmer/pull/1217) Polymorphic host functions based on dynamic trampoline generation. +- [#1252](https://github.com/wasmerio/wasmer/pull/1252) Allow `/` in wasi `--mapdir` wasm path. +- [#1212](https://github.com/wasmerio/wasmer/pull/1212) Add support for GDB JIT debugging: + - Add `--generate-debug-info` and `-g` flags to `wasmer run` to generate debug information during compilation. The debug info is passed via the GDB JIT interface to a debugger to allow source-level debugging of Wasm files. Currently only available on clif-backend. + - Break public middleware APIs: there is now a `source_loc` parameter that should be passed through if applicable. + - Break compiler trait methods such as `feed_local`, `feed_event` as well as `ModuleCodeGenerator::finalize`. + +## 0.14.1 - 2020-02-24 + +- [#1245](https://github.com/wasmerio/wasmer/pull/1245) Use Ubuntu 16.04 in CI so that we use an earlier version of GLIBC. +- [#1234](https://github.com/wasmerio/wasmer/pull/1234) Check for unused excluded spectest failures. +- [#1232](https://github.com/wasmerio/wasmer/pull/1232) `wasmer-interface-types` has a WAT decoder. + +## 0.14.0 - 2020-02-20 + +- [#1233](https://github.com/wasmerio/wasmer/pull/1233) Improved Wasmer C API release artifacts. +- [#1216](https://github.com/wasmerio/wasmer/pull/1216) `wasmer-interface-types` receives a binary encoder. +- [#1228](https://github.com/wasmerio/wasmer/pull/1228) Singlepass cleanup: Resolve several FIXMEs and remove protect_unix. +- [#1218](https://github.com/wasmerio/wasmer/pull/1218) Enable Cranelift verifier in debug mode. Fix bug with table indices being the wrong type. +- [#787](https://github.com/wasmerio/wasmer/pull/787) New crate `wasmer-interface-types` to implement WebAssembly Interface Types. +- [#1213](https://github.com/wasmerio/wasmer/pull/1213) Fixed WASI `fdstat` to detect `isatty` properly. +- [#1192](https://github.com/wasmerio/wasmer/pull/1192) Use `ExceptionCode` for error representation. +- [#1191](https://github.com/wasmerio/wasmer/pull/1191) Fix singlepass miscompilation on `Operator::CallIndirect`. +- [#1180](https://github.com/wasmerio/wasmer/pull/1180) Fix compilation for target `x86_64-unknown-linux-musl`. +- [#1170](https://github.com/wasmerio/wasmer/pull/1170) Improve the WasiFs builder API with convenience methods for overriding stdin, stdout, and stderr as well as a new sub-builder for controlling the permissions and properties of preopened directories. Also breaks that implementations of `WasiFile` must be `Send` -- please file an issue if this change causes you any issues. +- [#1161](https://github.com/wasmerio/wasmer/pull/1161) Require imported functions to be `Send`. This is a breaking change that fixes a soundness issue in the API. +- [#1140](https://github.com/wasmerio/wasmer/pull/1140) Use [`blake3`](https://github.com/BLAKE3-team/BLAKE3) as default hashing algorithm for caching. +- [#1129](https://github.com/wasmerio/wasmer/pull/1129) Standard exception types for singlepass backend. + +## 0.13.1 - 2020-01-16 +- Fix bug in wapm related to the `package.wasmer_extra_flags` entry in the manifest + +## 0.13.0 - 2020-01-15 + +Special thanks to [@repi](https://github.com/repi) and [@srenatus](https://github.com/srenatus) for their contributions! + +- [#1153](https://github.com/wasmerio/wasmer/pull/1153) Added Wasmex, an Elixir language integration, to the README +- [#1133](https://github.com/wasmerio/wasmer/pull/1133) New `wasmer_trap` function in the C API, to properly error from within a host function +- [#1147](https://github.com/wasmerio/wasmer/pull/1147) Remove `log` and `trace` macros from `wasmer-runtime-core`, remove `debug` and `trace` features from `wasmer-*` crates, use the `log` crate for logging and use `fern` in the Wasmer CLI binary to output log messages. Colorized output will be enabled automatically if printing to a terminal, to force colorization on or off, set the `WASMER_COLOR` environment variable to `true` or `false`. +- [#1128](https://github.com/wasmerio/wasmer/pull/1128) Fix a crash when a host function is missing and the `allow_missing_functions` flag is enabled +- [#1099](https://github.com/wasmerio/wasmer/pull/1099) Remove `backend::Backend` from `wasmer_runtime_core` +- [#1097](https://github.com/wasmerio/wasmer/pull/1097) Move inline breakpoint outside of runtime backend +- [#1095](https://github.com/wasmerio/wasmer/pull/1095) Update to cranelift 0.52. +- [#1092](https://github.com/wasmerio/wasmer/pull/1092) Add `get_utf8_string_with_nul` to `WasmPtr` to read nul-terminated strings from memory. +- [#1071](https://github.com/wasmerio/wasmer/pull/1071) Add support for non-trapping float-to-int conversions, enabled by default. + +## 0.12.0 - 2019-12-18 + +Special thanks to [@ethanfrey](https://github.com/ethanfrey), [@AdamSLevy](https://github.com/AdamSLevy), [@Jasper-Bekkers](https://github.com/Jasper-Bekkers), [@srenatus](https://github.com/srenatus) for their contributions! + +- [#1078](https://github.com/wasmerio/wasmer/pull/1078) Increase the maximum number of parameters `Func` can take +- [#1062](https://github.com/wasmerio/wasmer/pull/1062) Expose some opt-in Emscripten functions to the C API +- [#1032](https://github.com/wasmerio/wasmer/pull/1032) Change the signature of the Emscripten `abort` function to work with Emscripten 1.38.30 +- [#1060](https://github.com/wasmerio/wasmer/pull/1060) Test the capi with all the backends +- [#1069](https://github.com/wasmerio/wasmer/pull/1069) Add function `get_memory_and_data` to `Ctx` to help prevent undefined behavior and mutable aliasing. It allows accessing memory while borrowing data mutably for the `Ctx` lifetime. This new function is now being used in `wasmer-wasi`. +- [#1058](https://github.com/wasmerio/wasmer/pull/1058) Fix minor panic issue when `wasmer::compile_with` called with llvm backend. +- [#858](https://github.com/wasmerio/wasmer/pull/858) Minor panic fix when wasmer binary with `loader` option run a module without exported `_start` function. +- [#1056](https://github.com/wasmerio/wasmer/pull/1056) Improved `--invoke` args parsing (supporting `i32`, `i64`, `f32` and `f32`) in Wasmer CLI +- [#1054](https://github.com/wasmerio/wasmer/pull/1054) Improve `--invoke` output in Wasmer CLI +- [#1053](https://github.com/wasmerio/wasmer/pull/1053) For RuntimeError and breakpoints, use Box instead of Box. +- [#1052](https://github.com/wasmerio/wasmer/pull/1052) Fix minor panic and improve Error handling in singlepass backend. +- [#1050](https://github.com/wasmerio/wasmer/pull/1050) Attach C & C++ headers to releases. +- [#1033](https://github.com/wasmerio/wasmer/pull/1033) Set cranelift backend as default compiler backend again, require at least one backend to be enabled for Wasmer CLI +- [#1044](https://github.com/wasmerio/wasmer/pull/1044) Enable AArch64 support in the LLVM backend. +- [#1030](https://github.com/wasmerio/wasmer/pull/1030) Ability to generate `ImportObject` for a specific version WASI version with the C API. +- [#1028](https://github.com/wasmerio/wasmer/pull/1028) Introduce strict/non-strict modes for `get_wasi_version` +- [#1029](https://github.com/wasmerio/wasmer/pull/1029) Add the “floating” `WasiVersion::Latest` version. +- [#1006](https://github.com/wasmerio/wasmer/pull/1006) Fix minor panic issue when `wasmer::compile_with` called with llvm backend +- [#1009](https://github.com/wasmerio/wasmer/pull/1009) Enable LLVM verifier for all tests, add new llvm-backend-tests crate. +- [#1022](https://github.com/wasmerio/wasmer/pull/1022) Add caching support for Singlepass backend. +- [#1004](https://github.com/wasmerio/wasmer/pull/1004) Add the Auto backend to enable to adapt backend usage depending on wasm file executed. +- [#1068](https://github.com/wasmerio/wasmer/pull/1068) Various cleanups for the singlepass backend on AArch64. + +## 0.11.0 - 2019-11-22 + +- [#713](https://github.com/wasmerio/wasmer/pull/713) Add AArch64 support for singlepass. +- [#995](https://github.com/wasmerio/wasmer/pull/995) Detect when a global is read without being initialized (emit a proper error instead of panicking) +- [#996](https://github.com/wasmerio/wasmer/pull/997) Refactored spectests, emtests and wasitests to use default compiler logic +- [#992](https://github.com/wasmerio/wasmer/pull/992) Updates WAPM version to 0.4.1, fix arguments issue introduced in #990 +- [#990](https://github.com/wasmerio/wasmer/pull/990) Default wasmer CLI to `run`. Wasmer will now attempt to parse unrecognized command line options as if they were applied to the run command: `wasmer mywasm.wasm --dir=.` now works! +- [#987](https://github.com/wasmerio/wasmer/pull/987) Fix `runtime-c-api` header files when compiled by gnuc. +- [#957](https://github.com/wasmerio/wasmer/pull/957) Change the meaning of `wasmer_wasix::is_wasi_module` to detect any type of WASI module, add support for new wasi snapshot_preview1 +- [#934](https://github.com/wasmerio/wasmer/pull/934) Simplify float expressions in the LLVM backend. + +## 0.10.2 - 2019-11-18 + +- [#968](https://github.com/wasmerio/wasmer/pull/968) Added `--invoke` option to the command +- [#964](https://github.com/wasmerio/wasmer/pull/964) Enable cross-compilation for specific target +- [#971](https://github.com/wasmerio/wasmer/pull/971) In LLVM backend, use unaligned loads and stores for non-atomic accesses to wasmer memory. +- [#960](https://github.com/wasmerio/wasmer/pull/960) Fix `runtime-c-api` header files when compiled by clang. +- [#925](https://github.com/wasmerio/wasmer/pull/925) Host functions can be closures with a captured environment. +- [#917](https://github.com/wasmerio/wasmer/pull/917) Host functions (aka imported functions) may not have `&mut vm::Ctx` as first argument, i.e. the presence of the `&mut vm::Ctx` argument is optional. +- [#915](https://github.com/wasmerio/wasmer/pull/915) All backends share the same definition of `Trampoline` (defined in `wasmer-runtime-core`). + +## 0.10.1 - 2019-11-11 + +- [#952](https://github.com/wasmerio/wasmer/pull/952) Use C preprocessor to properly hide trampoline functions on Windows and non-x86_64 targets. + +## 0.10.0 - 2019-11-11 + +Special thanks to [@newpavlov](https://github.com/newpavlov) and [@Maxgy](https://github.com/Maxgy) for their contributions! + +- [#942](https://github.com/wasmerio/wasmer/pull/942) Deny missing docs in runtime core and add missing docs +- [#939](https://github.com/wasmerio/wasmer/pull/939) Fix bug causing attempts to append to files with WASI to delete the contents of the file +- [#940](https://github.com/wasmerio/wasmer/pull/940) Update supported Rust version to 1.38+ +- [#923](https://github.com/wasmerio/wasmer/pull/923) Fix memory leak in the C API caused by an incorrect cast in `wasmer_trampoline_buffer_destroy` +- [#921](https://github.com/wasmerio/wasmer/pull/921) In LLVM backend, annotate all memory accesses with TBAA metadata. +- [#883](https://github.com/wasmerio/wasmer/pull/883) Allow floating point operations to have arbitrary inputs, even including SNaNs. +- [#856](https://github.com/wasmerio/wasmer/pull/856) Expose methods in the runtime C API to get a WASI import object + +## 0.9.0 - 2019-10-23 + +Special thanks to @alocquet for their contributions! + +- [#898](https://github.com/wasmerio/wasmer/pull/898) State tracking is now disabled by default in the LLVM backend. It can be enabled with `--track-state`. +- [#861](https://github.com/wasmerio/wasmer/pull/861) Add descriptions to `unimplemented!` macro in various places +- [#897](https://github.com/wasmerio/wasmer/pull/897) Removes special casing of stdin, stdout, and stderr in WASI. Closing these files now works. Removes `stdin`, `stdout`, and `stderr` from `WasiFS`, replaced by the methods `stdout`, `stdout_mut`, and so on. +- [#863](https://github.com/wasmerio/wasmer/pull/863) Fix min and max for cases involving NaN and negative zero when using the LLVM backend. + +## 0.8.0 - 2019-10-02 + +Special thanks to @jdanford for their contributions! + +- [#850](https://github.com/wasmerio/wasmer/pull/850) New `WasiStateBuilder` API. small, add misc. breaking changes to existing API (for example, changing the preopen dirs arg on `wasi::generate_import_object` from `Vec` to `Vec`) +- [#852](https://github.com/wasmerio/wasmer/pull/852) Make minor grammar/capitalization fixes to README.md +- [#841](https://github.com/wasmerio/wasmer/pull/841) Slightly improve rustdoc documentation and small updates to outdated info in readme files +- [#836](https://github.com/wasmerio/wasmer/pull/836) Update Cranelift fork version to `0.44.0` +- [#839](https://github.com/wasmerio/wasmer/pull/839) Change supported version to stable Rust 1.37+ +- [#834](https://github.com/wasmerio/wasmer/pull/834) Fix panic when unwraping `wasmer` arguments +- [#835](https://github.com/wasmerio/wasmer/pull/835) Add parallel execution example (independent instances created from the same `ImportObject` and `Module` run with rayon) +- [#834](https://github.com/wasmerio/wasmer/pull/834) Fix panic when parsing numerical arguments for no-ABI targets run with the wasmer binary +- [#833](https://github.com/wasmerio/wasmer/pull/833) Add doc example of using ImportObject's new `maybe_with_namespace` method +- [#832](https://github.com/wasmerio/wasmer/pull/832) Delete unused runtime ABI +- [#809](https://github.com/wasmerio/wasmer/pull/809) Fix bugs leading to panics in `LocalBacking`. +- [#831](https://github.com/wasmerio/wasmer/pull/831) Add support for atomic operations, excluding wait and notify, to singlepass. +- [#822](https://github.com/wasmerio/wasmer/pull/822) Update Cranelift fork version to `0.43.1` +- [#829](https://github.com/wasmerio/wasmer/pull/829) Fix deps on `make bench-*` commands; benchmarks don't compile other backends now +- [#807](https://github.com/wasmerio/wasmer/pull/807) Implement Send for `Instance`, breaking change on `ImportObject`, remove method `get_namespace` replaced with `with_namespace` and `maybe_with_namespace` +- [#817](https://github.com/wasmerio/wasmer/pull/817) Add document for tracking features across backends and language integrations, [docs/feature_matrix.md] +- [#823](https://github.com/wasmerio/wasmer/issues/823) Improved Emscripten / WASI integration +- [#821](https://github.com/wasmerio/wasmer/issues/821) Remove patch version on most deps Cargo manifests. This gives Wasmer library users more control over which versions of the deps they use. +- [#820](https://github.com/wasmerio/wasmer/issues/820) Remove null-pointer checks in `WasmPtr` from runtime-core, re-add them in Emscripten +- [#803](https://github.com/wasmerio/wasmer/issues/803) Add method to `Ctx` to invoke functions by their `TableIndex` +- [#790](https://github.com/wasmerio/wasmer/pull/790) Fix flaky test failure with LLVM, switch to large code model. +- [#788](https://github.com/wasmerio/wasmer/pull/788) Use union merge on the changelog file. +- [#785](https://github.com/wasmerio/wasmer/pull/785) Include Apache license file for spectests. +- [#786](https://github.com/wasmerio/wasmer/pull/786) In the LLVM backend, lower atomic wasm operations to atomic machine instructions. +- [#784](https://github.com/wasmerio/wasmer/pull/784) Fix help string for wasmer run. + +## 0.7.0 - 2019-09-12 + +Special thanks to @YaronWittenstein @penberg for their contributions. + +- [#776](https://github.com/wasmerio/wasmer/issues/776) Allow WASI preopened fds to be closed +- [#774](https://github.com/wasmerio/wasmer/issues/774) Add more methods to the `WasiFile` trait +- [#772](https://github.com/wasmerio/wasmer/issues/772) [#770](https://github.com/wasmerio/wasmer/issues/770) Handle more internal failures by passing back errors +- [#756](https://github.com/wasmerio/wasmer/issues/756) Allow NULL parameter and 0 arity in `wasmer_export_func_call` C API +- [#747](https://github.com/wasmerio/wasmer/issues/747) Return error instead of panicking on traps when using the Wasmer binary +- [#741](https://github.com/wasmerio/wasmer/issues/741) Add validate Wasm fuzz target +- [#733](https://github.com/wasmerio/wasmer/issues/733) Remove dependency on compiler backends for `middleware-common` +- [#732](https://github.com/wasmerio/wasmer/issues/732) [#731](https://github.com/wasmerio/wasmer/issues/731) WASI bug fixes and improvements +- [#726](https://github.com/wasmerio/wasmer/issues/726) Add serialization and deserialization for Wasi State +- [#716](https://github.com/wasmerio/wasmer/issues/716) Improve portability of install script +- [#714](https://github.com/wasmerio/wasmer/issues/714) Add Code of Conduct +- [#708](https://github.com/wasmerio/wasmer/issues/708) Remove unconditional dependency on Cranelift in the C API +- [#703](https://github.com/wasmerio/wasmer/issues/703) Fix compilation on AArch64 Linux +- [#702](https://github.com/wasmerio/wasmer/issues/702) Add SharedMemory to Wasmer. Add `--enable-threads` flag, add partial implementation of atomics to LLVM backend. +- [#698](https://github.com/wasmerio/wasmer/issues/698) [#690](https://github.com/wasmerio/wasmer/issues/690) [#687](https://github.com/wasmerio/wasmer/issues/690) Fix panics in Emscripten +- [#689](https://github.com/wasmerio/wasmer/issues/689) Replace `wasmer_runtime_code::memory::Atomic` with `std::sync::atomic` atomics, changing its interface +- [#680](https://github.com/wasmerio/wasmer/issues/680) [#673](https://github.com/wasmerio/wasmer/issues/673) [#669](https://github.com/wasmerio/wasmer/issues/669) [#660](https://github.com/wasmerio/wasmer/issues/660) [#659](https://github.com/wasmerio/wasmer/issues/659) Misc. runtime and singlepass fixes +- [#677](https://github.com/wasmerio/wasmer/issues/677) [#675](https://github.com/wasmerio/wasmer/issues/675) [#674](https://github.com/wasmerio/wasmer/issues/674) LLVM backend fixes and improvements +- [#671](https://github.com/wasmerio/wasmer/issues/671) Implement fs polling in `wasi::poll_oneoff` for Unix-like platforms +- [#656](https://github.com/wasmerio/wasmer/issues/656) Move CI to Azure Pipelines +- [#650](https://github.com/wasmerio/wasmer/issues/650) Implement `wasi::path_rename`, improve WASI FS public api, and allow open files to exist even when the underlying file is deleted +- [#643](https://github.com/wasmerio/wasmer/issues/643) Implement `wasi::path_symlink` and improve WASI FS public api IO error reporting +- [#608](https://github.com/wasmerio/wasmer/issues/608) Implement wasi syscalls `fd_allocate`, `fd_sync`, `fd_pread`, `path_link`, `path_filestat_set_times`; update WASI fs API in a WIP way; reduce coupling of WASI code to host filesystem; make debug messages from WASI more readable; improve rights-checking when calling syscalls; implement reference counting on inodes; misc bug fixes and improvements +- [#616](https://github.com/wasmerio/wasmer/issues/616) Create the import object separately from instance instantiation in `runtime-c-api` +- [#620](https://github.com/wasmerio/wasmer/issues/620) Replace one `throw()` with `noexcept` in llvm backend +- [#618](https://github.com/wasmerio/wasmer/issues/618) Implement `InternalEvent::Breakpoint` in the llvm backend to allow metering in llvm +- [#615](https://github.com/wasmerio/wasmer/issues/615) Eliminate `FunctionEnvironment` construction in `feed_event()` speeding up to 70% of compilation in clif +- [#609](https://github.com/wasmerio/wasmer/issues/609) Update dependencies +- [#602](https://github.com/wasmerio/wasmer/issues/602) C api extract instance context from instance +- [#590](https://github.com/wasmerio/wasmer/issues/590) Error visibility changes in wasmer-c-api +- [#589](https://github.com/wasmerio/wasmer/issues/589) Make `wasmer_byte_array` fields `public` in wasmer-c-api + +## 0.6.0 - 2019-07-31 +- [#603](https://github.com/wasmerio/wasmer/pull/603) Update Wapm-cli, bump version numbers +- [#595](https://github.com/wasmerio/wasmer/pull/595) Add unstable public API for interfacing with the WASI file system in plugin-like usecases +- [#598](https://github.com/wasmerio/wasmer/pull/598) LLVM Backend is now supported in Windows +- [#599](https://github.com/wasmerio/wasmer/pull/599) Fix llvm backend failures in fat spec tests and simd_binaryen spec test. +- [#579](https://github.com/wasmerio/wasmer/pull/579) Fix bug in caching with LLVM and Singlepass backends. + Add `default-backend-singlepass`, `default-backend-llvm`, and `default-backend-cranelift` features to `wasmer-runtime` + to control the `default_compiler()` function (this is a breaking change). Add `compiler_for_backend` function in `wasmer-runtime` +- [#561](https://github.com/wasmerio/wasmer/pull/561) Call the `data_finalizer` field on the `Ctx` +- [#576](https://github.com/wasmerio/wasmer/pull/576) fix `Drop` of uninit `Ctx` +- [#542](https://github.com/wasmerio/wasmer/pull/542) Add SIMD support to Wasmer (LLVM backend only) + - Updates LLVM to version 8.0 + +## 0.5.7 - 2019-07-23 +- [#575](https://github.com/wasmerio/wasmer/pull/575) Prepare for release; update wapm to 0.3.6 +- [#555](https://github.com/wasmerio/wasmer/pull/555) WASI filesystem rewrite. Major improvements + - adds virtual root showing all preopened directories + - improved sandboxing and code-reuse + - symlinks work in a lot more situations + - many misc. improvements to most syscalls touching the filesystem + +## 0.5.6 - 2019-07-16 +- [#565](https://github.com/wasmerio/wasmer/pull/565) Update wapm and bump version to 0.5.6 +- [#563](https://github.com/wasmerio/wasmer/pull/563) Improve wasi testing infrastructure + - fixes arg parsing from comments & fixes the mapdir test to have the native code doing the same thing as the WASI code + - makes wasitests-generate output stdout/stderr by default & adds function to print stdout and stderr for a command if it fails + - compiles wasm with size optimizations & strips generated wasm with wasm-strip +- [#554](https://github.com/wasmerio/wasmer/pull/554) Finish implementation of `wasi::fd_seek`, fix bug in filestat +- [#550](https://github.com/wasmerio/wasmer/pull/550) Fix singlepass compilation error with `imul` instruction + + +## 0.5.5 - 2019-07-10 +- [#541](https://github.com/wasmerio/wasmer/pull/541) Fix dependency graph by making separate test crates; ABI implementations should not depend on compilers. Add Cranelift fork as git submodule of clif-backend +- [#537](https://github.com/wasmerio/wasmer/pull/537) Add hidden flag (`--cache-key`) to use prehashed key into the compiled wasm cache and change compiler backend-specific caching to use directories +- [#536](https://github.com/wasmerio/wasmer/pull/536) ~Update cache to use compiler backend name in cache key~ + +## 0.5.4 - 2019-07-06 +- [#529](https://github.com/wasmerio/wasmer/pull/529) Updates the Wasm Interface library, which is used by wapm, with bug fixes and error message improvements + +## 0.5.3 - 2019-07-03 +- [#523](https://github.com/wasmerio/wasmer/pull/523) Update wapm version to fix bug related to signed packages in the global namespace and locally-stored public keys + +## 0.5.2 - 2019-07-02 +- [#516](https://github.com/wasmerio/wasmer/pull/516) Add workaround for singlepass miscompilation on GetLocal +- [#521](https://github.com/wasmerio/wasmer/pull/521) Update Wapm-cli, bump version numbers +- [#518](https://github.com/wasmerio/wasmer/pull/518) Update Cranelift and WasmParser +- [#514](https://github.com/wasmerio/wasmer/pull/514) [#519](https://github.com/wasmerio/wasmer/pull/519) Improved Emscripten network related calls, added a null check to `WasmPtr` +- [#515](https://github.com/wasmerio/wasmer/pull/515) Improved Emscripten dyncalls +- [#513](https://github.com/wasmerio/wasmer/pull/513) Fix emscripten lseek implementation. +- [#510](https://github.com/wasmerio/wasmer/pull/510) Simplify construction of floating point constants in LLVM backend. Fix LLVM assertion failure due to definition of %ctx. + +## 0.5.1 - 2019-06-24 +- [#508](https://github.com/wasmerio/wasmer/pull/508) Update wapm version, includes bug fixes + +## 0.5.0 - 2019-06-17 + +- [#471](https://github.com/wasmerio/wasmer/pull/471) Added missing functions to run Python. Improved Emscripten bindings +- [#494](https://github.com/wasmerio/wasmer/pull/494) Remove deprecated type aliases from libc in the runtime C API +- [#493](https://github.com/wasmerio/wasmer/pull/493) `wasmer_module_instantiate` has better error messages in the runtime C API +- [#474](https://github.com/wasmerio/wasmer/pull/474) Set the install name of the dylib to `@rpath` +- [#490](https://github.com/wasmerio/wasmer/pull/490) Add MiddlewareChain and StreamingCompiler to runtime +- [#487](https://github.com/wasmerio/wasmer/pull/487) Fix stack offset check in singlepass backend +- [#450](https://github.com/wasmerio/wasmer/pull/450) Added Metering +- [#481](https://github.com/wasmerio/wasmer/pull/481) Added context trampoline into runtime +- [#484](https://github.com/wasmerio/wasmer/pull/484) Fix bugs in emscripten socket syscalls +- [#476](https://github.com/wasmerio/wasmer/pull/476) Fix bug with wasi::environ_get, fix off by one error in wasi::environ_sizes_get +- [#470](https://github.com/wasmerio/wasmer/pull/470) Add mapdir support to Emscripten, implement getdents for Unix +- [#467](https://github.com/wasmerio/wasmer/pull/467) `wasmer_instantiate` returns better error messages in the runtime C API +- [#463](https://github.com/wasmerio/wasmer/pull/463) Fix bug in WASI path_open allowing one level above preopened dir to be accessed +- [#461](https://github.com/wasmerio/wasmer/pull/461) Prevent passing negative lengths in various places in the runtime C API +- [#459](https://github.com/wasmerio/wasmer/pull/459) Add monotonic and real time clocks for wasi on windows +- [#447](https://github.com/wasmerio/wasmer/pull/447) Add trace macro (`--features trace`) for more verbose debug statements +- [#451](https://github.com/wasmerio/wasmer/pull/451) Add `--mapdir=src:dest` flag to rename host directories in the guest context +- [#457](https://github.com/wasmerio/wasmer/pull/457) Implement file metadata for WASI, fix bugs in WASI clock code for Unix platforms + +## 0.4.2 - 2019-05-16 + +- [#416](https://github.com/wasmerio/wasmer/pull/416) Remote code loading framework +- [#449](https://github.com/wasmerio/wasmer/pull/449) Fix bugs: opening host files in filestat and opening with write permissions unconditionally in path_open +- [#442](https://github.com/wasmerio/wasmer/pull/442) Misc. WASI FS fixes and implement readdir +- [#440](https://github.com/wasmerio/wasmer/pull/440) Fix type mismatch between `wasmer_instance_call` and `wasmer_export_func_*_arity` functions in the runtime C API. +- [#269](https://github.com/wasmerio/wasmer/pull/269) Add better runtime docs +- [#432](https://github.com/wasmerio/wasmer/pull/432) Fix returned value of `wasmer_last_error_message` in the runtime C API +- [#429](https://github.com/wasmerio/wasmer/pull/429) Get wasi::path_filestat_get working for some programs; misc. minor WASI FS improvements +- [#413](https://github.com/wasmerio/wasmer/pull/413) Update LLVM backend to use new parser codegen traits + +## 0.4.1 - 2019-05-06 + +- [#426](https://github.com/wasmerio/wasmer/pull/426) Update wapm-cli submodule, bump version to 0.4.1 +- [#422](https://github.com/wasmerio/wasmer/pull/422) Improved Emscripten functions to run optipng and pngquant compiled to wasm +- [#409](https://github.com/wasmerio/wasmer/pull/409) Improved Emscripten functions to run JavascriptCore compiled to wasm +- [#399](https://github.com/wasmerio/wasmer/pull/399) Add example of using a plugin extended from WASI +- [#397](https://github.com/wasmerio/wasmer/pull/397) Fix WASI fs abstraction to work on Windows +- [#390](https://github.com/wasmerio/wasmer/pull/390) Pin released wapm version and add it as a git submodule +- [#408](https://github.com/wasmerio/wasmer/pull/408) Add images to windows installer and update installer to add wapm bin directory to path + +## 0.4.0 - 2019-04-23 + +- [#383](https://github.com/wasmerio/wasmer/pull/383) Hook up wasi exit code to wasmer cli. +- [#382](https://github.com/wasmerio/wasmer/pull/382) Improve error message on `--backend` flag to only suggest currently enabled backends +- [#381](https://github.com/wasmerio/wasmer/pull/381) Allow retrieving propagated user errors. +- [#379](https://github.com/wasmerio/wasmer/pull/379) Fix small return types from imported functions. +- [#371](https://github.com/wasmerio/wasmer/pull/371) Add more Debug impl for WASI types +- [#368](https://github.com/wasmerio/wasmer/pull/368) Fix issue with write buffering +- [#343](https://github.com/wasmerio/wasmer/pull/343) Implement preopened files for WASI and fix aligment issue when accessing WASI memory +- [#367](https://github.com/wasmerio/wasmer/pull/367) Add caching support to the LLVM backend. +- [#366](https://github.com/wasmerio/wasmer/pull/366) Remove `UserTrapper` trait to fix [#365](https://github.com/wasmerio/wasmer/issues/365). +- [#348](https://github.com/wasmerio/wasmer/pull/348) Refactor internal runtime ↔ī¸ backend abstraction. +- [#355](https://github.com/wasmerio/wasmer/pull/355) Misc changes to `Cargo.toml`s for publishing +- [#352](https://github.com/wasmerio/wasmer/pull/352) Bump version numbers to 0.3.0 +- [#351](https://github.com/wasmerio/wasmer/pull/351) Add hidden option to specify wasm program name (can be used to improve error messages) +- [#350](https://github.com/wasmerio/wasmer/pull/350) Enforce that CHANGELOG.md is updated through CI. +- [#349](https://github.com/wasmerio/wasmer/pull/349) Add [CHANGELOG.md](https://github.com/wasmerio/wasmer/blob/master/CHANGELOG.md). + +## 0.3.0 - 2019-04-12 + +- [#276](https://github.com/wasmerio/wasmer/pull/276) [#288](https://github.com/wasmerio/wasmer/pull/288) [#344](https://github.com/wasmerio/wasmer/pull/344) Use new singlepass backend (with the `--backend=singlepass` when running Wasmer) +- [#338](https://github.com/wasmerio/wasmer/pull/338) Actually catch traps/panics/etc when using a typed func. +- [#325](https://github.com/wasmerio/wasmer/pull/325) Fixed func_index in debug mode +- [#323](https://github.com/wasmerio/wasmer/pull/323) Add validate subcommand to validate Wasm files +- [#321](https://github.com/wasmerio/wasmer/pull/321) Upgrade to Cranelift 0.3.0 +- [#319](https://github.com/wasmerio/wasmer/pull/319) Add Export and GlobalDescriptor to Runtime API +- [#310](https://github.com/wasmerio/wasmer/pull/310) Cleanup warnings +- [#299](https://github.com/wasmerio/wasmer/pull/299) [#300](https://github.com/wasmerio/wasmer/pull/300) [#301](https://github.com/wasmerio/wasmer/pull/301) [#303](https://github.com/wasmerio/wasmer/pull/303) [#304](https://github.com/wasmerio/wasmer/pull/304) [#305](https://github.com/wasmerio/wasmer/pull/305) [#306](https://github.com/wasmerio/wasmer/pull/306) [#307](https://github.com/wasmerio/wasmer/pull/307) Add support for WASI 🎉 +- [#286](https://github.com/wasmerio/wasmer/pull/286) Add extend to imports +- [#278](https://github.com/wasmerio/wasmer/pull/278) Add versioning to cache +- [#250](https://github.com/wasmerio/wasmer/pull/250) Setup bors diff --git a/arbitrator/tools/module_roots/wasmer/CODE_OF_CONDUCT.md b/arbitrator/tools/module_roots/wasmer/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..d9965c5 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at contact@wasmer.io. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/arbitrator/tools/module_roots/wasmer/CONTRIBUTING.md b/arbitrator/tools/module_roots/wasmer/CONTRIBUTING.md new file mode 100644 index 0000000..e2b5d10 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/CONTRIBUTING.md @@ -0,0 +1,28 @@ +# How to Contribute to Wasmer + +Thank you for your interest in contributing to Wasmer. This document outlines some recommendations on how to contribute. + +## Issues & Feature Requests + +Please use the issue template and provide a failing example if possible to help us recreate the issue. + +## Pull Requests + +For large changes, please try reaching communicating with the Wasmer maintainers via GitHub Issues or Spectrum Chat to ensure we can accept the change once it is ready. + +We recommend trying the following commands before sending a pull request to ensure code quality: + +- `cargo fmt --all` Ensures all code is correctly formatted. +- Run `cargo test` in the crates that you are modifying. +- Run `cargo build --all`. + +A comprehensive CI test suite will be run by a Wasmer team member after the PR has been created. + +### Common Build Issues + +#### LLVM Dependency + +`Didn't find usable system-wide LLVM` + +Building Wasmer with the LLVM backend requires LLVM 14 or better to be installed +On debian family you need `sudo apt install llvm14 libclang-common-14-dev libpolly-14-dev` diff --git a/arbitrator/tools/module_roots/wasmer/Cargo.lock b/arbitrator/tools/module_roots/wasmer/Cargo.lock new file mode 100644 index 0000000..9910cd9 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/Cargo.lock @@ -0,0 +1,7457 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli 0.28.1", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "any_ascii" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70033777eb8b5124a81a1889416543dddef2de240019b674c81285a2635a7e1e" + +[[package]] +name = "anyhow" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "ascii" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" + +[[package]] +name = "assert_cmd" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c98233c6673d8601ab23e77eb38f999c51100d46c5703b17288c57fddf3a1ffe" +dependencies = [ + "bstr 0.2.17", + "doc-comment", + "predicates 2.1.5", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[package]] +name = "assert_cmd" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8" +dependencies = [ + "anstyle", + "bstr 1.9.1", + "doc-comment", + "predicates 3.1.0", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[package]] +name = "async-compression" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86a9249d1447a85f95810c620abea82e001fe58a31713fcce614caf52499f905" +dependencies = [ + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio 1.37.0", +] + +[[package]] +name = "async-trait" +version = "0.1.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "async-tungstenite" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e9efbe14612da0a19fb983059a0b621e9cf6225d7018ecab4f9988215540dc" +dependencies = [ + "futures-io", + "futures-util", + "log 0.4.21", + "pin-project-lite", + "tungstenite", +] + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object 0.32.2", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake3" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" +dependencies = [ + "arrayref", + "arrayvec 0.7.4", + "cc", + "cfg-if 1.0.0", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata 0.1.10", +] + +[[package]] +name = "bstr" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +dependencies = [ + "memchr", + "regex-automata 0.4.6", + "serde", +] + +[[package]] +name = "build-deps" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f14468960818ce4f3e3553c32d524446687884f8e7af5d3e252331d8a87e43" +dependencies = [ + "glob", +] + +[[package]] +name = "build_const" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" + +[[package]] +name = "bumpalo" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +dependencies = [ + "serde", +] + +[[package]] +name = "bytesize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" +dependencies = [ + "serde", +] + +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.22", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cbindgen" +version = "0.24.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b922faaf31122819ec80c4047cc684c6979a087366c069611e33649bf98e18d" +dependencies = [ + "heck 0.4.1", + "indexmap 1.9.3", + "log 0.4.21", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 1.0.109", + "tempfile", + "toml 0.5.11", +] + +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.4", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half 2.4.0", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clap" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "colored" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f741c91823341bebf717d4c71bda820630ce065443b58bd1b7451af008355" +dependencies = [ + "is-terminal", + "lazy_static", + "winapi 0.3.9", +] + +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + +[[package]] +name = "combine" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" +dependencies = [ + "ascii", + "byteorder", + "either", + "memchr", + "unreachable", +] + +[[package]] +name = "comfy-table" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c64043d6c7b7a4c58e39e7efccfdea7b93d885a795d0c054a69dbbf4dd52686" +dependencies = [ + "crossterm", + "strum", + "strum_macros", + "unicode-width", +] + +[[package]] +name = "compiler-test-derive" +version = "0.0.1" +dependencies = [ + "pretty_assertions", + "proc-macro2", + "quote", + "syn 1.0.109", + "trybuild", +] + +[[package]] +name = "compiletest_rs" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0086d6ad78cf409c3061618cd98e2789d5c9ce598fc9651611cf62eae0a599cb" +dependencies = [ + "diff", + "filetime", + "getopts", + "lazy_static", + "libc", + "log 0.4.21", + "miow 0.3.7", + "regex", + "rustfix", + "serde", + "serde_derive", + "serde_json", + "tester", + "winapi 0.3.9", +] + +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen", +] + +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cooked-waker" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147be55d677052dabc6b22252d5dd0fd4c29c8c27aa4f2fbef0f94aa003b406f" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "corosensei" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80128832c58ea9cbd041d2a759ec449224487b2c1e400453d99d244eead87a8e" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "libc", + "scopeguard", + "windows-sys 0.33.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.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-bforest" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2ab4512dfd3a6f4be184403a195f76e81a8a9f9e6c898e19d2dc3ce20e0115" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98b022ed2a5913a38839dfbafe6cf135342661293b08049843362df4301261dc" +dependencies = [ + "arrayvec 0.7.4", + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-egraph", + "cranelift-entity", + "cranelift-isle", + "gimli 0.26.2", + "hashbrown 0.12.3", + "log 0.4.21", + "regalloc2", + "smallvec 1.13.2", + "target-lexicon 0.12.14", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "639307b45434ad112a98f8300c0f0ab085cbefcd767efcdef9ef19d4c0756e74" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "278e52e29c53fcf32431ef08406c295699a70306d05a0715c5b1bf50e33a9ab7" + +[[package]] +name = "cranelift-egraph" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624b54323b06e675293939311943ba82d323bb340468ce1889be5da7932c8d73" +dependencies = [ + "cranelift-entity", + "fxhash", + "hashbrown 0.12.3", + "indexmap 1.9.3", + "log 0.4.21", + "smallvec 1.13.2", +] + +[[package]] +name = "cranelift-entity" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a59bcbca89c3f1b70b93ab3cbba5e5e0cbf3e63dadb23c7525cb142e21a9d4c" + +[[package]] +name = "cranelift-frontend" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d70abacb8cfef3dc8ff7e8836e9c1d70f7967dfdac824a4cd5e30223415aca6" +dependencies = [ + "cranelift-codegen", + "hashbrown 0.12.3", + "log 0.4.21", + "smallvec 1.13.2", + "target-lexicon 0.12.14", +] + +[[package]] +name = "cranelift-isle" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "393bc73c451830ff8dbb3a07f61843d6cb41a084f9996319917c0b291ed785bb" + +[[package]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +dependencies = [ + "build_const", +] + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools 0.10.5", + "num-traits", + "once_cell", + "oorandom", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + +[[package]] +name = "crossbeam-channel" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +dependencies = [ + "crossbeam-utils 0.8.19", +] + +[[package]] +name = "crossbeam-deque" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed" +dependencies = [ + "crossbeam-epoch 0.8.2", + "crossbeam-utils 0.7.2", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch 0.9.18", + "crossbeam-utils 0.8.19", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +dependencies = [ + "autocfg", + "cfg-if 0.1.10", + "crossbeam-utils 0.7.2", + "lazy_static", + "maybe-uninit", + "memoffset 0.5.6", + "scopeguard", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils 0.8.19", +] + +[[package]] +name = "crossbeam-queue" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" +dependencies = [ + "cfg-if 0.1.10", + "crossbeam-utils 0.7.2", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils 0.8.19", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if 0.1.10", + "lazy_static", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags 2.5.0", + "crossterm_winapi", + "libc", + "parking_lot 0.12.1", + "winapi 0.3.9", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctor" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cynic" +version = "3.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7335114540697c7b1c1a0131cbe0e983fdb1e646f881234afe9e2a66133ac99a" +dependencies = [ + "cynic-proc-macros", + "ref-cast", + "reqwest", + "serde", + "serde_json", + "static_assertions", + "thiserror", +] + +[[package]] +name = "cynic-codegen" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac40efa3e97024222fa95d4c85ce093e2337ed5cdf7279374777132b419f50c" +dependencies = [ + "counter", + "darling 0.20.8", + "graphql-parser", + "once_cell", + "ouroboros", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.58", + "thiserror", +] + +[[package]] +name = "cynic-proc-macros" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4890059f47b6864d53231fd7d336893fc4a28bc1fff62c3ad4966a82994b84" +dependencies = [ + "cynic-codegen", + "darling 0.20.8", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + +[[package]] +name = "darling" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +dependencies = [ + "darling_core 0.20.8", + "darling_macro 0.20.8", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_core" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.58", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core 0.14.4", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +dependencies = [ + "darling_core 0.20.8", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if 1.0.0", + "hashbrown 0.14.3", + "lock_api 0.4.11", + "once_cell", + "parking_lot_core 0.9.9", +] + +[[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "derive_builder" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" +dependencies = [ + "darling 0.14.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_macro" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" +dependencies = [ + "derive_builder_core", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version 0.4.0", + "syn 1.0.109", +] + +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console", + "shell-words", + "tempfile", + "thiserror", + "zeroize", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + +[[package]] +name = "distance" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9d8664cf849d7d0f3114a3a387d2f5e4303176d746d5a951aaddc66dfe9240" + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "dynasm" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dynasmrt" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" +dependencies = [ + "byteorder", + "dynasm", + "memmap2 0.5.10", +] + +[[package]] +name = "edge-schema" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183ddfb52c2441be9d8c3c870632135980ba98e0c4f688da11bcbebb4e26f128" +dependencies = [ + "anyhow", + "bytesize", + "once_cell", + "parking_lot 0.12.1", + "rand_chacha", + "rand_core", + "schemars", + "serde", + "serde_json", + "serde_path_to_error", + "serde_yaml 0.8.26", + "sparx", + "time 0.3.34", + "url", + "uuid", + "wcgi-host", +] + +[[package]] +name = "edge-schema" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0966f1fd49610cc67a835124e6fb4d00a36104e1aa34383c5ef5a265ca00ea2a" +dependencies = [ + "anyhow", + "bytesize", + "once_cell", + "parking_lot 0.12.1", + "rand_chacha", + "rand_core", + "schemars", + "serde", + "serde_json", + "serde_path_to_error", + "serde_yaml 0.8.26", + "sparx", + "time 0.3.34", + "url", + "uuid", + "wcgi-host", +] + +[[package]] +name = "edge-util" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60d06780ae676369e0a9ac2c75f7d1a6d22e157ef5b10cfeda0ee6a465cf7337" +dependencies = [ + "edge-schema 0.0.3", + "http", + "serde", + "wasmparser 0.121.2", +] + +[[package]] +name = "educe" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "enum-iterator" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "enum-ordinalize" +version = "3.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "enumset" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" +dependencies = [ + "darling 0.20.8", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "erased-serde" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" +dependencies = [ + "serde", +] + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + +[[package]] +name = "fern" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" +dependencies = [ + "colored 1.9.4", + "log 0.4.21", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset 0.9.1", + "rustc_version 0.4.0", +] + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags 1.3.2", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "fuse" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e57070510966bfef93662a81cb8aa2b1c7db0964354fa9921434f04b9e8660" +dependencies = [ + "libc", + "log 0.3.9", + "pkg-config", + "thread-scoped", + "time 0.1.45", +] + +[[package]] +name = "futures" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "ghost" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0e085ded9f1267c32176b40921b9754c474f7dd96f7e808d4a982e48aa1e854" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +dependencies = [ + "fallible-iterator", + "indexmap 1.9.3", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "graphql-introspection-query" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2a4732cf5140bd6c082434494f785a19cfb566ab07d1382c3671f5812fed6d" +dependencies = [ + "serde", +] + +[[package]] +name = "graphql-parser" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ebc8013b4426d5b81a4364c419a95ed0b404af2b82e2457de52d9348f0e474" +dependencies = [ + "combine", + "thiserror", +] + +[[package]] +name = "graphql-ws-client" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0063064d93204da0f0baf4dc0e03bf8a9307f6924a37e604b912a63b08aa7ea" +dependencies = [ + "async-tungstenite", + "futures 0.3.30", + "graphql_client", + "log 0.4.21", + "pin-project", + "serde", + "serde_json", + "thiserror", + "uuid", +] + +[[package]] +name = "graphql_client" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cdf7b487d864c2939b23902291a5041bc4a84418268f25fda1c8d4e15ad8fa" +dependencies = [ + "graphql_query_derive", + "serde", + "serde_json", +] + +[[package]] +name = "graphql_client_codegen" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a40f793251171991c4eb75bd84bc640afa8b68ff6907bc89d3b712a22f700506" +dependencies = [ + "graphql-introspection-query", + "graphql-parser", + "heck 0.4.1", + "lazy_static", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 1.0.109", +] + +[[package]] +name = "graphql_query_derive" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bda454f3d313f909298f626115092d348bc231025699f557b27e248475f48c" +dependencies = [ + "graphql_client_codegen", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "gumdrop" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bc700f989d2f6f0248546222d9b4258f5b02a171a431f8285a81c08142629e3" +dependencies = [ + "gumdrop_derive", +] + +[[package]] +name = "gumdrop_derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729f9bd3449d77e7831a18abfb7ba2f99ee813dfd15b8c2167c9a54ba20aa99d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes 1.6.0", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.2.6", + "slab", + "tokio 1.37.0", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" + +[[package]] +name = "half" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +dependencies = [ + "cfg-if 1.0.0", + "crunchy", +] + +[[package]] +name = "handlebars" +version = "3.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4498fc115fa7d34de968184e473529abb40eeb6be8bc5f7faba3d08c316cb3e3" +dependencies = [ + "log 0.4.21", + "pest", + "pest_derive", + "quick-error", + "serde", + "serde_json", +] + +[[package]] +name = "harsh" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6fce2283849822530a18d7d8eeb1719ac65a27cfb6649c0dc8dfd2d2cc5edfb" + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash 0.8.11", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.3", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version 0.4.0", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes 1.6.0", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes 1.6.0", + "http", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" + +[[package]] +name = "http-serde" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f560b665ad9f1572cfcaf034f7fb84338a7ce945216d64a90fd81f046a3caee" +dependencies = [ + "http", + "serde", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes 1.6.0", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.6", + "tokio 1.37.0", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http", + "hyper", + "rustls 0.21.10", + "tokio 1.37.0", + "tokio-rustls", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes 1.6.0", + "hyper", + "native-tls", + "tokio 1.37.0", + "tokio-native-tls", +] + +[[package]] +name = "hyper-tungstenite" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc7dcb1ab67cd336f468a12491765672e61a3b6b148634dbfe2fe8acd3fe7d9" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio 1.37.0", + "tokio-tungstenite", + "tungstenite", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "indicatif" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +dependencies = [ + "console", + "instant", + "number_prefix", + "portable-atomic", + "unicode-width", +] + +[[package]] +name = "inkwell" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbac11e485159a525867fb7e6aa61981453e6a72f625fde6a4ab3047b0c6dec9" +dependencies = [ + "either", + "inkwell_internals", + "libc", + "llvm-sys", + "once_cell", + "parking_lot 0.12.1", +] + +[[package]] +name = "inkwell_internals" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87d00c17e264ce02be5bc23d7bff959188ec7137beddd06b8b6b05a7c680ea85" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "inline-c" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "340dd3d6102fa919bd20987024a6d84954c36ec691ac1efea37742ee983c8dd5" +dependencies = [ + "assert_cmd 1.0.8", + "cc", + "inline-c-macro", + "lazy_static", + "predicates 2.1.5", + "regex", + "rustc_version 0.3.3", + "target-lexicon 0.11.2", + "tempfile", +] + +[[package]] +name = "inline-c-macro" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f5621ec7adacda881d7c2826c064f5c29c72fd44333f97df61b458a583ae15" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version 0.3.3", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "insta" +version = "1.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eab73f58e59ca6526037208f0e98851159ec1633cf17b6cd2e1f2c3fd5d53cc" +dependencies = [ + "console", + "lazy_static", + "linked-hash-map", + "serde", + "similar", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "interfaces" +version = "0.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6250a98af259a26fd5a4a6081fccea9ac116e4c3178acf4aeb86d32d2b7715" +dependencies = [ + "bitflags 2.5.0", + "cc", + "handlebars", + "lazy_static", + "libc", + "nix 0.26.4", + "serde", + "serde_derive", +] + +[[package]] +name = "inventory" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84344c6e0b90a9e2b6f3f9abe5cc74402684e348df7b32adca28747e0cef091a" +dependencies = [ + "ctor", + "ghost", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "lexical-core" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" +dependencies = [ + "arrayvec 0.5.2", + "bitflags 1.3.2", + "cfg-if 1.0.0", + "ryu", + "static_assertions", +] + +[[package]] +name = "lexical-sort" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c09e4591611e231daf4d4c685a66cb0410cc1e502027a20ae55f2bb9e997207a" +dependencies = [ + "any_ascii", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29f835d03d717946d28b1d1ed632eb6f0e24a299388ee623d0c23118d3e8a7fa" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linked_hash_set" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47186c6da4d81ca383c7c47c1bfc80f4b95f4720514d860a5407aaf4233f9588" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "llvm-sys" +version = "150.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfd60e740af945d99c2446a52e3ab8cdba2f740a40a16c51f6871bdea2abc687" +dependencies = [ + "cc", + "lazy_static", + "libc", + "regex", + "semver 1.0.22", +] + +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" +dependencies = [ + "log 0.4.21", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "lz4_flex" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" +dependencies = [ + "twox-hash", +] + +[[package]] +name = "lzma-rs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aba8ecb0450dfabce4ad72085eed0a75dffe8f21f7ada05638564ea9db2d7fb1" +dependencies = [ + "byteorder", + "crc", +] + +[[package]] +name = "mac_address" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4863ee94f19ed315bf3bc00299338d857d4b5bc856af375cc97d237382ad3856" +dependencies = [ + "nix 0.23.2", + "winapi 0.3.9", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + +[[package]] +name = "macro-wasmer-universal-test" +version = "4.2.8" +dependencies = [ + "proc-macro2", + "proc-quote", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minisign" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4225fad231f4cfb67990de1750bb53f10ff1d5b42b91beb2a49e6ebd36c9ab4a" +dependencies = [ + "getrandom", + "rpassword", + "scrypt", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log 0.4.21", + "miow 0.2.2", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log 0.4.21", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio 0.6.23", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "more-asserts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log 0.4.21", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "net2" +version = "0.2.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "nix" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if 1.0.0", + "libc", + "memoffset 0.6.5", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if 1.0.0", + "libc", + "memoffset 0.7.1", + "pin-utils", +] + +[[package]] +name = "nom" +version = "5.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" +dependencies = [ + "lexical-core", + "memchr", + "version_check", +] + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "normpath" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec60c60a693226186f5d6edf073232bfb6464ed97eb22cf3b01c1e8198fd97f5" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi 0.3.9", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "object" +version = "0.28.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" +dependencies = [ + "memchr", +] + +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "crc32fast", + "hashbrown 0.12.3", + "indexmap 1.9.3", + "memchr", +] + +[[package]] +name = "object" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "flate2", + "memchr", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "flate2", + "memchr", + "ruzstd", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "opener" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c62dcb6174f9cb326eac248f07e955d5d559c272730b6c03e396b443b562788" +dependencies = [ + "bstr 1.9.1", + "normpath", + "winapi 0.3.9", +] + +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.5.0", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ouroboros" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b7be5a8a3462b752f4be3ff2b2bf2f7f1d00834902e46be2a4d68b87b0573c" +dependencies = [ + "aliasable", + "ouroboros_macro", + "static_assertions", +] + +[[package]] +name = "ouroboros_macro" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b645dcde5f119c2c454a92d0dfa271a2a3b205da92e4292a68ead4bdbfde1f33" +dependencies = [ + "heck 0.4.1", + "itertools 0.12.1", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +dependencies = [ + "lock_api 0.3.4", + "parking_lot_core 0.6.3", + "rustc_version 0.2.3", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api 0.4.11", + "parking_lot_core 0.9.9", +] + +[[package]] +name = "parking_lot_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66b810a62be75176a80873726630147a5ca780cd33921e0b5709033e66b0a" +dependencies = [ + "cfg-if 0.1.10", + "cloudabi", + "libc", + "redox_syscall 0.1.57", + "rustc_version 0.2.3", + "smallvec 0.6.14", + "winapi 0.3.9", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.4.1", + "smallvec 1.13.2", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "path-clean" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "pest_meta" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap 2.2.6", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "portable-atomic" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "predicates" +version = "2.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" +dependencies = [ + "difflib", + "float-cmp", + "itertools 0.10.5", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" +dependencies = [ + "anstyle", + "difflib", + "float-cmp", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" + +[[package]] +name = "predicates-tree" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi 0.5.1", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", + "version_check", + "yansi 1.0.1", +] + +[[package]] +name = "proc-quote" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e84ab161de78c915302ca325a19bee6df272800e2ae1a43fe3ef430bab2a100" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "proc-quote-impl", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "proc-quote-impl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb3ec628b063cdbcf316e06a8b8c1a541d28fa6c0a8eacd2bfb2b7f49e88aa0" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pulldown-cmark" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8" +dependencies = [ + "bitflags 1.3.2", + "memchr", + "unicase", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque 0.8.5", + "crossbeam-utils 0.8.19", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "ref-cast" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4846d4c50d1721b1a3bef8af76924eef20d5e723647333798c1b519b3a9473f" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "regalloc2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" +dependencies = [ + "fxhash", + "log 0.4.21", + "slice-group-by", + "smallvec 1.13.2", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "region" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6b6ebd13bc009aef9cd476c1310d49ac354d36e240cf1bd753290f3dc7199a7" +dependencies = [ + "bitflags 1.3.2", + "libc", + "mach2", + "windows-sys 0.52.0", +] + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "replace_with" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a8614ee435691de62bcffcf4a66d91b3594bf1428a5722e79103249a095690" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "async-compression", + "base64 0.21.7", + "bytes 1.6.0", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "hyper-tls", + "ipnet", + "js-sys", + "log 0.4.21", + "mime", + "mime_guess", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls 0.21.10", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio 1.37.0", + "tokio-native-tls", + "tokio-rustls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots 0.25.4", + "winreg", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rkyv" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" +dependencies = [ + "bitvec", + "bytecheck", + "bytes 1.6.0", + "hashbrown 0.12.3", + "indexmap 1.9.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rmp" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723ecff9ad04f4ad92fe1c8ca6c20d2196d9286e9c60727c4cb5511629260e9d" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + +[[package]] +name = "rpassword" +version = "7.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.48.0", +] + +[[package]] +name = "rtoolbox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "rusqlite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a" +dependencies = [ + "bitflags 1.3.2", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec 1.13.2", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.22", +] + +[[package]] +name = "rustfix" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c50b74badcddeb8f7652fa8323ce440b95286f8e4b64ebfd871c609672704e" +dependencies = [ + "anyhow", + "log 0.4.21", + "serde", + "serde_json", +] + +[[package]] +name = "rustix" +version = "0.38.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +dependencies = [ + "log 0.4.21", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + +[[package]] +name = "rustls" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99008d7ad0bbbea527ec27bddbc0e432c5b87d8175178cee68d2eec9c4a1813c" +dependencies = [ + "log 0.4.21", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.2", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-pki-types" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.102.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "rusty_jsc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f8f838ac489c4fec2ae60bcd620e70ca829b75f83d4516f2708fd2a9edafdd0" +dependencies = [ + "rusty_jsc_macros", + "rusty_jsc_sys", +] + +[[package]] +name = "rusty_jsc_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2407097cc2d172aedd2da887e68c1724c96f7828bad033096d0bb911dc4071" +dependencies = [ + "pkg-config", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rusty_jsc_sys" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927443ab7a267331865e31bfe0ffb70216cbbadb8e3b15c09774fae6caa9d8df" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "rusty_pool" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ed36cdb20de66d89a17ea04b8883fc7a386f2cf877aaedca5005583ce4876ff" +dependencies = [ + "crossbeam-channel", + "futures 0.3.30", + "futures-channel", + "futures-executor", + "num_cpus", +] + +[[package]] +name = "ruzstd" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c4eb8a81997cf040a091d1f7e1938aeab6749d3a0dfa73af43cdc32393483d" +dependencies = [ + "byteorder", + "derive_more", + "twox-hash", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "schemars" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "schemars_derive" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "scoped-tls" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "pbkdf2", + "salsa20", + "sha2", +] + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "security-framework" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "self_cell" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser 0.7.0", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser 0.10.2", +] + +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +dependencies = [ + "serde", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_bytes" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half 1.8.3", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap 1.9.3", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.2.6", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "serial_test" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" +dependencies = [ + "dashmap", + "futures 0.3.30", + "lazy_static", + "log 0.4.21", + "parking_lot 0.12.1", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shared-buffer" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6c99835bad52957e7aa241d3975ed17c1e5f8c92026377d117a606f36b84b16" +dependencies = [ + "bytes 1.6.0", + "memmap2 0.6.2", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + +[[package]] +name = "similar" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slice-group-by" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" + +[[package]] +name = "smallvec" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" +dependencies = [ + "maybe-uninit", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "smoltcp" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee34c1e1bfc7e9206cc0fb8030a90129b4e319ab53856249bb27642cab914fb3" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "managed", +] + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "sparx" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2257c28eacecfc38c658124ed239e7ecfc9b89082c0794b0672420b63b84c6" +dependencies = [ + "byteorder", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api 0.4.11", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.58", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tar" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "target-lexicon" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "422045212ea98508ae3d28025bc5aaa2bd4a9cdaecd442a08da2ee620ee9ea95" + +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi 0.3.9", +] + +[[package]] +name = "term_size" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "termios" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" +dependencies = [ + "libc", +] + +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + +[[package]] +name = "test-generator" +version = "0.1.0" +dependencies = [ + "anyhow", + "target-lexicon 0.12.14", +] + +[[package]] +name = "test-log" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b319995299c65d522680decf80f2c108d85b861d81dfe340a10d16cee29d9e6" +dependencies = [ + "test-log-macros", + "tracing-subscriber", +] + +[[package]] +name = "test-log-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8f546451eaa38373f549093fe9fd05e7d2bade739e2ddf834b9968621d60107" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "tester" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e8bf7e0eb2dd7b4228cc1b6821fc5114cd6841ae59f652a85488c016091e5f" +dependencies = [ + "cfg-if 1.0.0", + "getopts", + "libc", + "num_cpus", + "term", +] + +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "thread-scoped" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcbb6aa301e5d3b0b5ef639c9a9c7e2f1c944f177b460c04dc24c69b1fa2bd99" + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi 0.3.9", +] + +[[package]] +name = "time" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tldextract" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec03259a0567ad58eed30812bc3e5eda8030f154abc70317ab57b14f00699ca4" +dependencies = [ + "idna 0.2.3", + "log 0.4.21", + "regex", + "serde_json", + "thiserror", + "url", +] + +[[package]] +name = "tokio" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "mio 0.6.23", + "num_cpus", + "tokio-codec", + "tokio-current-thread", + "tokio-executor", + "tokio-fs", + "tokio-io", + "tokio-reactor", + "tokio-sync", + "tokio-tcp", + "tokio-threadpool", + "tokio-timer", + "tokio-udp", + "tokio-uds", +] + +[[package]] +name = "tokio" +version = "1.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +dependencies = [ + "backtrace", + "bytes 1.6.0", + "libc", + "mio 0.8.11", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.6", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-codec" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "tokio-io", +] + +[[package]] +name = "tokio-core" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87b1395334443abca552f63d4f61d0486f12377c2ba8b368e523f89e828cffd4" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "iovec", + "log 0.4.21", + "mio 0.6.23", + "scoped-tls 0.1.2", + "tokio 0.1.22", + "tokio-executor", + "tokio-io", + "tokio-reactor", + "tokio-timer", +] + +[[package]] +name = "tokio-current-thread" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" +dependencies = [ + "futures 0.1.31", + "tokio-executor", +] + +[[package]] +name = "tokio-executor" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" +dependencies = [ + "crossbeam-utils 0.7.2", + "futures 0.1.31", +] + +[[package]] +name = "tokio-fs" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" +dependencies = [ + "futures 0.1.31", + "tokio-io", + "tokio-threadpool", +] + +[[package]] +name = "tokio-io" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "log 0.4.21", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio 1.37.0", +] + +[[package]] +name = "tokio-reactor" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" +dependencies = [ + "crossbeam-utils 0.7.2", + "futures 0.1.31", + "lazy_static", + "log 0.4.21", + "mio 0.6.23", + "num_cpus", + "parking_lot 0.9.0", + "slab", + "tokio-executor", + "tokio-io", + "tokio-sync", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.10", + "tokio 1.37.0", +] + +[[package]] +name = "tokio-serde" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "911a61637386b789af998ee23f50aa30d5fd7edcec8d6d3dedae5e5815205466" +dependencies = [ + "bincode", + "bytes 1.6.0", + "educe", + "futures-core", + "futures-sink", + "pin-project", + "rmp-serde", + "serde", + "serde_cbor", + "serde_json", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio 1.37.0", + "tokio-util", +] + +[[package]] +name = "tokio-sync" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" +dependencies = [ + "fnv", + "futures 0.1.31", +] + +[[package]] +name = "tokio-tcp" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "iovec", + "mio 0.6.23", + "tokio-io", + "tokio-reactor", +] + +[[package]] +name = "tokio-threadpool" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" +dependencies = [ + "crossbeam-deque 0.7.4", + "crossbeam-queue 0.2.3", + "crossbeam-utils 0.7.2", + "futures 0.1.31", + "lazy_static", + "log 0.4.21", + "num_cpus", + "slab", + "tokio-executor", +] + +[[package]] +name = "tokio-timer" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" +dependencies = [ + "crossbeam-utils 0.7.2", + "futures 0.1.31", + "slab", + "tokio-executor", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +dependencies = [ + "futures-util", + "log 0.4.21", + "rustls 0.21.10", + "rustls-native-certs", + "tokio 1.37.0", + "tokio-rustls", + "tungstenite", + "webpki-roots 0.25.4", +] + +[[package]] +name = "tokio-udp" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "log 0.4.21", + "mio 0.6.23", + "tokio-codec", + "tokio-io", + "tokio-reactor", +] + +[[package]] +name = "tokio-uds" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "iovec", + "libc", + "log 0.4.21", + "mio 0.6.23", + "mio-uds", + "tokio-codec", + "tokio-io", + "tokio-reactor", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes 1.6.0", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio 1.37.0", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + +[[package]] +name = "toml" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.9", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.5", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio 1.37.0", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +dependencies = [ + "bitflags 2.5.0", + "bytes 1.6.0", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log 0.4.21", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log 0.4.21", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec 1.13.2", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "tracing-test" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a2c0ff408fe918a94c428a3f2ad04e4afd5c95bbc08fcf868eff750c15728a4" +dependencies = [ + "lazy_static", + "tracing-core", + "tracing-subscriber", + "tracing-test-macro", +] + +[[package]] +name = "tracing-test-macro" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258bc1c4f8e2e73a977812ab339d503e6feeb92700f6d07a6de4d321522d5c08" +dependencies = [ + "lazy_static", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tracing-wasm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" +dependencies = [ + "tracing", + "tracing-subscriber", + "wasm-bindgen", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "trybuild" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad7eb6319ebadebca3dacf1f85a93bc54b73dd81b9036795f73de7ddfe27d5a" +dependencies = [ + "glob", + "once_cell", + "serde", + "serde_derive", + "serde_json", + "termcolor", + "toml 0.8.12", +] + +[[package]] +name = "tun-tap" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a477a4e9367c72ac875d23cd07ac99ffa932497d8428767fed0cfa27bbabe50" +dependencies = [ + "cc", + "futures 0.1.31", + "libc", + "mio 0.6.23", + "tokio-core", +] + +[[package]] +name = "tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +dependencies = [ + "byteorder", + "bytes 1.6.0", + "data-encoding", + "http", + "httparse", + "log 0.4.21", + "rand", + "rustls 0.21.10", + "sha1", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if 1.0.0", + "static_assertions", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "typetag" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4080564c5b2241b5bff53ab610082234e0c57b0417f4bd10596f183001505b8a" +dependencies = [ + "erased-serde", + "inventory", + "once_cell", + "serde", + "typetag-impl", +] + +[[package]] +name = "typetag-impl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e60147782cc30833c05fba3bab1d9b5771b2685a2557672ac96fa5d154099c0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "unix_mode" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55eedc365f81a3c32aea49baf23fa965e3cd85bcc28fb8045708c7388d124ef" + +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +dependencies = [ + "void", +] + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f214ce18d8b2cbe84ed3aa6486ed3f5b285cf8d8fbdbce9f3f767a724adc35" +dependencies = [ + "base64 0.21.7", + "flate2", + "log 0.4.21", + "once_cell", + "rustls 0.22.3", + "rustls-pki-types", + "rustls-webpki 0.102.2", + "url", + "webpki-roots 0.26.1", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna 0.5.0", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +dependencies = [ + "getrandom", + "serde", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "virtual-fs" +version = "0.11.2" +dependencies = [ + "anyhow", + "async-trait", + "bytes 1.6.0", + "derivative", + "filetime", + "fs_extra", + "futures 0.3.30", + "getrandom", + "indexmap 1.9.3", + "lazy_static", + "libc", + "pin-project-lite", + "pretty_assertions", + "replace_with", + "serde", + "shared-buffer", + "slab", + "tempfile", + "thiserror", + "tokio 1.37.0", + "tracing", + "tracing-test", + "typetag", + "webc", +] + +[[package]] +name = "virtual-mio" +version = "0.3.1" +dependencies = [ + "async-trait", + "bytes 1.6.0", + "derivative", + "futures 0.3.30", + "mio 0.8.11", + "serde", + "socket2 0.4.10", + "thiserror", + "tracing", +] + +[[package]] +name = "virtual-net" +version = "0.6.3" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.21.7", + "bincode", + "bytecheck", + "bytes 1.6.0", + "derivative", + "futures-util", + "hyper", + "hyper-tungstenite", + "libc", + "mio 0.8.11", + "pin-project-lite", + "rkyv", + "serde", + "serial_test", + "smoltcp", + "socket2 0.4.10", + "thiserror", + "tokio 1.37.0", + "tokio-serde", + "tokio-tungstenite", + "tokio-util", + "tracing", + "tracing-test", + "virtual-mio", +] + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "wai-bindgen-gen-core" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa3dc41b510811122b3088197234c27e08fcad63ef936306dd8e11e2803876c" +dependencies = [ + "anyhow", + "wai-parser", +] + +[[package]] +name = "wai-bindgen-gen-rust" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19bc05e8380515c4337c40ef03b2ff233e391315b178a320de8640703d522efe" +dependencies = [ + "heck 0.3.3", + "wai-bindgen-gen-core", +] + +[[package]] +name = "wai-bindgen-gen-rust-wasm" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f35ce5e74086fac87f3a7bd50f643f00fe3559adb75c88521ecaa01c8a6199" +dependencies = [ + "heck 0.3.3", + "wai-bindgen-gen-core", + "wai-bindgen-gen-rust", +] + +[[package]] +name = "wai-bindgen-gen-wasmer" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f61484185d8c520a86d5a7f7f8265f446617c2f9774b2e20a52de19b6e53432" +dependencies = [ + "heck 0.3.3", + "wai-bindgen-gen-core", + "wai-bindgen-gen-rust", +] + +[[package]] +name = "wai-bindgen-rust" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e5601c6f448c063e83a5e931b8fefcdf7e01ada424ad42372c948d2e3d67741" +dependencies = [ + "bitflags 1.3.2", + "wai-bindgen-rust-impl", +] + +[[package]] +name = "wai-bindgen-rust-impl" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdeeb5c1170246de8425a3e123e7ef260dc05ba2b522a1d369fe2315376efea4" +dependencies = [ + "proc-macro2", + "syn 1.0.109", + "wai-bindgen-gen-core", + "wai-bindgen-gen-rust-wasm", +] + +[[package]] +name = "wai-bindgen-wasmer" +version = "0.18.3" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 1.3.2", + "once_cell", + "thiserror", + "tracing", + "wai-bindgen-wasmer-impl", + "wasmer", +] + +[[package]] +name = "wai-bindgen-wasmer-impl" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b3488ed88d4dd0e3bf85bad4e27dac6cb31aae5d122a5dda2424803c8dc863a" +dependencies = [ + "proc-macro2", + "syn 1.0.109", + "wai-bindgen-gen-core", + "wai-bindgen-gen-wasmer", +] + +[[package]] +name = "wai-parser" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd0acb6d70885ea0c343749019ba74f015f64a9d30542e66db69b49b7e28186" +dependencies = [ + "anyhow", + "id-arena", + "pulldown-cmark", + "unicode-normalization", + "unicode-xid", +] + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "waker-fn" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi-test-generator" +version = "4.2.8" +dependencies = [ + "glob", + "gumdrop", + "serde", + "serde_json", + "tempfile", + "wast 24.0.0", +] + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log 0.4.21", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.58", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls 1.0.1", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "wasm-coredump-builder" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ca262b320e4530a60946ba16a1cbf987d3f7d4aa6a953bfcc96e179e3e7458" +dependencies = [ + "wasm-coredump-encoder", + "wasm-coredump-types", + "wasm-encoder 0.23.0", +] + +[[package]] +name = "wasm-coredump-encoder" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f36ccfe604720ce093fce7d7b0d609c086c646ec4bb9bba58cb9f4dc2c5623" +dependencies = [ + "leb128", + "wasm-coredump-types", +] + +[[package]] +name = "wasm-coredump-types" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2763d9807903c461b41275a13489396d04695d7bc365743b8ea430cfd72f336" + +[[package]] +name = "wasm-encoder" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b4949d4f2b25a4b208317dcf86aacef9e7a5884e48dfc45d4aeb91808d6f86" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c3e4bc09095436c8e7584d86d33e6c3ee67045af8fb262cbb9cc321de553428" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-smith" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7e95fdeed16adeffed44efdc7ccf27d4f57ff2e99de417c75bcee7dee09049b" +dependencies = [ + "arbitrary", + "indexmap 1.9.3", + "leb128", + "wasm-encoder 0.4.1", +] + +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmer" +version = "4.2.8" +dependencies = [ + "anyhow", + "bytes 1.6.0", + "cfg-if 1.0.0", + "derivative", + "hashbrown 0.11.2", + "indexmap 1.9.3", + "js-sys", + "macro-wasmer-universal-test", + "more-asserts", + "rustc-demangle", + "rusty_jsc", + "serde", + "serde-wasm-bindgen", + "shared-buffer", + "target-lexicon 0.12.14", + "tempfile", + "thiserror", + "tracing", + "wasm-bindgen", + "wasm-bindgen-test", + "wasmer-compiler", + "wasmer-compiler-cranelift", + "wasmer-compiler-llvm", + "wasmer-compiler-singlepass", + "wasmer-derive", + "wasmer-types", + "wasmer-vm", + "wasmparser 0.121.2", + "wat", + "winapi 0.3.9", +] + +[[package]] +name = "wasmer-api" +version = "0.0.25" +dependencies = [ + "anyhow", + "base64 0.13.1", + "cynic", + "edge-schema 0.1.0", + "futures 0.3.30", + "harsh", + "pin-project-lite", + "reqwest", + "serde", + "serde_json", + "serde_path_to_error", + "time 0.3.34", + "tokio 1.37.0", + "tracing", + "url", + "uuid", + "webc", +] + +[[package]] +name = "wasmer-argus" +version = "4.2.8" +dependencies = [ + "anyhow", + "async-trait", + "clap", + "cynic", + "derive_more", + "futures 0.3.30", + "indicatif", + "log 0.4.21", + "reqwest", + "serde", + "serde_json", + "tokio 1.37.0", + "tracing", + "tracing-subscriber", + "url", + "wasmer", + "wasmer-api", + "webc", +] + +[[package]] +name = "wasmer-bin-fuzz" +version = "0.0.0" +dependencies = [ + "anyhow", + "libfuzzer-sys", + "wasm-smith", + "wasmer", + "wasmer-compiler", + "wasmer-compiler-cranelift", + "wasmer-compiler-llvm", + "wasmer-compiler-singlepass", + "wasmer-middlewares", + "wasmprinter", +] + +[[package]] +name = "wasmer-c-api" +version = "4.2.8" +dependencies = [ + "cbindgen", + "cfg-if 1.0.0", + "enumset", + "field-offset", + "inline-c", + "lazy_static", + "libc", + "paste", + "thiserror", + "tokio 1.37.0", + "typetag", + "virtual-fs", + "wasmer", + "wasmer-compiler", + "wasmer-compiler-cranelift", + "wasmer-compiler-llvm", + "wasmer-compiler-singlepass", + "wasmer-emscripten", + "wasmer-inline-c", + "wasmer-middlewares", + "wasmer-types", + "wasmer-wasix", + "webc", +] + +[[package]] +name = "wasmer-c-api-test-runner" +version = "4.2.8" +dependencies = [ + "cc", + "regex", + "target-lexicon 0.11.2", + "walkdir", +] + +[[package]] +name = "wasmer-cache" +version = "4.2.8" +dependencies = [ + "blake3", + "clap", + "clap_builder", + "clap_derive", + "clap_lex", + "criterion", + "hex", + "rand", + "tempfile", + "thiserror", + "wasmer", + "wasmer-compiler-singlepass", +] + +[[package]] +name = "wasmer-capi-examples-runner" +version = "4.2.8" +dependencies = [ + "cc", + "regex", + "target-lexicon 0.11.2", + "walkdir", +] + +[[package]] +name = "wasmer-cli" +version = "4.2.8" +dependencies = [ + "anyhow", + "assert_cmd 2.0.14", + "async-trait", + "bytes 1.6.0", + "bytesize", + "cargo_metadata", + "cfg-if 1.0.0", + "chrono", + "clap", + "colored 2.1.0", + "comfy-table", + "dialoguer", + "dirs", + "edge-schema 0.1.0", + "edge-util", + "flate2", + "fuse", + "futures 0.3.30", + "futures-util", + "hex", + "http", + "humantime", + "hyper", + "indexmap 1.9.3", + "indicatif", + "interfaces", + "is-terminal", + "libc", + "log 0.4.21", + "mac_address", + "mio 0.8.11", + "normpath", + "object 0.32.2", + "once_cell", + "opener", + "parking_lot 0.12.1", + "pathdiff", + "predicates 3.1.0", + "pretty_assertions", + "regex", + "reqwest", + "rkyv", + "semver 1.0.22", + "serde", + "serde_json", + "serde_yaml 0.8.26", + "sha2", + "shared-buffer", + "tar", + "target-lexicon 0.12.14", + "tempfile", + "thiserror", + "time 0.1.45", + "time 0.3.34", + "tldextract", + "tokio 1.37.0", + "tokio-tungstenite", + "toml 0.5.11", + "tracing", + "tracing-subscriber", + "tun-tap", + "unix_mode", + "url", + "uuid", + "virtual-fs", + "virtual-mio", + "virtual-net", + "walkdir", + "wasm-coredump-builder", + "wasmer", + "wasmer-api", + "wasmer-compiler", + "wasmer-compiler-cranelift", + "wasmer-compiler-llvm", + "wasmer-compiler-singlepass", + "wasmer-emscripten", + "wasmer-object", + "wasmer-registry", + "wasmer-toml", + "wasmer-types", + "wasmer-vm", + "wasmer-wasix", + "wasmer-wast", + "webc", +] + +[[package]] +name = "wasmer-compiler" +version = "4.2.8" +dependencies = [ + "backtrace", + "bytes 1.6.0", + "cfg-if 1.0.0", + "enum-iterator", + "enumset", + "hashbrown 0.11.2", + "lazy_static", + "leb128", + "memmap2 0.5.10", + "more-asserts", + "region", + "rkyv", + "self_cell", + "serde", + "serde_bytes", + "shared-buffer", + "smallvec 1.13.2", + "thiserror", + "wasmer-object", + "wasmer-types", + "wasmer-vm", + "wasmparser 0.121.2", + "winapi 0.3.9", +] + +[[package]] +name = "wasmer-compiler-cli" +version = "4.2.8" +dependencies = [ + "anyhow", + "bytesize", + "cfg-if 1.0.0", + "clap", + "colored 2.1.0", + "distance", + "fern", + "is-terminal", + "log 0.4.21", + "target-lexicon 0.12.14", + "unix_mode", + "wasmer-compiler", + "wasmer-compiler-cranelift", + "wasmer-compiler-singlepass", + "wasmer-types", +] + +[[package]] +name = "wasmer-compiler-cranelift" +version = "4.2.8" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "gimli 0.26.2", + "hashbrown 0.11.2", + "lazy_static", + "more-asserts", + "rayon", + "smallvec 1.13.2", + "target-lexicon 0.12.14", + "tracing", + "wasmer-compiler", + "wasmer-types", +] + +[[package]] +name = "wasmer-compiler-llvm" +version = "4.2.8" +dependencies = [ + "byteorder", + "cc", + "inkwell", + "itertools 0.10.5", + "lazy_static", + "libc", + "object 0.28.4", + "rayon", + "regex", + "rustc_version 0.4.0", + "semver 1.0.22", + "smallvec 1.13.2", + "target-lexicon 0.12.14", + "wasmer-compiler", + "wasmer-types", + "wasmer-vm", +] + +[[package]] +name = "wasmer-compiler-singlepass" +version = "4.2.8" +dependencies = [ + "byteorder", + "dynasm", + "dynasmrt", + "enumset", + "gimli 0.26.2", + "hashbrown 0.11.2", + "lazy_static", + "more-asserts", + "rayon", + "smallvec 1.13.2", + "target-lexicon 0.12.14", + "wasmer-compiler", + "wasmer-types", +] + +[[package]] +name = "wasmer-derive" +version = "4.2.8" +dependencies = [ + "compiletest_rs", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", + "wasmer-types", +] + +[[package]] +name = "wasmer-emscripten" +version = "4.2.8" +dependencies = [ + "byteorder", + "getrandom", + "lazy_static", + "libc", + "log 0.4.21", + "time 0.3.34", + "wasmer", + "wasmer-types", +] + +[[package]] +name = "wasmer-inline-c" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c4e7a2a3363ceeb2ee60371af9460748f2bf53569b58627f1f640284ab07778" +dependencies = [ + "assert_cmd 1.0.8", + "cc", + "lazy_static", + "predicates 2.1.5", + "regex", + "rustc_version 0.3.3", + "target-lexicon 0.11.2", + "tempfile", + "wasmer-inline-c-macro", +] + +[[package]] +name = "wasmer-inline-c-macro" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058581df4116836c51906e346d6f49ad1b1de0129243fba6b1e1d3c206cd36d1" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version 0.3.3", +] + +[[package]] +name = "wasmer-integration-tests-cli" +version = "4.2.8" +dependencies = [ + "anyhow", + "assert_cmd 2.0.14", + "derivative", + "dirs", + "flate2", + "futures 0.3.30", + "hex", + "insta", + "libc", + "md5", + "object 0.30.4", + "once_cell", + "predicates 2.1.5", + "pretty_assertions", + "rand", + "regex", + "reqwest", + "serde", + "tar", + "target-lexicon 0.12.14", + "tempfile", + "tokio 1.37.0", + "wasmer-registry", +] + +[[package]] +name = "wasmer-integration-tests-ios" +version = "4.2.8" + +[[package]] +name = "wasmer-journal" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.21.7", + "bincode", + "bytecheck", + "bytes 1.6.0", + "derivative", + "lz4_flex", + "num_enum", + "rkyv", + "serde", + "serde_json", + "shared-buffer", + "tempfile", + "thiserror", + "tracing", + "tracing-test", + "virtual-fs", + "virtual-net", + "wasmer", + "wasmer-wasix-types", +] + +[[package]] +name = "wasmer-middlewares" +version = "4.2.8" +dependencies = [ + "wasmer", + "wasmer-types", + "wasmer-vm", +] + +[[package]] +name = "wasmer-object" +version = "4.2.8" +dependencies = [ + "object 0.29.0", + "thiserror", + "wasmer-types", +] + +[[package]] +name = "wasmer-registry" +version = "5.10.4" +dependencies = [ + "anyhow", + "clap", + "console", + "dialoguer", + "dirs", + "filetime", + "flate2", + "futures 0.3.30", + "futures-util", + "graphql-ws-client", + "graphql_client", + "hex", + "indexmap 1.9.3", + "indicatif", + "lazy_static", + "log 0.4.21", + "lzma-rs", + "minisign", + "pretty_assertions", + "regex", + "reqwest", + "rpassword", + "rusqlite", + "semver 1.0.22", + "serde", + "serde_json", + "tar", + "tempfile", + "thiserror", + "time 0.3.34", + "tldextract", + "tokio 1.37.0", + "tokio-tungstenite", + "toml 0.5.11", + "tracing", + "url", + "wasmer-toml", + "wasmer-wasm-interface", + "wasmparser 0.121.2", + "whoami", +] + +[[package]] +name = "wasmer-sys-utils" +version = "0.18.3" +dependencies = [ + "libc", + "region", + "tracing", + "tracing-subscriber", + "wasmer", + "wasmer-types", + "wasmer-vm", + "wasmer-wasix", +] + +[[package]] +name = "wasmer-toml" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d21472954ee9443235ca32522b17fc8f0fe58e2174556266a0d9766db055cc52" +dependencies = [ + "anyhow", + "derive_builder", + "indexmap 2.2.6", + "semver 1.0.22", + "serde", + "serde_cbor", + "serde_json", + "serde_yaml 0.9.34+deprecated", + "thiserror", + "toml 0.8.12", +] + +[[package]] +name = "wasmer-types" +version = "4.2.8" +dependencies = [ + "bytecheck", + "enum-iterator", + "enumset", + "indexmap 1.9.3", + "memoffset 0.9.1", + "more-asserts", + "rkyv", + "serde", + "serde_bytes", + "target-lexicon 0.12.14", + "thiserror", +] + +[[package]] +name = "wasmer-vm" +version = "4.2.8" +dependencies = [ + "backtrace", + "cc", + "cfg-if 1.0.0", + "corosensei", + "crossbeam-queue 0.3.11", + "dashmap", + "derivative", + "enum-iterator", + "fnv", + "indexmap 1.9.3", + "lazy_static", + "libc", + "mach", + "memoffset 0.9.1", + "more-asserts", + "region", + "scopeguard", + "serde", + "thiserror", + "tracing", + "wasmer-types", + "winapi 0.3.9", +] + +[[package]] +name = "wasmer-wasix" +version = "0.18.3" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.21.7", + "bincode", + "blake3", + "bytecheck", + "bytes 1.6.0", + "cfg-if 1.0.0", + "chrono", + "cooked-waker", + "dashmap", + "derivative", + "futures 0.3.30", + "getrandom", + "heapless", + "hex", + "http", + "hyper", + "js-sys", + "lazy_static", + "libc", + "linked_hash_set", + "lz4_flex", + "num_enum", + "once_cell", + "petgraph", + "pin-project", + "pretty_assertions", + "rand", + "rayon", + "reqwest", + "rkyv", + "rusty_pool", + "semver 1.0.22", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "serde_yaml 0.9.34+deprecated", + "sha2", + "shared-buffer", + "tempfile", + "term_size", + "termios", + "thiserror", + "tokio 1.37.0", + "tokio-stream", + "tower", + "tower-http", + "tracing", + "tracing-subscriber", + "tracing-test", + "tracing-wasm", + "typetag", + "url", + "urlencoding", + "virtual-fs", + "virtual-mio", + "virtual-net", + "waker-fn", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test", + "wasmer", + "wasmer-emscripten", + "wasmer-journal", + "wasmer-types", + "wasmer-wasix-types", + "wcgi", + "wcgi-host", + "web-sys", + "webc", + "weezl", + "winapi 0.3.9", + "xxhash-rust", +] + +[[package]] +name = "wasmer-wasix-types" +version = "0.18.3" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "byteorder", + "cfg-if 1.0.0", + "num_enum", + "pretty_assertions", + "serde", + "time 0.3.34", + "tracing", + "wai-bindgen-gen-core", + "wai-bindgen-gen-rust", + "wai-bindgen-gen-rust-wasm", + "wai-bindgen-rust", + "wai-parser", + "wasmer", + "wasmer-derive", + "wasmer-types", +] + +[[package]] +name = "wasmer-wasm-interface" +version = "4.2.8" +dependencies = [ + "bincode", + "either", + "nom", + "serde", + "wasmparser 0.121.2", + "wat", +] + +[[package]] +name = "wasmer-wast" +version = "4.2.8" +dependencies = [ + "anyhow", + "futures 0.3.30", + "serde", + "tempfile", + "thiserror", + "tokio 1.37.0", + "virtual-fs", + "wasmer", + "wasmer-wasix", + "wast 38.0.1", +] + +[[package]] +name = "wasmer-workspace" +version = "4.2.8" +dependencies = [ + "anyhow", + "build-deps", + "cfg-if 1.0.0", + "clap", + "clap_builder", + "clap_derive", + "clap_lex", + "compiler-test-derive", + "criterion", + "crossbeam-queue 0.3.11", + "glob", + "lazy_static", + "rustc_version 0.4.0", + "serial_test", + "tempfile", + "test-generator", + "test-log", + "tokio 1.37.0", + "tracing", + "tracing-subscriber", + "ureq", + "wasi-test-generator", + "wasmer", + "wasmer-cache", + "wasmer-compiler", + "wasmer-compiler-cranelift", + "wasmer-compiler-llvm", + "wasmer-compiler-singlepass", + "wasmer-emscripten", + "wasmer-middlewares", + "wasmer-types", + "wasmer-wasix", + "wasmer-wast", +] + +[[package]] +name = "wasmparser" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ea896273ea99b15132414be1da01ab0d8836415083298ecaffbe308eaac87a" +dependencies = [ + "indexmap 1.9.3", + "url", +] + +[[package]] +name = "wasmparser" +version = "0.121.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" +dependencies = [ + "bitflags 2.5.0", + "indexmap 2.2.6", + "semver 1.0.22", +] + +[[package]] +name = "wasmprinter" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60e73986a6b7fdfedb7c5bf9e7eb71135486507c8fbc4c0c42cffcb6532988b7" +dependencies = [ + "anyhow", + "wasmparser 0.121.2", +] + +[[package]] +name = "wast" +version = "24.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff1e3bd3ad0b2ee7784add89c30dc96b89a54b43e5d6d95d774eda1863b3500" +dependencies = [ + "leb128", +] + +[[package]] +name = "wast" +version = "38.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae0d7b256bef26c898fa7344a2d627e8499f5a749432ce0a05eae1a64ff0c271" +dependencies = [ + "leb128", +] + +[[package]] +name = "wast" +version = "64.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a259b226fd6910225aa7baeba82f9d9933b6d00f2ce1b49b80fa4214328237cc" +dependencies = [ + "leb128", + "memchr", + "unicode-width", + "wasm-encoder 0.32.0", +] + +[[package]] +name = "wat" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53253d920ab413fca1c7dc2161d601c79b4fdf631d0ba51dd4343bf9b556c3f6" +dependencies = [ + "wast 64.0.0", +] + +[[package]] +name = "wcgi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ca8f334eec3a8197bd25a612c74f415b8691d219ee11f1acd20f15a3e2bf77" +dependencies = [ + "http", + "http-serde", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "wcgi-host" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a762cf2b0ed389a2a2fb591d63a398c1a4c0f8bef938cfd040285a3c63b695cc" +dependencies = [ + "http", + "schemars", + "serde", + "tokio 1.37.0", + "wasmparser 0.95.0", + "wcgi", +] + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webc" +version = "5.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "973ca5a91b4fb3e4bb37cfebe03ef9364d0aff2765256abefdb7e79dc9188483" +dependencies = [ + "anyhow", + "base64 0.21.7", + "byteorder", + "bytes 1.6.0", + "flate2", + "indexmap 1.9.3", + "leb128", + "lexical-sort", + "once_cell", + "path-clean", + "rand", + "semver 1.0.22", + "serde", + "serde_cbor", + "serde_json", + "sha2", + "shared-buffer", + "tar", + "tempfile", + "thiserror", + "toml 0.7.8", + "url", + "walkdir", + "wasmer-toml", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "webpki-roots" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + +[[package]] +name = "whoami" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +dependencies = [ + "redox_syscall 0.4.1", + "wasite", + "web-sys", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-sys" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43dbb096663629518eb1dfa72d80243ca5a6aca764cae62a2df70af760a9be75" +dependencies = [ + "windows_aarch64_msvc 0.33.0", + "windows_i686_gnu 0.33.0", + "windows_i686_msvc 0.33.0", + "windows_x86_64_gnu 0.33.0", + "windows_x86_64_msvc 0.33.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if 1.0.0", + "windows-sys 0.48.0", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "xattr" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +dependencies = [ + "libc", + "linux-raw-sys", + "rustix", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/arbitrator/tools/module_roots/wasmer/Cargo.toml b/arbitrator/tools/module_roots/wasmer/Cargo.toml new file mode 100644 index 0000000..c5edd62 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/Cargo.toml @@ -0,0 +1,330 @@ +[package] +name = "wasmer-workspace" +description = "Wasmer workspace" +publish = false +autoexamples = false +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[dependencies] +wasmer = { version = "=4.2.8", path = "lib/api", default-features = false } +wasmer-compiler = { version = "=4.2.8", path = "lib/compiler", features = [ + "compiler", +], optional = true } +wasmer-compiler-cranelift = { version = "=4.2.8", path = "lib/compiler-cranelift", optional = true } +wasmer-compiler-singlepass = { version = "=4.2.8", path = "lib/compiler-singlepass", optional = true } +wasmer-compiler-llvm = { version = "=4.2.8", path = "lib/compiler-llvm", optional = true } +wasmer-emscripten = { version = "=4.2.8", path = "lib/emscripten", optional = true } +wasmer-wasix = { version = "0.18.3", path = "lib/wasix", optional = true } +wasmer-wast = { version = "=4.2.8", path = "tests/lib/wast", optional = true } +wasi-test-generator = { version = "=4.2.8", path = "tests/wasi-wast", optional = true } +wasmer-cache = { version = "=4.2.8", path = "lib/cache", optional = true } +wasmer-types = { version = "=4.2.8", path = "lib/types" } +wasmer-middlewares = { version = "=4.2.8", path = "lib/middlewares", optional = true } +cfg-if = "1.0" +tokio = { version = "1", features = [ + "rt", + "rt-multi-thread", + "macros", +], optional = true } +crossbeam-queue = "0.3.8" + +[workspace] +members = [ + "fuzz", + "lib/api", + "lib/api/macro-wasmer-universal-test", + "lib/backend-api", + "lib/c-api", + "lib/c-api/examples/wasmer-capi-examples-runner", + "lib/c-api/tests/wasmer-c-api-test-runner", + "lib/cache", + "lib/cli-compiler", + "lib/cli", + "lib/compiler-cranelift", + "lib/compiler-llvm", + "lib/compiler-singlepass", + "lib/compiler", + "lib/derive", + "lib/emscripten", + "lib/object", + "lib/registry", + "lib/sys-utils", + "lib/types", + "lib/virtual-io", + "lib/virtual-fs", + "lib/virtual-net", + "lib/vm", + "lib/wai-bindgen-wasmer", + "lib/wasi-types", + "lib/wasix", + "lib/wasm-interface", + "lib/journal", + "tests/integration/cli", + "tests/integration/ios", + "tests/lib/compiler-test-derive", + "tests/lib/wast", + "tests/wasi-wast", + "tests/wasmer-argus", +] +resolver = "2" + +[workspace.package] +authors = ["Wasmer Engineering Team "] +edition = "2021" +homepage = "https://wasmer.io/" +license = "MIT" +repository = "https://github.com/wasmerio/wasmer" +rust-version = "1.73" +version = "4.2.8" + +[workspace.dependencies] +enumset = "1.1.0" +memoffset = "0.9.0" +wasmer-toml = "0.9.2" +wasmparser = { version = "0.121.0", default-features = false } +webc = { version = "5.8.0", default-features = false, features = ["package"] } +shared-buffer = "0.1.4" +rkyv = { version = "0.7.40", features = ["indexmap", "validation", "strict"] } +memmap2 = { version = "0.6.2" } +edge-schema = { version = "=0.1.0" } + +[build-dependencies] +test-generator = { path = "tests/lib/test-generator" } +build-deps = "0.1.4" +anyhow = "1.0" +glob = "0.3" +rustc_version = "0.4" + +[dev-dependencies] +wasmer = { version = "=4.2.8", path = "lib/api", features = [ + "compiler", + "singlepass", + "sys", +] } +anyhow = "1.0" +criterion = { version = "0.5", default-features = false } +clap = { version = "=4.4.11" } +clap_builder = { version = "=4.4.11" } +clap_derive = { version = "=4.4.7" } +clap_lex = { version = "=0.6.0" } +lazy_static = "1.4" +serial_test = { version = "2.0", default-features = false } +compiler-test-derive = { path = "tests/lib/compiler-test-derive" } +tempfile = "3.6.0" +ureq = "2.6" +# For logging tests using the `RUST_LOG=debug` when testing +test-log = { version = "0.2", default-features = false, features = ["trace"] } +tracing = { version = "0.1", default-features = false, features = ["log"] } +tracing-subscriber = { version = "0.3", default-features = false, features = [ + "env-filter", + "fmt", +] } + +[features] +# Don't add the compiler features in default, please add them on the Makefile +# since we might want to autoconfigure them depending on the availability on the host. +default = ["wat", "wast", "cache", "wasi", "engine", "emscripten", "middlewares"] +# backend means that the `wasmer` crate will be compiled with the `wasmer-compiler` or the `jsc`. +# That means: that is able to execute modules +backend = [] +jsc = ["wasmer/jsc", "backend", "wat", "wasmer/std"] +engine = ["universal"] +universal = [] +cache = ["wasmer-cache"] +wast = ["wasmer-wast"] +wasi = ["wasmer-wasix"] +emscripten = ["wasmer-emscripten"] +wat = ["wasmer/wat"] +compiler = ["wasmer/compiler", "backend", "wasmer-compiler/translator"] +singlepass = ["compiler", "wasmer-compiler-singlepass", "wasmer/singlepass"] +cranelift = ["compiler", "wasmer-compiler-cranelift", "wasmer/cranelift"] +llvm = ["compiler", "wasmer-compiler-llvm", "wasmer/llvm"] +middlewares = ["wasmer-middlewares"] +wasmer-artifact-load = ["wasmer-compiler/wasmer-artifact-load"] +wasmer-artifact-create = ["wasmer-compiler/wasmer-artifact-create"] +static-artifact-load = ["wasmer-compiler/static-artifact-load"] +static-artifact-create = ["wasmer-compiler/static-artifact-create"] + +# Testing features +test-singlepass = ["singlepass"] +test-cranelift = ["cranelift"] +test-llvm = ["llvm"] + +test-universal = ["test-generator/test-universal"] + +# Specifies that we're running in coverage testing mode. This disables tests +# that raise signals because that interferes with tarpaulin. +coverage = [] + +#[profile.release] +#debug = true + +# Enable optimizations for a few crates, even for debug builds. +# This greatly speeds up using debug builds, because these crates are extremely +# slow without optimizations. +[profile.dev.package.cranelift-codegen] +opt-level = 3 +[profile.dev.package.regalloc2] +opt-level = 3 +[profile.dev.package.wasmparser] +opt-level = 3 +[profile.dev.package.rkyv] +opt-level = 3 +[profile.dev.package.wasmer-types] +opt-level = 3 +[profile.dev.package.weezl] +opt-level = 3 +[profile.dev.package.sha2] +opt-level = 3 +[profile.dev.package.digest] +opt-level = 3 + +[[bench]] +name = "static_and_dynamic_functions" +harness = false + +[[example]] +name = "early-exit" +path = "examples/early_exit.rs" +required-features = ["backend"] + +[[example]] +name = "engine" +path = "examples/engine.rs" +required-features = ["cranelift"] + +[[example]] +name = "engine-headless" +path = "examples/engine_headless.rs" +required-features = ["cranelift"] + +[[example]] +name = "platform-headless-ios" +path = "examples/platform_ios_headless.rs" +required-features = ["cranelift"] + +[[example]] +name = "cross-compilation" +path = "examples/engine_cross_compilation.rs" +required-features = ["cranelift"] + +[[example]] +name = "compiler-singlepass" +path = "examples/compiler_singlepass.rs" +required-features = ["singlepass"] + +[[example]] +name = "compiler-cranelift" +path = "examples/compiler_cranelift.rs" +required-features = ["cranelift"] + +[[example]] +name = "compiler-llvm" +path = "examples/compiler_llvm.rs" +required-features = ["llvm"] + +[[example]] +name = "exported-function" +path = "examples/exports_function.rs" +required-features = ["backend"] + +[[example]] +name = "exported-global" +path = "examples/exports_global.rs" +required-features = ["backend"] + +[[example]] +name = "exported-memory" +path = "examples/exports_memory.rs" +required-features = ["backend"] + +[[example]] +name = "imported-function" +path = "examples/imports_function.rs" +required-features = ["backend"] + +[[example]] +name = "imported-global" +path = "examples/imports_global.rs" +required-features = ["backend"] + +[[example]] +name = "tunables-limit-memory" +path = "examples/tunables_limit_memory.rs" +required-features = ["cranelift"] + +[[example]] +name = "wasi" +path = "examples/wasi.rs" +required-features = ["backend", "wasi"] + +[[example]] +name = "wasi-manual-setup" +path = "examples/wasi_manual_setup.rs" +required-features = ["tokio", "backend", "wasi"] + +[[example]] +name = "wasi-pipes" +path = "examples/wasi_pipes.rs" +required-features = ["tokio", "backend", "wasi"] + +[[example]] +name = "table" +path = "examples/table.rs" +required-features = ["backend"] + +[[example]] +name = "memory" +path = "examples/memory.rs" +required-features = ["backend"] + +[[example]] +name = "instance" +path = "examples/instance.rs" +required-features = ["backend"] + +[[example]] +name = "errors" +path = "examples/errors.rs" +required-features = ["backend"] + +[[example]] +name = "imported-function-env" +path = "examples/imports_function_env.rs" +required-features = ["backend"] + +[[example]] +name = "imported-function-env-global" +path = "examples/imports_function_env_global.rs" +required-features = ["backend"] + +[[example]] +name = "hello-world" +path = "examples/hello_world.rs" +required-features = ["backend"] + +[[example]] +name = "metering" +path = "examples/metering.rs" +required-features = ["cranelift"] + +[[example]] +name = "imports-exports" +path = "examples/imports_exports.rs" +required-features = ["backend"] + +[[example]] +name = "features" +path = "examples/features.rs" +required-features = ["cranelift"] + +[[example]] +name = "http-dynamic-size" +path = "examples/http_dynamic_size.rs" +required-features = ["cranelift"] diff --git a/arbitrator/tools/module_roots/wasmer/LICENSE b/arbitrator/tools/module_roots/wasmer/LICENSE new file mode 100644 index 0000000..62bb543 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019-present Wasmer, Inc. and its affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/arbitrator/tools/module_roots/wasmer/Makefile b/arbitrator/tools/module_roots/wasmer/Makefile new file mode 100644 index 0000000..9050822 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/Makefile @@ -0,0 +1,947 @@ +SHELL=/usr/bin/env bash + +##### +# +# The Matrix +# +##### + +# The matrix is the product of the following columns: +# +# |------------|----------|--------------|-------| +# | Compiler ⨯ Platform ⨯ Architecture ⨯ libc | +# |------------|----------|--------------|-------| +# | Cranelift | Linux | amd64 | glibc | +# | LLVM | Darwin | aarch64 | musl | +# | Singlepass | Windows | riscv | | +# |------------|----------|--------------|-------| +# +# Here is what works and what doesn't: +# +# * Cranelift works everywhere, +# +# * LLVM works on Linux+Darwin/`amd64`, +# and linux+`aarch64`, linux+`riscv` +# but it doesn't work on Darwin/`aarch64` or Windows/`aarch64`. +# +# * Singlepass works on Linux+Darwin+Windows/`amd64`, +# and Linux+Darwin/`aarch64` +# it doesn't work on */`riscv`. +# +# * Windows isn't tested on `aarch64`, that's why we consider it's not +# working, but it might possibly be. +# * The Only target for `riscv` familly of processor is the RV64, with the `GC` extensions + + +##### +# +# Define the “Platform” and “Architecture” columns of the matrix. +# +##### + + + +IS_DARWIN := 0 +IS_LINUX := 0 +IS_FREEBSD := 0 +IS_WINDOWS := 0 +IS_AMD64 := 0 +IS_AARCH64 := 0 +IS_RISCV64 := 0 + +# Test Windows apart because it doesn't support `uname -s`. +ifeq ($(OS), Windows_NT) + # We can assume it will likely be in amd64. + IS_AMD64 := 1 + IS_WINDOWS := 1 +else + # Platform + uname := $(shell uname -s) + + ifeq ($(uname), Darwin) + IS_DARWIN := 1 + else ifeq ($(uname), Linux) + IS_LINUX := 1 + else ifeq ($(uname), FreeBSD) + IS_FREEBSD := 1 + else + # We use spaces instead of tabs to indent `$(error)` + # otherwise it's considered as a command outside a + # target and it will fail. + $(error Unrecognized platform, expect `Darwin`, `Linux` or `Windows_NT`) + endif + + # Architecture + uname := $(shell uname -m) + + ifneq (, $(filter $(uname), x86_64 amd64)) + IS_AMD64 := 1 + else ifneq (, $(filter $(uname), aarch64 arm64)) + IS_AARCH64 := 1 + else ifneq (, $(filter $(uname), riscv64)) + IS_RISCV64 := 1 + else + # We use spaces instead of tabs to indent `$(error)` + # otherwise it's considered as a command outside a + # target and it will fail. + $(error Unrecognized architecture, expect `x86_64`, `aarch64`, `arm64`, 'riscv64') + endif + + # Libc + LIBC ?= $(shell ldd 2>&1 | grep -o musl | head -n1) +endif + + +##### +# +# Define the “Compiler” column of the matrix. +# +##### + +CARGO_BINARY ?= cargo +CARGO_TARGET ?= +CARGO_TARGET_FLAG ?= + +ifneq ($(CARGO_TARGET),) +CARGO_TARGET_FLAG := --target $(CARGO_TARGET) +endif + +# Variables that can be overridden by the users to force to enable or +# to disable a specific compiler. +ENABLE_CRANELIFT ?= +ENABLE_LLVM ?= +ENABLE_SINGLEPASS ?= + +# Which compilers we build. These have dependencies that may not be on the system. +compilers := + +## +# Cranelift +## + +# If the user didn't disable the Cranelift compilerâ€Ļ +ifneq ($(ENABLE_CRANELIFT), 0) + # â€Ļ then maybe the user forced to enable the Cranelift compiler. + ifeq ($(ENABLE_CRANELIFT), 1) + compilers += cranelift + # â€Ļ otherwise, we try to check whether Cranelift works on this host. + else + compilers += cranelift + endif +endif + +ifneq (, $(findstring cranelift,$(compilers))) + ENABLE_CRANELIFT := 1 +endif + +## +# LLVM +## + +# If the user didn't disable the LLVM compilerâ€Ļ +ifeq ($(ENABLE_LLVM), 0) + LLVM_VERSION := + # â€Ļ then maybe the user forced to enable the LLVM compiler. +else ifeq ($(ENABLE_LLVM), 1) + LLVM_VERSION := $(shell llvm-config --version) + compilers += llvm + # â€Ļ or try to autodetect LLVM from `llvm-config-`. +else ifneq (, $(shell which llvm-config-15 2>/dev/null)) + LLVM_VERSION := $(shell llvm-config-15 --version) + compilers += llvm + # need force LLVM_SYS_150_PREFIX, or llvm_sys will not build in the case + export LLVM_SYS_150_PREFIX = $(shell llvm-config-15 --prefix) +else ifneq (, $(shell which llvm-config 2>/dev/null)) + LLVM_VERSION := $(shell llvm-config --version) + ifneq (, $(findstring 15,$(LLVM_VERSION))) + compilers += llvm + export LLVM_SYS_150_PREFIX = $(shell llvm-config --prefix) + else ifneq (, $(findstring 14,$(LLVM_VERSION))) + compilers += llvm + export LLVM_SYS_150_PREFIX = $(shell llvm-config --prefix) + endif +endif + +# If findstring is not empty, then it have found the value + +exclude_tests := --exclude wasmer-c-api --exclude wasmer-cli --exclude wasmer-compiler-cli +# We run integration tests separately (it requires building the c-api) +exclude_tests += --exclude wasmer-integration-tests-cli +exclude_tests += --exclude wasmer-integration-tests-ios + +ifneq (, $(findstring llvm,$(compilers))) + ENABLE_LLVM := 1 +else + # We exclude LLVM from our package testing + exclude_tests += --exclude wasmer-compiler-llvm +endif + +## +# Singlepass +## + +# If the user didn't disable the Singlepass compilerâ€Ļ +ifneq ($(ENABLE_SINGLEPASS), 0) + # â€Ļ then maybe the user forced to enable the Singlepass compiler. + ifeq ($(ENABLE_SINGLEPASS), 1) + compilers += singlepass + # â€Ļ otherwise, we try to check whether Singlepass works on this host. + else ifneq (, $(filter 1, $(IS_DARWIN) $(IS_LINUX) $(IS_FREEBSD) $(IS_WINDOWS))) + ifeq ($(IS_AMD64), 1) + compilers += singlepass + endif + ifeq ($(IS_AARCH64), 1) + ifneq ($(IS_WINDOWS), 1) + compilers += singlepass + endif + endif + endif +endif + +ifneq (, $(findstring singlepass,$(compilers))) + ENABLE_SINGLEPASS := 1 +endif + +## +# Clean the `compilers` variable. +## + +compilers := $(strip $(compilers)) + + +##### +# +# Define the “Engine” column of the matrix. +# +##### + + +# The engine is part of a pair of kind (compiler, engine). All the +# pairs are stored in the `compilers_engines` variable. +compilers_engines := + +## +# The Cranelift case. +## + +ifeq ($(ENABLE_CRANELIFT), 1) + compilers_engines += cranelift-universal +endif + +## +# The LLVM case. +## + +ifeq ($(ENABLE_LLVM), 1) + ifneq (, $(filter 1, $(IS_WINDOWS) $(IS_DARWIN) $(IS_LINUX) $(IS_FREEBSD))) + ifeq ($(IS_AMD64), 1) + compilers_engines += llvm-universal + else ifeq ($(IS_AARCH64), 1) + compilers_engines += llvm-universal + else ifeq ($(IS_RISCV64), 1) + compilers_engines += llvm-universal + endif + endif +endif + +## +# The Singlepass case. +## + +ifeq ($(ENABLE_SINGLEPASS), 1) + ifneq (, $(filter 1, $(IS_WINDOWS) $(IS_DARWIN) $(IS_LINUX) $(IS_FREEBSD))) + ifeq ($(IS_AMD64), 1) + compilers_engines += singlepass-universal + endif + ifeq ($(IS_AARCH64), 1) + compilers_engines += singlepass-universal + endif + endif +endif + +# Clean the `compilers_engines` variable. +compilers_engines := $(strip $(compilers_engines)) + + +##### +# +# Cargo features. +# +##### + +# Small trick to define a space and a comma. +space := $() $() +comma := , + +# Define the compiler Cargo features for all crates. +compiler_features := --features $(subst $(space),$(comma),$(compilers)),wasmer-artifact-create,static-artifact-create,wasmer-artifact-load,static-artifact-load +capi_compilers_engines_exclude := + +# Define the compiler Cargo features for the C API. It always excludes +# LLVM for the moment because it causes the linker to fail since LLVM is not statically linked. +# TODO: Reenable LLVM in C-API +capi_compiler_features := --features $(subst $(space),$(comma),$(filter-out llvm, $(compilers))),wasmer-artifact-create,static-artifact-create,wasmer-artifact-load,static-artifact-load +capi_compilers_engines_exclude += llvm-universal + +# We exclude singlepass-universal because it doesn't support multivalue (required in wasm-c-api tests) +capi_compilers_engines_exclude += singlepass-universal + +capi_compilers_engines := $(filter-out $(capi_compilers_engines_exclude),$(compilers_engines)) + +##### +# +# Display information. +# +##### + +ifneq (, $(filter 1, $(IS_DARWIN) $(IS_LINUX) $(IS_FREEBSD))) + bold := $(shell tput bold 2>/dev/null || echo -n '') + green := $(shell tput setaf 2 2>/dev/null || echo -n '') + yellow := $(shell tput setaf 3 2>/dev/null || echo -n '') + reset := $(shell tput sgr0 2>/dev/null || echo -n '') +endif + +HOST_TARGET=$(shell rustc -Vv | grep 'host: ' | cut -d':' -f2 | tr -d ' ') + +TARGET_DIR ?= target/release + +ifneq (, $(TARGET)) + TARGET_DIR ?= target/$(TARGET)/release +endif + +$(info -----------) +$(info $(bold)$(green)INFORMATION$(reset)) +$(info -----------) +$(info ) +$(info Host Target: `$(bold)$(green)$(HOST_TARGET)$(reset)`.) +ifneq (, $(TARGET)) + # We use spaces instead of tabs to indent `$(info)` + # otherwise it's considered as a command outside a + # target and it will fail. + $(info Build Target: $(bold)$(green)$(TARGET)$(reset) $(yellow)($(TARGET_DIR))$(reset)) +endif +ifneq (, $(LIBC)) + # We use spaces instead of tabs to indent `$(info)` + # otherwise it's considered as a command outside a + # target and it will fail. + $(info C standard library: $(bold)$(green)$(LIBC)$(reset)) +endif +$(info Enabled Compilers: $(bold)$(green)$(subst $(space),$(reset)$(comma)$(space)$(bold)$(green),$(compilers))$(reset).) +$(info Testing the following compilers & engines:) +$(info   * API: $(bold)$(green)${compilers_engines}$(reset),) +$(info   * C-API: $(bold)$(green)${capi_compilers_engines}$(reset).) +$(info Cargo features:) +$(info   * Compilers: `$(bold)$(green)${compiler_features}$(reset)`.) +$(info Rust version: $(bold)$(green)$(shell rustc --version)$(reset).) +$(info NodeJS version: $(bold)$(green)$(shell node --version)$(reset).) +ifeq ($(ENABLE_LLVM), 1) + $(info LLVM version: $(bold)$(green)${LLVM_VERSION}$(reset).) +endif +$(info ) +$(info ) +$(info --------------) +$(info $(bold)$(green)RULE EXECUTION$(reset)) +$(info --------------) +$(info ) +$(info ) + +##### +# +# Configure `sed -i` for a cross-platform usage. +# +##### + +SEDI ?= + +ifeq ($(IS_DARWIN), 1) + SEDI := "-i ''" +else ifeq ($(IS_FREEBSD), 1) + SEDI := "-i ''" +else ifeq ($(IS_LINUX), 1) + SEDI := "-i" +endif + +##### +# +# Building. +# +##### + +# Not really "all", just the default target that builds enough so make +# install will go through. +all: build-wasmer build-capi build-capi-headless + +check: check-wasmer check-wasmer-wasm check-capi + +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 + +check-capi: + RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) check $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml \ + --no-default-features --features wat,compiler,wasi,middlewares $(capi_compiler_features) + + +build-wasmer: + $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --release --manifest-path lib/cli/Cargo.toml $(compiler_features) --bin wasmer --locked + +build-wasmer-jsc: + $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --release --manifest-path lib/cli/Cargo.toml --no-default-features --features="jsc,wat" --bin wasmer --locked + +build-wasmer-debug: + $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --manifest-path lib/cli/Cargo.toml $(compiler_features) --bin wasmer --locked + +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 + +# For best results ensure the release profile looks like the following +# in Cargo.toml: +# [profile.release] +# opt-level = 'z' +# debug = false +# debug-assertions = false +# overflow-checks = false +# lto = true +# panic = 'abort' +# incremental = false +# codegen-units = 1 +# rpath = false +build-wasmer-headless-minimal: RUSTFLAGS += -C panic=abort +build-wasmer-headless-minimal: + RUSTFLAGS="${RUSTFLAGS}" xargo build --target $(HOST_TARGET) --release --manifest-path=lib/cli/Cargo.toml --no-default-features --features sys,headless-minimal --bin wasmer-headless +ifeq ($(IS_DARWIN), 1) + strip target/$(HOST_TARGET)/release/wasmer-headless +else ifeq ($(IS_WINDOWS), 1) + strip --strip-unneeded target/$(HOST_TARGET)/release/wasmer-headless.exe +else + strip --strip-unneeded target/$(HOST_TARGET)/release/wasmer-headless +endif + +build-docs: + $(CARGO_BINARY) doc $(CARGO_TARGET_FLAG) --release $(compiler_features) --document-private-items --no-deps --workspace --exclude wasmer-c-api --locked + +# The tokio crate was excluded from the docs build because the code (which is not under our control) +# does not currently compile its docs successfully +# +# ``` +# error[E0432]: unresolved import `self::doc::os` +# --> /home/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.35.1/src/lib.rs:636:16 +# | +# 636 | pub(crate) use self::doc::os; +# | ^^^^^^^^^^^^^ no `os` in `doc` +# ``` +test-build-docs-rs: + @manifest_docs_rs_features_path="package.metadata.docs.rs.features"; \ + for manifest_path in lib/*/Cargo.toml; do \ + toml get "$$manifest_path" "$$manifest_docs_rs_features_path" >/dev/null 2>&1; \ + if [ $$? -ne 0 ]; then \ + features=""; \ + else \ + features=$$(toml get "$$manifest_path" "$$manifest_docs_rs_features_path" | sed 's/\[//; s/\]//; s/"\([^"]*\)"/\1/g'); \ + fi; \ + printf "*** Building doc for package with manifest $$manifest_path ***\n\n"; \ + + if [ -z "$$features" ]; then \ + RUSTDOCFLAGS="--cfg=docsrs" $(CARGO_BINARY) +nightly doc $(CARGO_TARGET_FLAG) --manifest-path "$$manifest_path" --exclude tokio --locked || exit 1; \ + else \ + printf "Following features are inferred from Cargo.toml: $$features\n\n\n"; \ + RUSTDOCFLAGS="--cfg=docsrs" $(CARGO_BINARY) +nightly doc $(CARGO_TARGET_FLAG) --manifest-path "$$manifest_path" --exclude tokio --features "$$features" --locked || exit 1; \ + fi; \ + done + +test-build-docs-rs-ci: + @manifest_docs_rs_features_path="package.metadata.docs.rs.features"; \ + for manifest_path in lib/*/Cargo.toml; do \ + toml get "$$manifest_path" "$$manifest_docs_rs_features_path" >/dev/null 2>&1; \ + if [ $$? -ne 0 ]; then \ + features=""; \ + else \ + features=$$(toml get "$$manifest_path" "$$manifest_docs_rs_features_path" | sed 's/\[//; s/\]//; s/"\([^"]*\)"/\1/g'); \ + fi; \ + printf "*** Building doc for package with manifest $$manifest_path ***\n\n"; \ + if [ -z "$$features" ]; then \ + RUSTDOCFLAGS="--cfg=docsrs" $(CARGO_BINARY) +nightly-2023-10-05 doc $(CARGO_TARGET_FLAG) --manifest-path "$$manifest_path" --no-deps --locked || exit 1; \ + else \ + printf "Following features are inferred from Cargo.toml: $$features\n\n\n"; \ + RUSTDOCFLAGS="--cfg=docsrs" $(CARGO_BINARY) +nightly-2023-10-05 doc $(CARGO_TARGET_FLAG) --manifest-path "$$manifest_path" --no-deps --features "$$features" --locked || exit 1; \ + fi; \ + done + +build-docs-capi: + # `wasmer-c-api` lib's name is `wasmer`. To avoid a conflict + # when generating the documentation, we rename it to its + # crate's name. Then we restore the lib's name. + sed "$(SEDI)" -e 's/name = "wasmer" # ##lib.name##/name = "wasmer_c_api" # ##lib.name##/' lib/c-api/Cargo.toml + RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) doc $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --no-deps --features wat,compiler,cranelift,wasi --locked + sed "$(SEDI)" -e 's/name = "wasmer_c_api" # ##lib.name##/name = "wasmer" # ##lib.name##/' lib/c-api/Cargo.toml + +build-capi: + RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --release \ + --no-default-features --features wat,compiler,wasi,middlewares,webc_runner $(capi_compiler_features) --locked + +build-capi-singlepass: + RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --release \ + --no-default-features --features wat,compiler,singlepass,wasi,middlewares,webc_runner --locked + +build-capi-singlepass-universal: + RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --release \ + --no-default-features --features wat,compiler,singlepass,wasi,middlewares,webc_runner --locked + +build-capi-cranelift: + RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --release \ + --no-default-features --features wat,compiler,cranelift,wasi,middlewares,webc_runner --locked + +build-capi-cranelift-universal: + RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --release \ + --no-default-features --features wat,compiler,cranelift,wasi,middlewares,webc_runner --locked + +build-capi-llvm: + RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --release \ + --no-default-features --features wat,compiler,llvm,wasi,middlewares,webc_runner --locked + +build-capi-llvm-universal: + RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --release \ + --no-default-features --features wat,compiler,llvm,wasi,middlewares,webc_runner --locked + +build-capi-jsc: + RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --release \ + --no-default-features --features wat,jsc,wasi --locked + +# Headless (we include the minimal to be able to run) + +build-capi-headless: +ifeq ($(CARGO_TARGET_FLAG),) + RUSTFLAGS="${RUSTFLAGS} -C panic=abort -C link-dead-code -C lto -O -C embed-bitcode=yes" $(CARGO_BINARY) build --target $(HOST_TARGET) --manifest-path lib/c-api/Cargo.toml --release \ + --no-default-features --features compiler-headless,wasi,webc_runner --target-dir target/headless --locked +else + RUSTFLAGS="${RUSTFLAGS} -C panic=abort -C link-dead-code -C lto -O -C embed-bitcode=yes" $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --release \ + --no-default-features --features compiler-headless,wasi,webc_runner --target-dir target/headless --locked +endif + +build-capi-headless-ios: + RUSTFLAGS="${RUSTFLAGS} -C panic=abort" cargo lipo --manifest-path lib/c-api/Cargo.toml --release \ + --no-default-features --features compiler-headless,wasi,webc_runner --target-dir target/$(CARGO_TARGET)/headless + +##### +# +# Testing. +# +##### + +# test compilers +test-stage-0-wast: + $(CARGO_BINARY) nextest run $(CARGO_TARGET_FLAG) --release $(compiler_features) --locked + +# test packages +test-stage-1-test-all: + $(CARGO_BINARY) nextest run $(CARGO_TARGET_FLAG) --workspace --release $(exclude_tests) --exclude wasmer-c-api-test-runner --exclude wasmer-capi-examples-runner $(compiler_features) --locked + $(CARGO_BINARY) test --doc $(CARGO_TARGET_FLAG) --workspace --release $(exclude_tests) --exclude wasmer-c-api-test-runner --exclude wasmer-capi-examples-runner $(compiler_features) --locked +test-stage-2-test-compiler-cranelift-nostd: + $(CARGO_BINARY) test $(CARGO_TARGET_FLAG) --manifest-path lib/compiler-cranelift/Cargo.toml --release --no-default-features --features=std --locked +test-stage-3-test-compiler-singlepass-nostd: + $(CARGO_BINARY) test $(CARGO_TARGET_FLAG) --manifest-path lib/compiler-singlepass/Cargo.toml --release --no-default-features --features=std --locked +test-stage-4-wasmer-cli: + $(CARGO_BINARY) test $(CARGO_TARGET_FLAG) --manifest-path lib/virtual-fs/Cargo.toml --release --locked + $(CARGO_BINARY) test $(CARGO_TARGET_FLAG) --manifest-path lib/cli/Cargo.toml $(compiler_features) --release --locked + +# test examples +test-stage-5-test-examples: + $(CARGO_BINARY) test $(CARGO_TARGET_FLAG) $(compiler_features) --features wasi --examples --locked +test-stage-6-test-examples-release: + $(CARGO_BINARY) test $(CARGO_TARGET_FLAG) --release $(compiler_features) --features wasi --examples --locked + +test-stage-7-capi-integration-tests: + $(CARGO_BINARY) test $(CARGO_TARGET_FLAG) --release --package wasmer-c-api-test-runner --locked + $(CARGO_BINARY) test $(CARGO_TARGET_FLAG) --release --package wasmer-capi-examples-runner --locked + +test: test-compilers test-packages test-examples + +test-compilers: test-stage-0-wast + +test-packages: test-stage-1-test-all test-stage-2-test-compiler-cranelift-nostd test-stage-3-test-compiler-singlepass-nostd test-stage-4-wasmer-cli + +test-examples: test-stage-5-test-examples test-stage-6-test-examples-release + +test-js: test-js-api test-js-wasi + +# TODO: disabled because the no-std / core feature doesn't actually work at the moment. +# See https://github.com/wasmerio/wasmer/issues/3429 +# test-js-core: +# cd lib/api && wasm-pack test --node -- --no-default-features --features js,core,wasm-types-polyfill,wat + +test-js-api: + cd lib/api && wasm-pack test --node -- --no-default-features --features js-default,wat + +test-js-wasi: + cd lib/wasix && wasm-pack test --node -- --no-default-features --features test-js,wasmer/js,wasmer/std + +##### +# +# Testing compilers. +# +##### + +test-compilers-compat: $(foreach compiler,$(compilers),test-$(compiler)) + +test-singlepass-universal: + $(CARGO_BINARY) test $(CARGO_TARGET_FLAG) --release --tests $(compiler_features) --locked -- singlepass::universal + +test-cranelift-universal: + $(CARGO_BINARY) test $(CARGO_TARGET_FLAG) --release --tests $(compiler_features) --locked -- cranelift::universal + +test-llvm-universal: + $(CARGO_BINARY) test $(CARGO_TARGET_FLAG) --release --tests $(compiler_features) --locked -- llvm::universal + +test-singlepass: $(foreach singlepass_engine,$(filter singlepass-%,$(compilers_engines)),test-$(singlepass_engine)) + +test-cranelift: $(foreach cranelift_engine,$(filter cranelift-%,$(compilers_engines)),test-$(cranelift_engine)) + +test-llvm: $(foreach llvm_engine,$(filter llvm-%,$(compilers_engines)),test-$(llvm_engine)) + +# same as test-capi, but without the build-capi step first +test-capi-ci: $(foreach compiler_engine,$(capi_compilers_engines),test-capi-crate-$(compiler_engine) test-capi-integration-$(compiler_engine)) + +# This test requires building the capi with all the available +# compilers first +test-capi: build-capi package-capi test-capi-ci + +test-capi-jsc: build-capi-jsc package-capi test-capi-integration-jsc + +test-capi-crate-%: + WASMER_CAPI_CONFIG=$(shell echo $@ | sed -e s/test-capi-crate-//) $(CARGO_BINARY) test $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --release \ + --no-default-features --features wat,compiler,wasi,middlewares,webc_runner $(capi_compiler_features) --locked -- --nocapture + +test-capi-integration-%: + # note: you need to do make build-capi and make package-capi first! + # Test the Wasmer C API tests for C + cd lib/c-api/tests; WASMER_CAPI_CONFIG=$(shell echo $@ | sed -e s/test-capi-integration-//) WASMER_DIR=`pwd`/../../../package make test + # Test the Wasmer C API examples + cd lib/c-api/examples; WASMER_CAPI_CONFIG=$(shell echo $@ | sed -e s/test-capi-integration-//) WASMER_DIR=`pwd`/../../../package make run + +test-wasi-unit: + $(CARGO_BINARY) test $(CARGO_TARGET_FLAG) --manifest-path lib/wasi/Cargo.toml --release --locked + +test-wasi: + $(CARGO_BINARY) test $(CARGO_TARGET_FLAG) --release --tests $(compiler_features) --locked -- wasi::wasitests + +test-wasi-fyi: build-wasmer + cd tests/wasi-fyi; \ + ./test.sh + +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 + 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 + $(CARGO_BINARY) nextest run $(CARGO_TARGET_FLAG) --features webc_runner -p wasmer-integration-tests-cli --locked + +test-integration-ios: + $(CARGO_BINARY) test $(CARGO_TARGET_FLAG) --features webc_runner -p wasmer-integration-tests-ios --locked + +generate-wasi-tests: +# Uncomment the following for installing the toolchain +# cargo run -p wasi-test-generator -- -s + $(CARGO_BINARY) run $(CARGO_TARGET_FLAG) -p wasi-test-generator -- -g +##### +# +# Packaging. +# +##### + +package-minimal-headless-wasmer: +ifeq ($(IS_WINDOWS), 1) + if [ -f "target/$(HOST_TARGET)/release/wasmer-headless.exe" ]; then \ + cp target/$(HOST_TARGET)/release/wasmer-headless.exe package/bin ;\ + fi +else + if [ -f "target/$(HOST_TARGET)/release/wasmer-headless" ]; then \ + cp target/$(HOST_TARGET)/release/wasmer-headless package/bin ;\ + fi +endif + +package-wasmer: + mkdir -p "package/bin" + ls -R target +ifeq ($(IS_WINDOWS), 1) + if [ -f "$(TARGET_DIR)/wasmer.exe" ]; then \ + cp "$(TARGET_DIR)/wasmer.exe" package/bin ;\ + fi + if [ -f "target/$(HOST_TARGET)/release/wasmer.exe" ]; then \ + cp "target/$(HOST_TARGET)/release/wasmer.exe" package/bin ;\ + fi +else + if [ -f "$(TARGET_DIR)/wasmer" ]; then \ + cp $(TARGET_DIR)/wasmer package/bin ;\ + fi + if [ -f "target/$(HOST_TARGET)/release/wasmer" ]; then \ + cp "target/$(HOST_TARGET)/release/wasmer" package/bin ;\ + fi +ifeq ($(IS_DARWIN), 1) + codesign -s - package/bin/wasmer || true +endif +endif + +package-capi-headless: build-capi-headless package-capi + +package-capi-jsc: build-capi-jsc package-capi + +package-capi: + mkdir -p "package/include" + mkdir -p "package/lib" + mkdir -p "package/winsdk" + cp lib/c-api/wasmer.h* package/include + cp lib/c-api/wasmer_wasm.h* package/include + cp lib/c-api/tests/wasm-c-api/include/wasm.h* package/include + cp lib/c-api/README.md package/include/README.md + if [ -f $(TARGET_DIR)/wasmer.dll ]; then \ + cp $(TARGET_DIR)/wasmer.dll package/lib/wasmer.dll ;\ + fi + + if [ -f target/headless/$(CARGO_TARGET)/release/wasmer.dll ]; then \ + cp target/headless/$(CARGO_TARGET)/release/wasmer.dll package/lib/wasmer-headless.dll ;\ + fi + + if [ -f target/headless/$(HOST_TARGET)/release/wasmer.dll ]; then \ + cp target/headless/$(HOST_TARGET)/release/wasmer.dll package/lib/wasmer-headless.dll ;\ + fi + + if [ -f $(TARGET_DIR)/wasmer.dll.lib ]; then \ + cp $(TARGET_DIR)/wasmer.dll.lib package/lib/wasmer.dll.lib ;\ + fi + + if [ -f target/headless/$(CARGO_TARGET)/release/wasmer.dll.lib ]; then \ + cp target/headless/$(CARGO_TARGET)/release/wasmer.dll.lib package/lib/wasmer-headless.dll.lib ;\ + fi + + if [ -f target/headless/$(HOST_TARGET)/release/wasmer.dll.lib ]; then \ + cp target/headless/$(HOST_TARGET)/release/wasmer.dll.lib package/lib/wasmer-headless.dll.lib ;\ + fi + + if [ -f $(TARGET_DIR)/wasmer.lib ]; then \ + cp $(TARGET_DIR)/wasmer.lib package/lib/wasmer.lib ;\ + fi + + if [ -f target/headless/$(CARGO_TARGET)/release/wasmer.lib ]; then \ + cp target/headless/$(CARGO_TARGET)/release/wasmer.lib package/lib/wasmer-headless.lib ;\ + fi + + if [ -f target/headless/$(HOST_TARGET)/release/wasmer.lib ]; then \ + cp target/headless/$(HOST_TARGET)/release/wasmer.lib package/lib/wasmer-headless.lib ;\ + fi + + if [ -f $(TARGET_DIR)/libwasmer.dylib ]; then \ + cp $(TARGET_DIR)/libwasmer.dylib package/lib/libwasmer.dylib ;\ + fi + + if [ -f target/headless/$(CARGO_TARGET)/release/libwasmer.dylib ]; then \ + cp target/headless/$(CARGO_TARGET)/release/libwasmer.dylib package/lib/libwasmer-headless.dylib ;\ + fi + + if [ -f target/headless/$(HOST_TARGET)/release/libwasmer.dylib ]; then \ + cp target/headless/$(HOST_TARGET)/release/libwasmer.dylib package/lib/libwasmer-headless.dylib ;\ + fi + + if [ -f $(TARGET_DIR)/libwasmer.so ]; then \ + cp $(TARGET_DIR)/libwasmer.so package/lib/libwasmer.so ;\ + fi + + if [ -f target/headless/$(CARGO_TARGET)/release/libwasmer.so ]; then \ + cp target/headless/$(CARGO_TARGET)/release/libwasmer.so package/lib/libwasmer-headless.so ;\ + fi + + if [ -f target/headless/$(HOST_TARGET)/release/libwasmer.so ]; then \ + cp target/headless/$(HOST_TARGET)/release/libwasmer.so package/lib/libwasmer-headless.so ;\ + fi + + if [ -f $(TARGET_DIR)/libwasmer.a ]; then \ + cp $(TARGET_DIR)/libwasmer.a package/lib/libwasmer.a ;\ + fi + + if [ -f target/headless/$(CARGO_TARGET)/release/libwasmer.a ]; then \ + cp target/headless/$(CARGO_TARGET)/release/libwasmer.a package/lib/libwasmer-headless.a ;\ + fi + + if [ -f target/headless/$(HOST_TARGET)/release/libwasmer.a ]; then \ + cp target/headless/$(HOST_TARGET)/release/libwasmer.a package/lib/libwasmer-headless.a ;\ + fi + + if [ -f target/$(HOST_TARGET)/release/wasmer.dll ]; then \ + cp target/$(HOST_TARGET)/release/wasmer.dll package/lib/wasmer.dll ;\ + fi + + if [ -f target/$(HOST_TARGET)/release/wasmer.dll.lib ]; then \ + cp target/$(HOST_TARGET)/release/wasmer.dll.lib package/lib/wasmer.dll.lib ;\ + fi + if [ -f target/$(HOST_TARGET)/release/wasmer.lib ]; then \ + cp target/$(HOST_TARGET)/release/wasmer.lib package/lib/wasmer.lib ;\ + fi + + if [ -f target/$(HOST_TARGET)/release/libwasmer.dylib ]; then \ + cp target/$(HOST_TARGET)/release/libwasmer.dylib package/lib/libwasmer.dylib ;\ + fi + + if [ -f target/$(HOST_TARGET)/release/libwasmer.so ]; then \ + cp target/$(HOST_TARGET)/release/libwasmer.so package/lib/libwasmer.so ;\ + fi + if [ -f target/$(HOST_TARGET)/release/libwasmer.a ]; then \ + cp target/$(HOST_TARGET)/release/libwasmer.a package/lib/libwasmer.a ;\ + fi + +package-docs: build-docs build-docs-capi + mkdir -p "package/docs/crates" + cp -R target/doc/ package/docs/crates + echo '' > package/docs/index.html + echo '' > package/docs/crates/index.html + +package: package-wasmer package-minimal-headless-wasmer package-capi + +tar-capi: + ls -R package + tar -C package -zcvf build-capi.tar.gz lib include winsdk + +untar-capi: + mkdir -p package + mkdir -p target/release + mkdir -p target/$(HOST_TARGET)/release + tar -C package -xf ./build-capi.tar.gz + cp package/lib/* target/release + cp package/lib/* target/$(HOST_TARGET)/release + mkdir -p target/debug + mkdir -p target/$(HOST_TARGET)/debug + tar -C package -xf ./build-capi.tar.gz + cp package/lib/* target/debug + cp package/lib/* target/$(HOST_TARGET)/debug + echo "untar capi" + ls -R target + echo "package" + ls -R package + +tar-wasmer: + ls -R package + tar -C package -zcvf build-wasmer.tar.gz bin + +untar-wasmer: + mkdir -p package + mkdir -p target/release + mkdir -p target/$(HOST_TARGET)/release + tar -C package -xf ./build-wasmer.tar.gz + cp package/bin/* target/release + cp package/bin/* target/$(HOST_TARGET)/release + mkdir -p target/debug + mkdir -p target/$(HOST_TARGET)/debug + tar -C package -xf ./build-wasmer.tar.gz + cp package/bin/* target/debug + cp package/bin/* target/$(HOST_TARGET)/debug + echo "untar wasmer" + ls -R target + echo "package" + ls -R package + +distribution-gnu: package-capi + cp LICENSE package/LICENSE + cp ATTRIBUTIONS.md package/ATTRIBUTIONS + mkdir -p dist + tar -C package -zcvf wasmer.tar.gz lib include winsdk LICENSE ATTRIBUTIONS + mv wasmer.tar.gz dist/ + +distribution: package + cp LICENSE package/LICENSE + cp ATTRIBUTIONS.md package/ATTRIBUTIONS + mkdir -p dist +ifeq ($(IS_WINDOWS), 1) + iscc scripts/windows-installer/wasmer.iss + cp scripts/windows-installer/WasmerInstaller.exe dist/ +endif + tar -C package -zcvf wasmer.tar.gz bin lib include LICENSE ATTRIBUTIONS + mv wasmer.tar.gz dist/ + +##### +# +# Installation (for Distros). +# +##### + +DESTDIR ?= /usr/local + +install: install-wasmer install-capi-headers install-capi-lib install-pkgconfig install-misc + +install-capi: install-capi-headers install-capi-lib install-capi-pkgconfig install-misc + +install-wasmer: + install -Dm755 target/release/wasmer $(DESTDIR)/bin/wasmer + +install-capi-headers: + for header in lib/c-api/*.h; do install -Dm644 "$$header" $(DESTDIR)/include/$$(basename $$header); done + install -Dm644 lib/c-api/README.md $(DESTDIR)/include/wasmer-README.md + +# Currently implemented for linux only. TODO +install-capi-lib: + pkgver=$$($(CARGO_BINARY) pkgid --manifest-path lib/c-api/Cargo.toml | sed 's/^.*wasmer-c-api@//') && \ + shortver="$${pkgver%.*}" && \ + majorver="$${shortver%.*}" && \ + install -Dm755 target/release/libwasmer.so "$(DESTDIR)/lib/libwasmer.so.$$pkgver" && \ + ln -sf "libwasmer.so.$$pkgver" "$(DESTDIR)/lib/libwasmer.so.$$shortver" && \ + ln -sf "libwasmer.so.$$pkgver" "$(DESTDIR)/lib/libwasmer.so.$$majorver" && \ + ln -sf "libwasmer.so.$$pkgver" "$(DESTDIR)/lib/libwasmer.so" + +install-misc: + install -Dm644 LICENSE "$(DESTDIR)"/share/licenses/wasmer/LICENSE + +install-capi-pkgconfig: + @pkgver=$$($(CARGO_BINARY) pkgid --manifest-path lib/c-api/Cargo.toml | sed --posix 's/^.*wasmer-c-api:\([0-9.]*\)$\/\1/') && \ + printf "prefix=%s\nincludedir=\044{prefix}/include\nlibdir=\044{prefix}/lib\n\nName: wasmer\nDescription: The Wasmer library for running WebAssembly\nVersion: %s\nCflags: -I\044{prefix}/include\nLibs: -L\044{prefix}/lib -lwasmer\n" "$(DESTDIR)" "$${pkgver}" | install -Dm644 /dev/stdin "$(DESTDIR)"/lib/pkgconfig/wasmer.pc + +install-pkgconfig: + # Make sure WASMER_INSTALL_PREFIX is set during build + unset WASMER_DIR; \ + if pc="$$(target/release/wasmer config --pkg-config 2>/dev/null)"; then \ + echo "$$pc" | install -Dm644 /dev/stdin "$(DESTDIR)"/lib/pkgconfig/wasmer.pc; \ + else \ + echo 1>&2 "WASMER_INSTALL_PREFIX was not set during build, not installing wasmer.pc"; \ + fi + +install-wasmer-headless-minimal: + install -Dm755 target/release/wasmer-headless $(DESTDIR)/bin/wasmer-headless + +##### +# +# Miscellaneous. +# +##### + +# Updates the spectests from the repo +update-testsuite: + git subtree pull --prefix tests/wast/spec https://github.com/WebAssembly/testsuite.git master --squash + +lint-packages: RUSTFLAGS += -D dead-code -D nonstandard-style -D unused-imports -D unused-mut -D unused-variables -D unused-unsafe -D unreachable-patterns -D bad-style -D improper-ctypes -D unused-allocation -D unused-comparisons -D while-true -D unconditional-recursion -D bare-trait-objects -D function_item_references # TODO: add `-D missing-docs` +lint-packages: + RUSTFLAGS="${RUSTFLAGS}" cargo clippy --all --exclude wasmer-cli --locked -- -D clippy::all + RUSTFLAGS="${RUSTFLAGS}" cargo clippy --manifest-path lib/cli/Cargo.toml --locked $(compiler_features) -- -D clippy::all + RUSTFLAGS="${RUSTFLAGS}" cargo clippy --manifest-path fuzz/Cargo.toml --locked $(compiler_features) -- -D clippy::all + +lint-formatting: + cargo fmt --all -- --check + cargo fmt --manifest-path fuzz/Cargo.toml -- --check + +lint: lint-formatting lint-packages + +install-local: package + tar -C ~/.wasmer -zxvf wasmer.tar.gz + +test-minimal-versions: + rm -f Cargo.lock + cargo +nightly build --tests -Z minimal-versions --all-features + +update-graphql-schema: + curl -sSfL https://registry.wapm.io/graphql/schema.graphql > lib/registry/graphql/schema.graphql + +require-nextest: + cargo nextest --version > /dev/null || cargo binstall cargo-nextest --secure || cargo install cargo-nextest diff --git a/arbitrator/tools/module_roots/wasmer/PACKAGING.md b/arbitrator/tools/module_roots/wasmer/PACKAGING.md new file mode 100644 index 0000000..7d32cc8 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/PACKAGING.md @@ -0,0 +1,55 @@ +# Wasmer OS distro packaging notes + +* Wasmer is written in Rust. To build Wasmer, where possible, do not + directly invoke `cargo`, but use the supplied `Makefile` + +* Wasmer provides several compilers and the `Makefile` autodetects + when compilers can be compiled and/or installed. Set the environment + variables `ENABLE_{CRANELIFT,LLVM,SINGLEPASS}=1` to force compiler + to be build or to fail trying, e.g: + + ```sh + $ ENABLE_LLVM=1 make build-wasmer + ``` + +* `make install` respects `DESTDIR`, but `prefix` must be configured + with `WASMER_INSTALL_PREFIX`. Note that `DESTDIR` must include + `WASMER_INSTALL_PREFIX`, e.g.: + + ```sh + export WASMER_INSTALL_PREFIX=/usr + make + DESTDIR=/tmp/staging/usr make install + ``` + +* In case you must build/install directly with `cargo`, make sure to + enable at least one compiler feature, like e.g. `--features + cranelift`, + + * Beware that compiling with `cargo build --workspace --features â€Ļ` + will not enable features on the subcrates in the workspace and + result in a headless Wasmer binary that can not compile Wasm files + directly. + +* If you split the package into several subpackages, beware that the + `create-exe` command of the `wasmer` CLI requires `libwasmer.a` to + be installed at `$WASMER_INSTALL_PREFIX/lib/libwasmer.a`. Suggestions for splitting: + + * The `wasmer-headless` CLI contains a subset of the `wasmer`'s functionalities + and should only be packaged when splitting — it must be built + explicitly with: + + ```sh + $ make build-wasmer-headless-minimal install-wasmer-headless-minimal + ``` + * `libwasmer`, containing `libwasmer.so*`, + * `libwasmer-dev`, containing the header files and a `.pc` file, + * `libwasmer-static`, containing `libwasmer.a`. + +The Wasmer distro packaging story is still in its infancy, so feedback is very welcome. + +## Miscellaneous: binfmt_misc + +Wasmer can be registered as a binfmt interpreter for wasm binaries. +An example systemd [.service](./scripts/wasmer-binfmt.service.example) is included here. +Please consider statically linking the wasmer binary so that this capability is also available in mount namespaces. diff --git a/arbitrator/tools/module_roots/wasmer/README.md b/arbitrator/tools/module_roots/wasmer/README.md new file mode 100644 index 0000000..31aad97 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/README.md @@ -0,0 +1,259 @@ + + +
+ +Wasmer is a _blazing fast_ and _secure_ [**WebAssembly**](https://webassembly.org) runtime that enables incredibly +_lightweight containers_ to run anywhere: from _Desktop_ to the _Cloud_, _Edge_ and your browser. + +- **Secure** by default. No file, network, or environment access, unless explicitly enabled. +- **Pluggable**. supports [**WASIX**](https://wasix.org/), [WASI](https://github.com/WebAssembly/WASI) and [Emscripten](https://emscripten.org/) out of the box. +- **Incredibly Fast**. Run WebAssembly at near-native speeds. +- **Embeddable** [anywhere via Wasmer SDKs](https://github.com/wasmerio/wasmer/#wasmer-sdk) + +### Install Wasmer + +```sh +curl https://get.wasmer.io -sSfL | sh +``` + +
+ Other installation options (Powershell, Brew, Cargo, ...) + + _Wasmer can be installed from various package managers. Choose the one that fits best for your environment:_ + + * Powershell (Windows) + ```powershell + iwr https://win.wasmer.io -useb | iex + ``` + +- Homebrew (macOS, Linux) + + ```sh + brew install wasmer + ``` + +- Scoop (Windows) + + ```sh + scoop install wasmer + ``` + +- Chocolatey (Windows) + + ```sh + choco install wasmer + ``` + +- Cargo binstall + + ```sh + cargo binstall wasmer-cli + ``` + +- Cargo + + _Note: All the available + features are described in the [`wasmer-cli` + crate docs](https://github.com/wasmerio/wasmer/tree/master/lib/cli/README.md)_ + + ```sh + cargo install wasmer-cli + ``` + +> Looking for more installation options? See [the `wasmer-install` +> repository](https://github.com/wasmerio/wasmer-install) to learn +> more! + +
+ +> Note: You can also try Wasmer online in [wasmer.sh](https://wasmer.sh/) +> +### Quickstart + +You can start by running +[Cowsay](https://wasmer.io/syrusakbary/cowsay): + +```bash +$ wasmer run cowsay "hello world" + _____________ +< hello world > + ------------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || +``` + +> There are many more available packages, such as [`wasmer/python`](https://wasmer.io/wasmer/python) or [`quickjs`](https://wasmer.io/saghul/quickjs). [Create your own package](https://docs.wasmer.io/registry/get-started), or explore packages from the community: https://wasmer.io/explore + +#### Here is what you can do next: + +- [Run a package](https://docs.wasmer.io/runtime/get-started) +- [Publish a package](https://docs.wasmer.io/registry/get-started) +- [Deploy your website](https://docs.wasmer.io/edge/get-started) +- [Read more about Wasmer](https://wasmer.io/posts) + +## Wasmer SDK + +You can use the Wasmer runtime **embedded in different +languages** with the Wasmer SDK: + +| | Language | Package | Documentation | +| ---------------- | ------------------------------------ | ------------------------------------- | ---------------------- | +| ![Rust logo] | [**Rust**][Rust integration] | [`wasmer` Rust crate] | [Learn][rust docs] | +| ![C logo] | [**C**][C integration] | [`wasm.h` header] | [Learn][c docs] | +| ![C++ logo] | [**C++**][C integration] | [`wasm.hh` header] | [Learn][c docs] | +| ![C# logo] | [**C#**][C# integration] | [`WasmerSharp` NuGet package] | [Learn][c# docs] | +| ![D logo] | [**D**][D integration] | [`wasmer` Dub package] | [Learn][d docs] | +| ![Python logo] | [**Python**][Python integration] | [`wasmer` PyPI package] | [Learn][python docs] | +| ![JS logo] | [**Javascript**][JS integration] | [`@wasmerio` NPM packages] | [Learn][js docs] | +| ![Go logo] | [**Go**][Go integration] | [`wasmer` Go package] | [Learn][go docs] | +| ![PHP logo] | [**PHP**][PHP integration] | [`wasm` PECL package] | [Learn][php docs] | +| ![Ruby logo] | [**Ruby**][Ruby integration] | [`wasmer` Ruby Gem] | [Learn][ruby docs] | +| ![Java logo] | [**Java**][Java integration] | [`wasmer/wasmer-jni` Bintray package] | [Learn][java docs] | +| ![R logo] | [**R**][R integration] | _no published package_ | [Learn][r docs] | +| ![Postgres logo] | [**Postgres**][Postgres integration] | _no published package_ | [Learn][postgres docs] | +| ![Swift logo] | [**Swift**][Swift integration] | _no published package_ | | +| ![Zig logo] | [**Zig**][Zig integration] | _no published package_ | | +| ![Dart logo] | [**Dart**][Dart integration] | [`wasm` pub package] | | +| ![Crystal logo] | [**Crystal**][Crystal integration] | _no published package_ | [Learn][crystal docs] | +| ![Lisp logo] | [**Lisp**][Lisp integration] | _no published package_ | | +| ![Julia logo] | [**Julia**][Julia integration] | _no published package_ | | +| ![VLang logo] | [**V**][vlang integration] | _no published package_ | | +| ![Ocaml logo] | [**OCaml**][OCaml integration] | [`wasmer` OCaml package] | | + +[👋  Missing a language?](https://github.com/wasmerio/wasmer/issues/new?assignees=&labels=%F0%9F%8E%89+enhancement&template=---feature-request.md&title=) + +[rust logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/rust.svg +[rust integration]: https://github.com/wasmerio/wasmer/tree/master/lib/api +[`wasmer` rust crate]: https://crates.io/crates/wasmer/ +[rust docs]: https://docs.rs/wasmer/ +[c logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/c.svg +[c integration]: https://github.com/wasmerio/wasmer/tree/master/lib/c-api +[`wasm.h` header]: https://github.com/wasmerio/wasmer/blob/master/lib/c-api/tests/wasm-c-api/include/wasm.h +[c docs]: https://docs.rs/wasmer-c-api/*/wasmer/wasm_c_api/index.html +[c++ logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/cpp.svg +[`wasm.hh` header]: https://github.com/wasmerio/wasmer/blob/master/lib/c-api/tests/wasm-c-api/include/wasm.hh +[c# logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/csharp.svg +[c# integration]: https://github.com/migueldeicaza/WasmerSharp +[`wasmersharp` nuget package]: https://www.nuget.org/packages/WasmerSharp/ +[c# docs]: https://migueldeicaza.github.io/WasmerSharp/ +[d logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/d.svg +[d integration]: https://github.com/chances/wasmer-d +[`wasmer` Dub package]: https://code.dlang.org/packages/wasmer +[d docs]: https://chances.github.io/wasmer-d +[python logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/python.svg +[python integration]: https://github.com/wasmerio/wasmer-python +[`wasmer` pypi package]: https://pypi.org/project/wasmer/ +[python docs]: https://wasmerio.github.io/wasmer-python/api/wasmer +[go logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/go.svg +[go integration]: https://github.com/wasmerio/wasmer-go +[`wasmer` go package]: https://pkg.go.dev/github.com/wasmerio/wasmer-go/wasmer +[go docs]: https://pkg.go.dev/github.com/wasmerio/wasmer-go/wasmer?tab=doc +[php logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/php.svg +[php integration]: https://github.com/wasmerio/wasmer-php +[`wasm` pecl package]: https://pecl.php.net/package/wasm +[php docs]: https://wasmerio.github.io/wasmer-php/ +[js logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/js.svg +[js integration]: https://github.com/wasmerio/wasmer-js +[`@wasmerio` npm packages]: https://www.npmjs.com/org/wasmer +[js docs]: https://docs.wasmer.io/integrations/js/reference-api +[ruby logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/ruby.svg +[ruby integration]: https://github.com/wasmerio/wasmer-ruby +[`wasmer` ruby gem]: https://rubygems.org/gems/wasmer +[ruby docs]: https://wasmerio.github.io/wasmer-ruby/wasmer_ruby/index.html +[java logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/java.svg +[java integration]: https://github.com/wasmerio/wasmer-java +[`wasmer/wasmer-jni` bintray package]: https://bintray.com/wasmer/wasmer-jni/wasmer-jni +[java docs]: https://github.com/wasmerio/wasmer-java/#api-of-the-wasmer-library +[elixir logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/elixir.svg +[elixir integration]: https://github.com/tessi/wasmex +[elixir docs]: https://hexdocs.pm/wasmex/api-reference.html +[`wasmex` hex package]: https://hex.pm/packages/wasmex +[r logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/r.svg +[r integration]: https://github.com/dirkschumacher/wasmr +[r docs]: https://github.com/dirkschumacher/wasmr#example +[postgres logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/postgres.svg +[postgres integration]: https://github.com/wasmerio/wasmer-postgres +[postgres docs]: https://github.com/wasmerio/wasmer-postgres#usage--documentation +[swift logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/swift.svg +[swift integration]: https://github.com/AlwaysRightInstitute/SwiftyWasmer +[zig logo]: https://raw.githubusercontent.com/ziglang/logo/master/zig-favicon.png +[zig integration]: https://github.com/zigwasm/wasmer-zig +[dart logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/dart.svg +[dart integration]: https://github.com/dart-lang/wasm +[`wasm` pub package]: https://pub.dev/packages/wasm +[lisp logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/lisp.svg +[lisp integration]: https://github.com/helmutkian/cl-wasm-runtime +[crystal logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/crystal.svg +[crystal integration]: https://github.com/naqvis/wasmer-crystal +[crystal docs]: https://naqvis.github.io/wasmer-crystal/ +[julia logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/julia.svg +[julia integration]: https://github.com/Pangoraw/Wasmer.jl +[vlang logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/vlang.svg +[vlang integration]: https://github.com/vlang/wasmer +[OCaml logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/ocaml.svg +[OCaml integration]: https://github.com/wasmerio/wasmer-ocaml +[`wasmer` OCaml package]: https://opam.ocaml.org/packages/wasmer/ + +## Contribute + +We appreciate your help! 💜 + +We recommend reading the following guide on how to contribute into a complex project successfully: +https://mitchellh.com/writing/contributing-to-complex-projects + +Check our docs on how to [build Wasmer from +source](https://docs.wasmer.io/developers/build-from-source) or [test your changes](https://docs.wasmer.io/developers/testing). + + + +## Community + +Wasmer has an amazing community of developers and contributors. Welcome, please join us! 👋 + +- [Wasmer Community Discord](https://discord.gg/rWkMNStrEW) +- [Wasmer on Twitter](https://twitter.com/wasmerio) + +-------- + +> _README also in: +> [🇨đŸ‡ŗ 中 文 -Chinese](https://github.com/wasmerio/wasmer/blob/master/docs/cn/README.md) â€ĸ +> [🇩đŸ‡Ē Deutsch-German](https://github.com/wasmerio/wasmer/blob/master/docs/de/README.md) â€ĸ +> [đŸ‡Ē🇸 EspaÃąol-Spanish](https://github.com/wasmerio/wasmer/blob/master/docs/es/README.md) â€ĸ +> [đŸ‡Ģ🇷 Français-French](https://github.com/wasmerio/wasmer/blob/master/docs/fr/README.md) â€ĸ +> [đŸ‡¯đŸ‡ĩ æ—ĨæœŦ čĒž -Japanese](https://github.com/wasmerio/wasmer/blob/master/docs/ja/README.md) â€ĸ +> [🇰🇷 한ęĩ­ė–´ -Korean](https://github.com/wasmerio/wasmer/blob/master/docs/ko/README.md)_. + diff --git a/arbitrator/tools/module_roots/wasmer/SECURITY.md b/arbitrator/tools/module_roots/wasmer/SECURITY.md new file mode 100644 index 0000000..0f8b874 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/SECURITY.md @@ -0,0 +1,23 @@ +# Security Policy + +## Supported Versions + +The table below summarizes which versions are still supported, and which aren't. + +| Version | Supported | +|-|-| +| 0.x | ❌ | +| 1.x | ❌ | +| 2.x | ✅ | +| 3.x | ✅ | + +## Reporting a Vulnerability + +The Wasmer team and community take security bugs in Wasmer seriously. +We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions. + +To report a security issue, email security@wasmer.io or +hello@wasmer.io and include the word "SECURITY" in the subject line. + +The Wasmer team will send a response indicating the next steps in handling your report. +After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance. diff --git a/arbitrator/tools/module_roots/wasmer/Xargo.toml b/arbitrator/tools/module_roots/wasmer/Xargo.toml new file mode 100644 index 0000000..2b87983 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/Xargo.toml @@ -0,0 +1,2 @@ +[dependencies] +std = {} diff --git a/arbitrator/tools/module_roots/wasmer/assets/README.md b/arbitrator/tools/module_roots/wasmer/assets/README.md new file mode 100644 index 0000000..b32a943 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/README.md @@ -0,0 +1,3 @@ +# Wasmer Documentation Assets + +This directory contains documentation assets. diff --git a/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_compilation.png b/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_compilation.png new file mode 100644 index 0000000..5ed33a7 Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_compilation.png differ diff --git a/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_compilation.svg b/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_compilation.svg new file mode 100644 index 0000000..f930d20 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_compilation.svg @@ -0,0 +1 @@ +
wasmer::Module::new
wasmer::Module::new
Wasm bytes
Wasm bytes
wasmer::Module::compile
wasmer::Module::compile
wasmer_engine::Engine::compile
wasmer_engine::Engine::compile
wasmer::Module::validate
wasmer::Module::validate
wasmer_compiler::ModuleEnvironment::translate
wasmer_compiler::ModuleEnvironment::translate
wasmer_compiler::Compiler::compile_module
wasmer_compiler::Compiler::compile_module
diff --git a/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_instantiation.png b/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_instantiation.png new file mode 100644 index 0000000..69e01ed Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_instantiation.png differ diff --git a/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_instantiation.svg b/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_instantiation.svg new file mode 100644 index 0000000..a3d049d --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_instantiation.svg @@ -0,0 +1 @@ +
wasmer::Instance::new
wasmer::Instance::new
wasmer::Module::instantiate
wasmer::Module::instantiate
wasmer_engine::Artifact::instantiate
wasmer_engine::Artifact::instantiate
wasmer_engine::resolver::resolve_imports
wasmer_engine::resolver::resolve_imports
wasmer_engine::Tunables::create_memories
wasmer_engine::Tunables::create_memories
wasmer_engine::Tunables::create_tables
wasmer_engine::Tunables::create_tables
wasmer_engine::Tunables::create_globals
wasmer_engine::Tunables::create_globals
wasmer_engine::Artifact::finish_instantiation
wasmer_engine::Artifact::finish_instantiation
diff --git a/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_serialization.png b/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_serialization.png new file mode 100644 index 0000000..cc83a82 Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_serialization.png differ diff --git a/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_serialization.svg b/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_serialization.svg new file mode 100644 index 0000000..1ce98d3 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/diagrams/Diagram_module_serialization.svg @@ -0,0 +1 @@ +
wasmer::Module::serialize
wasmer::Module::serialize
wasmer::Module::deserialize
wasmer::Module::deserialize
use a headless engine
for example
use a headless engine...
wasmer_engine::Artifact::serialize
wasmer_engine::Artifact::serialize
wasmer_engine::Artifact::deserialize
wasmer_engine::Artifact::deserialize
diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/c.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/c.svg new file mode 100644 index 0000000..3d37c89 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/c.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/cpp.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/cpp.svg new file mode 100644 index 0000000..db14db4 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/cpp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/crystal.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/crystal.svg new file mode 100644 index 0000000..2993192 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/crystal.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/csharp.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/csharp.svg new file mode 100644 index 0000000..aeba5f7 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/csharp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/d.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/d.svg new file mode 100644 index 0000000..f19e305 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/d.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/dart.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/dart.svg new file mode 100644 index 0000000..ab1636f --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/dart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/elixir.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/elixir.svg new file mode 100644 index 0000000..1976dfb --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/elixir.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/go.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/go.svg new file mode 100644 index 0000000..76f70ad --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/go.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/java.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/java.svg new file mode 100644 index 0000000..15083c7 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/java.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/js.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/js.svg new file mode 100644 index 0000000..39e20b3 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/js.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/julia.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/julia.svg new file mode 100644 index 0000000..3668e0c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/julia.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/lisp.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/lisp.svg new file mode 100644 index 0000000..0d58753 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/lisp.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/ocaml.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/ocaml.svg new file mode 100644 index 0000000..75550c6 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/ocaml.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/php.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/php.svg new file mode 100644 index 0000000..f88c8ac --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/php.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/postgres.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/postgres.svg new file mode 100644 index 0000000..e7b5622 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/postgres.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/python.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/python.svg new file mode 100644 index 0000000..3a223e7 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/python.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/r.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/r.svg new file mode 100644 index 0000000..73ef658 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/r.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/ruby.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/ruby.svg new file mode 100644 index 0000000..aad08a2 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/ruby.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/rust.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/rust.svg new file mode 100644 index 0000000..c88b70b --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/rust.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/swift.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/swift.svg new file mode 100644 index 0000000..6678248 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/swift.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/languages/vlang.svg b/arbitrator/tools/module_roots/wasmer/assets/languages/vlang.svg new file mode 100644 index 0000000..8407d7d --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/assets/languages/vlang.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/assets/logo-white.png b/arbitrator/tools/module_roots/wasmer/assets/logo-white.png new file mode 100644 index 0000000..8b2cbc2 Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/assets/logo-white.png differ diff --git a/arbitrator/tools/module_roots/wasmer/assets/logo.png b/arbitrator/tools/module_roots/wasmer/assets/logo.png new file mode 100644 index 0000000..e691e92 Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/assets/logo.png differ diff --git a/arbitrator/tools/module_roots/wasmer/benches/README.md b/arbitrator/tools/module_roots/wasmer/benches/README.md new file mode 100644 index 0000000..cb81186 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/benches/README.md @@ -0,0 +1,4 @@ +# Wasmer Benches + +This directory contains small, punctual benches. Other benchmarks are +landing somewhere else. We will update this section soon. diff --git a/arbitrator/tools/module_roots/wasmer/benches/static_and_dynamic_functions.rs b/arbitrator/tools/module_roots/wasmer/benches/static_and_dynamic_functions.rs new file mode 100644 index 0000000..4a8bf30 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/benches/static_and_dynamic_functions.rs @@ -0,0 +1,195 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +use wasmer::*; + +static BASIC_WAT: &str = r#"(module + (func $multiply (import "env" "multiply") (param i32 i32) (result i32)) + (func (export "add") (param i32 i32) (result i32) + (i32.add (local.get 0) + (local.get 1))) + (func (export "add20") (param i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32 + i32 i32 i32 i32 i32) (result i32) + (i32.add + (i32.add + (i32.add (i32.add (i32.add (local.get 0) (local.get 1)) + (i32.add (local.get 2) (local.get 3))) + (i32.add (i32.add (local.get 4) (local.get 5)) + (i32.add (local.get 6) (local.get 7)))) + (i32.add + (i32.add (i32.add (local.get 8) (local.get 9)) + (i32.add (local.get 10) (local.get 11))) + (i32.add (i32.add (local.get 12) (local.get 13)) + (i32.add (local.get 14) (local.get 15))))) + + (i32.add (i32.add (local.get 16) (local.get 17)) + (i32.add (local.get 18) (local.get 19)))) +) + (func (export "double_then_add") (param i32 i32) (result i32) + (i32.add (call $multiply (local.get 0) (i32.const 2)) + (call $multiply (local.get 1) (i32.const 2)))) +)"#; + +pub fn run_basic_static_function(store: &mut Store, compiler_name: &str, c: &mut Criterion) { + let module = Module::new(store, BASIC_WAT).unwrap(); + let import_object = imports! { + "env" => { + "multiply" => Function::new_typed(store, |a: i32, b: i32| a * b), + }, + }; + let instance = Instance::new(store, &module, &import_object).unwrap(); + let dyn_f: &Function = instance.exports.get("add").unwrap(); + let f: TypedFunction<(i32, i32), i32> = dyn_f.typed(store).unwrap(); + + c.bench_function(&format!("basic static func {}", compiler_name), |b| { + b.iter(|| { + let result = black_box(f.call(store, 4, 6).unwrap()); + assert_eq!(result, 10); + }) + }); + + let dyn_f_many: &Function = instance.exports.get("add20").unwrap(); + let f_many: TypedFunction< + ( + i32, + i32, + i32, + i32, + i32, + i32, + i32, + i32, + i32, + i32, + i32, + i32, + i32, + i32, + i32, + i32, + i32, + i32, + i32, + i32, + ), + i32, + > = dyn_f_many.typed(store).unwrap(); + c.bench_function( + &format!("basic static func with many args {}", compiler_name), + |b| { + b.iter(|| { + let result = black_box( + f_many + .call( + store, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, + ) + .unwrap(), + ); + assert_eq!(result, 210); + }) + }, + ); +} + +pub fn run_basic_dynamic_function(store: &mut Store, compiler_name: &str, c: &mut Criterion) { + let module = Module::new(store, BASIC_WAT).unwrap(); + let import_object = imports! { + "env" => { + "multiply" => Function::new_typed(store, |a: i32, b: i32| a * b), + }, + }; + let instance = Instance::new(store, &module, &import_object).unwrap(); + + let dyn_f: &Function = instance.exports.get("add").unwrap(); + c.bench_function(&format!("basic dynfunc {}", compiler_name), |b| { + b.iter(|| { + let dyn_result = black_box(dyn_f.call(store, &[Value::I32(4), Value::I32(6)]).unwrap()); + assert_eq!(dyn_result[0], Value::I32(10)); + }) + }); + + let dyn_f_many: &Function = instance.exports.get("add20").unwrap(); + c.bench_function( + &format!("basic dynfunc with many args {}", compiler_name), + |b| { + b.iter(|| { + let dyn_result = black_box( + dyn_f_many + .call( + store, + &[ + Value::I32(1), + Value::I32(2), + Value::I32(3), + Value::I32(4), + Value::I32(5), + Value::I32(6), + Value::I32(7), + Value::I32(8), + Value::I32(9), + Value::I32(10), + Value::I32(11), + Value::I32(12), + Value::I32(13), + Value::I32(14), + Value::I32(15), + Value::I32(16), + Value::I32(17), + Value::I32(18), + Value::I32(19), + Value::I32(20), + ], + ) + .unwrap(), + ); + assert_eq!(dyn_result[0], Value::I32(210)); + }) + }, + ); +} + +fn run_static_benchmarks(_c: &mut Criterion) { + #[cfg(feature = "llvm")] + { + let mut store = Store::new(wasmer_compiler_llvm::LLVM::new()); + run_basic_static_function(&mut store, "llvm", _c); + } + + #[cfg(feature = "cranelift")] + { + let mut store = Store::new(wasmer_compiler_cranelift::Cranelift::new()); + run_basic_static_function(&mut store, "cranelift", _c); + } + + #[cfg(feature = "singlepass")] + { + let mut store = Store::new(wasmer_compiler_singlepass::Singlepass::new()); + run_basic_static_function(&mut store, "singlepass", _c); + } +} + +fn run_dynamic_benchmarks(_c: &mut Criterion) { + #[cfg(feature = "llvm")] + { + let mut store = Store::new(wasmer_compiler_llvm::LLVM::new()); + run_basic_dynamic_function(&mut store, "llvm", _c); + } + + #[cfg(feature = "cranelift")] + { + let mut store = Store::new(wasmer_compiler_cranelift::Cranelift::new()); + run_basic_dynamic_function(&mut store, "cranelift", _c); + } + + #[cfg(feature = "singlepass")] + { + let mut store = Store::new(wasmer_compiler_singlepass::Singlepass::new()); + run_basic_dynamic_function(&mut store, "singlepass", _c); + } +} + +criterion_group!(benches, run_static_benchmarks, run_dynamic_benchmarks); + +criterion_main!(benches); diff --git a/arbitrator/tools/module_roots/wasmer/build.rs b/arbitrator/tools/module_roots/wasmer/build.rs new file mode 100644 index 0000000..d069178 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/build.rs @@ -0,0 +1,109 @@ +//! The logic that gets executed before building the binary and tests. +//! We use it to auto-generate the Wasm spectests for each of the +//! available compilers. +//! +//! Please try to keep this file as clean as possible. + +use std::env; +use std::fs; +use std::path::PathBuf; +use std::process::Command; +use test_generator::{ + test_directory, test_directory_module, wasi_processor, wast_processor, with_test_module, + Testsuite, +}; + +fn main() -> anyhow::Result<()> { + // As rerun-if-changed doesn't support globs, we use another crate + // to check changes in directories. + build_deps::rerun_if_changed_paths("tests/wasi-wast/wasi/unstable/*") + .expect("Can't get directory"); + build_deps::rerun_if_changed_paths("tests/wasi-wast/wasi/snapshot1/*") + .expect("Can't get directory"); + build_deps::rerun_if_changed_paths("tests/wasi-wast/wasi/nightly-2022-10-18/*") + .expect("Can't get directory"); + + let out_dir = PathBuf::from( + env::var_os("OUT_DIR").expect("The OUT_DIR environment variable must be set"), + ); + + // Spectests test generation + { + let mut spectests = Testsuite { + buffer: String::new(), + path: vec![], + }; + + with_test_module(&mut spectests, "spec", |spectests| { + let _spec_tests = test_directory(spectests, "tests/wast/spec", wast_processor)?; + test_directory_module( + spectests, + "tests/wast/spec/proposals/multi-value", + wast_processor, + )?; + test_directory_module(spectests, "tests/wast/spec/proposals/simd", wast_processor)?; + test_directory_module( + spectests, + "tests/wast/spec/proposals/threads", + wast_processor, + )?; + // test_directory_module(spectests, "tests/wast/spec/proposals/bulk-memory-operations", wast_processor)?; + Ok(()) + })?; + with_test_module(&mut spectests, "wasmer", |spectests| { + let _spec_tests = test_directory(spectests, "tests/wast/wasmer", wast_processor)?; + Ok(()) + })?; + + let spectests_output = out_dir.join("generated_spectests.rs"); + fs::write(&spectests_output, spectests.buffer)?; + + // Write out our auto-generated tests and opportunistically format them with + // `rustfmt` if it's installed. + // Note: We need drop because we don't want to run `unwrap` or `expect` as + // the command might fail, but we don't care about it's result. + drop(Command::new("rustfmt").arg(&spectests_output).status()); + } + + // Wasitest test generation + { + let mut wasitests = Testsuite { + buffer: String::new(), + path: vec![], + }; + + with_test_module(&mut wasitests, "wasitests", |wasitests| { + for wasi_version in &["unstable", "snapshot1", "nightly_2022_10_18"] { + with_test_module(wasitests, wasi_version, |wasitests| { + for (wasi_filesystem_test_name, wasi_filesystem_kind) in &[ + ("host_fs", "WasiFileSystemKind::Host"), + ("mem_fs", "WasiFileSystemKind::InMemory"), + ("tmp_fs", "WasiFileSystemKind::Tmp"), + ("passthru_fs", "WasiFileSystemKind::PassthruMemory"), + ("union_fs", "WasiFileSystemKind::UnionHostMemory"), + ("root_fs", "WasiFileSystemKind::RootFileSystemBuilder"), + ] { + with_test_module(wasitests, wasi_filesystem_test_name, |wasitests| { + test_directory( + wasitests, + format!("tests/wasi-wast/wasi/{}", wasi_version), + |out, path| wasi_processor(out, path, wasi_filesystem_kind), + ) + })?; + } + + Ok(()) + })?; + } + + Ok(()) + })?; + + let wasitests_output = out_dir.join("generated_wasitests.rs"); + fs::write(&wasitests_output, wasitests.buffer)?; + + drop(Command::new("rustfmt").arg(&wasitests_output).status()); + } + + Ok(()) +} diff --git a/arbitrator/tools/module_roots/wasmer/deny.toml b/arbitrator/tools/module_roots/wasmer/deny.toml new file mode 100644 index 0000000..949f572 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/deny.toml @@ -0,0 +1,222 @@ +# This template contains all of the possible sections and their default values + +# Note that all fields that take a lint level have these possible values: +# * deny - An error will be produced and the check will fail +# * warn - A warning will be produced, but the check will not fail +# * allow - No warning or error will be produced, though in some cases a note +# will be + +# The values provided in this template are the default values that will be used +# when any section or field is not specified in your own configuration + +# If 1 or more target triples (and optionally, target_features) are specified, +# only the specified targets will be checked when running `cargo deny check`. +# This means, if a particular package is only ever used as a target specific +# dependency, such as, for example, the `nix` crate only being used via the +# `target_family = "unix"` configuration, that only having windows targets in +# this list would mean the nix crate, as well as any of its exclusive +# dependencies not shared by any other crates, would be ignored, as the target +# list here is effectively saying which targets you are building for. +targets = [ + # The triple can be any string, but only the target triples built in to + # rustc (as of 1.40) can be checked against actual config expressions + #{ triple = "x86_64-unknown-linux-musl" }, + # You can also specify which target_features you promise are enabled for a + # particular target. target_features are currently not validated against + # the actual valid features supported by the target architecture. + #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, +] + +# This section is considered when running `cargo deny check advisories` +# More documentation for the advisories section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html +[advisories] +# The path where the advisory database is cloned/fetched into +db-path = "~/.cargo/advisory-db" +# The url(s) of the advisory databases to use +db-urls = ["https://github.com/rustsec/advisory-db"] +# The lint level for security vulnerabilities +vulnerability = "deny" +# The lint level for unmaintained crates +unmaintained = "warn" +# The lint level for crates that have been yanked from their source registry +yanked = "deny" +# The lint level for crates with security notices. Note that as of +# 2019-12-17 there are no security notice advisories in +# https://github.com/rustsec/advisory-db +notice = "warn" +# A list of advisory IDs to ignore. Note that ignored advisories will still +# output a note when they are encountered. +ignore = [ + #"RUSTSEC-0000-0000", + +] +# If this is true, then cargo deny will use the git executable to fetch advisory database. +# If this is false, then it uses a built-in git library. +# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support. +# See Git Authentication for more information about setting up git authentication. +git-fetch-with-cli = true +# Threshold for security vulnerabilities, any vulnerability with a CVSS score +# lower than the range specified will be ignored. Note that ignored advisories +# will still output a note when they are encountered. +# * None - CVSS Score 0.0 +# * Low - CVSS Score 0.1 - 3.9 +# * Medium - CVSS Score 4.0 - 6.9 +# * High - CVSS Score 7.0 - 8.9 +# * Critical - CVSS Score 9.0 - 10.0 +#severity-threshold = + +# This section is considered when running `cargo deny check licenses` +# More documentation for the licenses section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html +[licenses] +# The lint level for crates which do not have a detectable license +unlicensed = "deny" +# List of explicitly allowed licenses +# See https://spdx.org/licenses/ for list of possible licenses +# [possible values: any SPDX 3.7 short identifier (+ optional exception)]. +allow = [ + "MIT", + "Apache-2.0", + "Apache-2.0 WITH LLVM-exception", +] +# List of explicitly disallowed licenses +# See https://spdx.org/licenses/ for list of possible licenses +# [possible values: any SPDX 3.7 short identifier (+ optional exception)]. +deny = [ + "AGPL-1.0", + "AGPL-3.0", +] +# Lint level for licenses considered copyleft +copyleft = "warn" +# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses +# * both - The license will be approved if it is both OSI-approved *AND* FSF +# * either - The license will be approved if it is either OSI-approved *OR* FSF +# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF +# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved +# * neither - This predicate is ignored and the default lint level is used +allow-osi-fsf-free = "either" +# Lint level used when no other predicates are matched +# 1. License isn't in the allow or deny lists +# 2. License isn't copyleft +# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" +default = "deny" +# The confidence threshold for detecting a license from license text. +# The higher the value, the more closely the license text must be to the +# canonical license text of a valid SPDX license file. +# [possible values: any between 0.0 and 1.0]. +confidence-threshold = 0.8 +# Allow 1 or more licenses on a per-crate basis, so that particular licenses +# aren't accepted for every possible crate as with the normal allow list +exceptions = [ + # Each entry is the crate and version constraint, and its specific allow + # list +] + + + +# Some crates don't have (easily) machine readable licensing information, +# adding a clarification entry for it allows you to manually specify the +# licensing information +[[licenses.clarify]] +name = "webc" +version = "*" +expression = "BSL-1.0" +license-files = [ + { path = "LICENSE.txt", hash = 0xa2180a97 } +] + +[[licenses.clarify]] +name = "ring" +version = "*" +expression = "MIT AND ISC AND OpenSSL" +license-files = [ + # Each entry is a crate relative path, and the (opaque) hash of its contents + { path = "LICENSE", hash = 0xbd0eed23 } +] + +[licenses.private] +# If true, ignores workspace crates that aren't published, or are only +# published to private registries +ignore = false +# One or more private registries that you might publish crates to, if a crate +# is only published to private registries, and ignore is true, the crate will +# not have its license(s) checked +registries = [ + #"https://sekretz.com/registry +] + +# This section is considered when running `cargo deny check bans`. +# More documentation about the 'bans' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html +[bans] +# Lint level for when multiple versions of the same crate are detected +multiple-versions = "warn" +# Lint level for when a crate version requirement is `*` +wildcards = "allow" +# The graph highlighting used when creating dotgraphs for crates +# with multiple versions +# * lowest-version - The path to the lowest versioned duplicate is highlighted +# * simplest-path - The path to the version with the fewest edges is highlighted +# * all - Both lowest-version and simplest-path are used +highlight = "all" +# List of crates that are allowed. Use with care! +allow = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +# List of crates to deny +deny = [ + # Each entry the name of a crate and a version range. If version is + # not specified, all versions will be matched. + #{ name = "ansi_term", version = "=0.11.0" }, + # + # Wrapper crates can optionally be specified to allow the crate when it + # is a direct dependency of the otherwise banned crate + #{ name = "ansi_term", version = "=0.11.0", wrappers = [] }, +] +# Certain crates/versions that will be skipped when doing duplicate detection. +skip = [ + { name = "webc" }, # licence is not fetched correctly and end up as unknown + { name = "ahash", version = "=0.4.7" }, + { name = "hashbrown", version = "=0.9.1" }, + { name = "cfg-if", version = "=0.1.10" }, + { name = "gimli", version = "=0.25.0" }, + { name = "strsim", version = "=0.8.0" }, + { name = "semver", version = "=0.9.0" }, + { name = "semver", version = "=0.11.0" }, + { name = "semver-parser", version = "=0.7.0" }, + { name = "rustc_version", version = "=0.2.3" }, + { name = "itoa", version = "=0.4.8" }, + { name = "object", version = "=0.27.1" }, +] +# Similarly to `skip` allows you to skip certain crates during duplicate +# detection. Unlike skip, it also includes the entire tree of transitive +# dependencies starting at the specified crate, up to a certain depth, which is +# by default infinite +skip-tree = [ + #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, +] + +# This section is considered when running `cargo deny check sources`. +# More documentation about the 'sources' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html +[sources] +# Lint level for what to happen when a crate from a crate registry that is not +# in the allow list is encountered +unknown-registry = "deny" +# Lint level for what to happen when a crate from a git repository that is not +# in the allow list is encountered +unknown-git = "deny" +# List of URLs for allowed crate registries. Defaults to the crates.io index +# if not specified. If it is specified but empty, no registries are allowed. +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +# List of URLs for allowed Git repositories +allow-git = [] + +#[sources.allow-org] +# 1 or more github.com organizations to allow git sources for +#github = [""] +# 1 or more gitlab.com organizations to allow git sources for +#gitlab = [""] +# 1 or more bitbucket.org organizations to allow git sources for +#bitbucket = [""] diff --git a/arbitrator/tools/module_roots/wasmer/docs/README.md b/arbitrator/tools/module_roots/wasmer/docs/README.md new file mode 100644 index 0000000..18ac2c3 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/docs/README.md @@ -0,0 +1,24 @@ +# Wasmer Documentation + +Wasmer provides multiple documentations. Here are some pointers: + +* [The Wasmer runtime + `README.md`](https://github.com/wasmerio/wasmer/blob/master/README.md) + is a good start for the first steps, like installations, first runs etc., +* [The public documentation](https://docs.wasmer.io/) contains all the + documentation you need to learn about Wasmer and WebAssembly, +* [The Rust crates documentations](https://wasmerio.github.io/wasmer/) + contain all the documentations to use the `wasmer-*` Rust crates, + with many examples, +* [The collection of + examples](https://github.com/wasmerio/wasmer/blob/master/examples/README.md) + illustrates how to use Wasmer and its numerous features through very + commented examples, +* [Documentations for all embeddings/language + integrations](https://github.com/wasmerio/wasmer/blob/master/README.md): + the Wasmer runtime can be embedded in various languages or + environments, each embedding provides its own documentation, book + etc., +* [OS distro packaging + notes](https://github.com/wasmerio/wasmer/blob/master/PACKAGING.md) + contains notes about how to package Wasmer for OS distributions. diff --git a/arbitrator/tools/module_roots/wasmer/docs/RISCV.md b/arbitrator/tools/module_roots/wasmer/docs/RISCV.md new file mode 100755 index 0000000..0cd470a --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/docs/RISCV.md @@ -0,0 +1,12 @@ +# Current state of the RISCV support + +Only Cranelift and LLVM compiler are supported. +Singlepass can be done, but no ressources are allocated on this task for now. + +Both LLVM and Cranelift support are quite new, and so it is expected to have a few things not working well. + +LLVM code needs a hack to force the ABI to "lp64d", and some tested with funciton & float/double values are still not working correctly and have be disable for now. + +On Cranelift, SIMD is not supported as the CPU doesn't have official SIMD/Vector extension for now, and no Workaround is in place. + +Test have be conducted on actual hardware, with a Vision Fixe 2 board running Debian. Some previous tests have also be done on a Vison Five 1 running Fedora (with LLVM only). \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/docs/cn/README.md b/arbitrator/tools/module_roots/wasmer/docs/cn/README.md new file mode 100644 index 0000000..f2cf547 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/docs/cn/README.md @@ -0,0 +1,206 @@ +
+ + Wasmer logo + + +

+ + Build Status + + + License + + + Wasmer Docs + + + Slack channel + +

+ +

+ įŊ‘įĢ™ + â€ĸ + 文æĄŖ + â€ĸ + čŠå¤Š +

+ +
+ +
+ +[Wasmer](https://wasmer.io/) 提䞛åŸēäēŽ [WebAssembly](https://webassembly.org/) įš„čļ…čŊģ量įē§åŽšå™¨īŧŒå…ļ可äģĨ在äģģäŊ•åœ°æ–ščŋčĄŒīŧšäģŽæĄŒéĸ到äē‘、äģĨ及 IoT čŽžå¤‡īŧŒåšļ且也čƒŊåĩŒå…Ĩ到 [*äģģäŊ•įŧ–į¨‹č¯­č¨€*](https://github.com/wasmerio/wasmer#language-integrations) 中. + +> å…ļäģ–č¯­č¨€įš„ Readme: [🇩đŸ‡Ē Deutsch-åžˇčĒž](https://github.com/wasmerio/wasmer/blob/master/docs/de/README.md) â€ĸ [đŸ‡Ŧ🇧 English-č‹ąæ–‡](https://github.com/wasmerio/wasmer/blob/master/README.md) â€ĸ [đŸ‡Ē🇸 EspaÃąol-čĨŋį­į‰™č¯­](https://github.com/wasmerio/wasmer/blob/master/docs/es/README.md) â€ĸ [đŸ‡Ģ🇷 Français-æŗ•č¯­](https://github.com/wasmerio/wasmer/blob/master/docs/fr/README.md) â€ĸ [đŸ‡¯đŸ‡ĩ æ—ĨæœŦčĒž-æ—Ĩ文](https://github.com/wasmerio/wasmer/blob/master/docs/ja/README.md). + +## į‰šæ€§ + +* **åŋĢ速又厉全**. Wasmeråœ¨åŽŒå…¨æ˛™į›’化įš„įŽ¯åĸƒä¸­äģĨ“æŽĨčŋ‘æœŦæœē”įš„速åēĻčŋčĄŒ WebAssembly。 + +* **可插拔**. Wasmer 可äģĨ栚捎äŊ įš„éœ€æą‚æ”¯æŒä¸åŒįš„įŧ–č¯‘æĄ†æžļ (LLVMīŧŒCranelift ...). + +* **通į”¨įš„**. äŊ å¯äģĨ在**äģģäŊ•åšŗ台**(macOS, Linux and Windows) å’ŒčŠ¯į‰‡įģ„čŋčĄŒ Wasmer. + +* **įŦĻ合标准**. čŋčĄŒæ—ļ通čŋ‡äē†[厘斚WebAssemblyæĩ‹č¯•é›†](https://github.com/WebAssembly/testsuite) 支持[WASI](https://github.com/WebAssembly/WASI) 和[Emscripten](https://emscripten.org/). + +## åŋĢ速åŧ€å§‹ + +Wasmer 不需čĻåŽ‰čŖ…å…ļäģ–䞝čĩ–. äŊ å¯äģĨäŊŋį”¨äģĨ下厉čŖ…į¨‹åēčŋ›čĄŒåŽ‰čŖ…: + +```sh +curl https://get.wasmer.io -sSfL | sh +``` + +
+ äŊŋį”¨Powershell (Windows) +

+ +```powershell +iwr https://win.wasmer.io -useb | iex +``` + +

+
+ +> 有å…ŗ更多厉čŖ…选饚īŧŒč¯ˇå‚见 [wasmer-install](https://github.com/wasmerio/wasmer-install): Homebrew, Scoop, Cargo... + + +#### æ‰§čĄŒWebAssembly文äģļ + +厉čŖ…Wasmer䚋后īŧŒäŊ åˇ˛įģå‡†å¤‡åĨŊæ‰§čĄŒįŦŦ一ä¸ĒWebAssembly文äģļäē†! 🎉 + +您可äģĨ通čŋ‡čŋčĄŒ QuickJS åŧ€å§‹: [qjs.wasm](https://registry-cdn.wapm.io/contents/_/quickjs/0.0.3/build/qjs.wasm) + +```bash +$ wasmer qjs.wasm +QuickJS - Type "\h" for help +qjs > const i = 1 + 2; +qjs > console.log("hello " + i); +hello 3 +``` + +#### æŽĨ下æĨ是äŊ å¯äģĨ做įš„: + +- [在äŊ įš„Ruståē”į”¨į¨‹åēä¸­äŊŋį”¨Wasmer](https://docs.wasmer.io/integrations/rust) +- [在WAPM上发布Wasmį¨‹åēåŒ…](https://docs.wasmer.io/ecosystem/wapm/publishing-your-package) +- [阅č¯ģ有å…ŗWasmerįš„更多äŋĄæ¯](https://medium.com/wasmer/) + +## č¯­č¨€æ•´åˆ + +đŸ“Ļ Wasmer čŋčĄŒæ—ļčƒŊäģĨåē“įš„åŊĸåŧ**åĩŒå…Ĩ到不同įš„č¯­č¨€**īŧŒå› æ­¤äŊ å¯äģĨ在äģģäŊ•åœ°æ–šäŊŋį”¨WebAssembly. + +|   | č¯­č¨€ | į¨‹åēåŒ… | 文æĄŖ | +|-|-|-|-| +| ![Rust logo] | [**Rust**][Rust integration] | [`wasmer` Rust crate] | [文æĄŖ][rust docs] +| ![C logo] | [**C**][C integration] | [`wasm.h` header] | [文æĄŖ][c docs] | +| ![C++ logo] | [**C++**][C integration] | [`wasm.hh` header] | [文æĄŖ][c docs] | +| ![C# logo] | [**C#**][C# integration] | [`WasmerSharp` NuGet package] | [文æĄŖ][c# docs] | +| ![D logo] | [**D**][D integration] | [`wasmer` Dub package] | [文æĄŖ][d docs] | +| ![Python logo] | [**Python**][Python integration] | [`wasmer` PyPI package] | [文æĄŖ][python docs] | +| ![JS logo] | [**Javascript**][JS integration] | [`@wasmerio` NPM packages] | [文æĄŖ][js docs] | +| ![Go logo] | [**Go**][Go integration] | [`wasmer` Go package] | [文æĄŖ][go docs] | +| ![PHP logo] | [**PHP**][PHP integration] | [`wasm` PECL package] | [文æĄŖ][php docs] | +| ![Ruby logo] | [**Ruby**][Ruby integration] | [`wasmer` Ruby Gem] | [文æĄŖ][ruby docs] | +| ![Java logo] | [**Java**][Java integration] | [`wasmer/wasmer-jni` Bintray package] | [文æĄŖ][java docs] | +| ![Elixir logo] | [**Elixir**][Elixir integration] | [`wasmex` hex package] | [文æĄŖ][elixir docs] | +| ![R logo] | [**R**][R integration] | *æ˛Ąæœ‰åˇ˛å‘å¸ƒįš„čŊ¯äģļ包* | [文æĄŖ][r docs] | +| ![Postgres logo] | [**Postgres**][Postgres integration] | *æ˛Ąæœ‰åˇ˛å‘å¸ƒįš„čŊ¯äģļ包* | [文æĄŖ][postgres docs] | +| | [**Swift**][Swift integration] | *æ˛Ąæœ‰åˇ˛å‘å¸ƒįš„čŊ¯äģļ包* | | +| ![Zig logo] | [**Zig**][Zig integration] | *no published package* | | +| ![Ocaml logo] | [**OCaml**][OCaml integration] | [`wasmer` OCaml package] | | + +[👋 įŧēå°‘č¯­č¨€īŧŸ](https://github.com/wasmerio/wasmer/issues/new?assignees=&labels=%F0%9F%8E%89+enhancement&template=---feature-request.md&title=) + +[rust logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/rust.svg +[rust integration]: https://github.com/wasmerio/wasmer/tree/master/lib/api +[`wasmer` rust crate]: https://crates.io/crates/wasmer/ +[rust docs]: https://docs.rs/wasmer/ + +[c logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/c.svg +[c integration]: https://github.com/wasmerio/wasmer/tree/master/lib/c-api +[`wasmer.h` headers]: https://wasmerio.github.io/wasmer/c/ +[c docs]: https://docs.rs/wasmer-c-api/*/wasmer/wasm_c_api/index.html + +[c# logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/csharp.svg +[c# integration]: https://github.com/migueldeicaza/WasmerSharp +[`wasmersharp` nuget package]: https://www.nuget.org/packages/WasmerSharp/ +[c# docs]: https://migueldeicaza.github.io/WasmerSharp/ + +[d logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/d.svg +[d integration]: https://github.com/chances/wasmer-d +[`wasmer` Dub package]: https://code.dlang.org/packages/wasmer +[d docs]: https://chances.github.io/wasmer-d + +[python logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/python.svg +[python integration]: https://github.com/wasmerio/wasmer-python +[`wasmer` pypi package]: https://pypi.org/project/wasmer/ +[python docs]: https://github.com/wasmerio/wasmer-python#api-of-the-wasmer-extensionmodule + +[go logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/go.svg +[go integration]: https://github.com/wasmerio/wasmer-go +[`wasmer` go package]: https://pkg.go.dev/github.com/wasmerio/wasmer-go/wasmer +[go docs]: https://pkg.go.dev/github.com/wasmerio/wasmer-go/wasmer?tab=doc + +[php logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/php.svg +[php integration]: https://wasmerio.github.io/wasmer-php/ +[`wasm` pecl package]: https://pecl.php.net/package/wasm +[php docs]: https://wasmerio.github.io/wasmer-php/wasm/ + +[js logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/js.svg +[js integration]: https://github.com/wasmerio/wasmer-js +[`@wasmerio` npm packages]: https://www.npmjs.com/org/wasmer +[js docs]: https://docs.wasmer.io/integrations/js/reference-api + +[ruby logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/ruby.svg +[ruby integration]: https://github.com/wasmerio/wasmer-ruby +[`wasmer` ruby gem]: https://rubygems.org/gems/wasmer +[ruby docs]: https://www.rubydoc.info/gems/wasmer/ + +[java logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/java.svg +[java integration]: https://github.com/wasmerio/wasmer-java +[`wasmer/wasmer-jni` bintray package]: https://bintray.com/wasmer/wasmer-jni/wasmer-jni +[java docs]: https://github.com/wasmerio/wasmer-java/#api-of-the-wasmer-library + +[elixir logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/elixir.svg +[elixir integration]: https://github.com/tessi/wasmex +[elixir docs]: https://hexdocs.pm/wasmex/api-reference.html +[`wasmex` hex package]: https://hex.pm/packages/wasmex + +[r logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/r.svg +[r integration]: https://github.com/dirkschumacher/wasmr +[r docs]: https://github.com/dirkschumacher/wasmr#example + +[postgres logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/postgres.svg +[postgres integration]: https://github.com/wasmerio/wasmer-postgres +[postgres docs]: https://github.com/wasmerio/wasmer-postgres#usage--documentation + +[swift integration]: https://github.com/AlwaysRightInstitute/SwiftyWasmer + +[zig logo]: https://raw.githubusercontent.com/ziglang/logo/master/zig-favicon.png +[zig integration]: https://github.com/zigwasm/wasmer-zig + +[OCaml logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/ocaml.svg +[OCaml integration]: https://github.com/wasmerio/wasmer-ocaml +[`wasmer` OCaml package]: https://opam.ocaml.org/packages/wasmer/ + +## č´ĄįŒŽ + +**我äģŦæŦĸčŋŽäģģäŊ•åŊĸåŧįš„č´ĄįŒŽīŧŒå°¤å…ļ是æĨč‡Ēį¤žåŒē新成员įš„č´ĄįŒŽ** 💜 + +äŊ å¯äģĨ在[我äģŦįš„å‡ēč‰˛æ–‡æĄŖ](https://docs.wasmer.io/developers/build-from-source) 中å­Ļäš åĻ‚äŊ•æž„åģē Wasmer čŋčĄŒæ—ļ! + +### æĩ‹č¯• + +æƒŗčĻæĩ‹č¯•å—? [å‚č€ƒ Wasmer 文æĄŖ](https://docs.wasmer.io/ecosystem/wasmer/building-from-source/testing). + +## į¤žåŒē + +Wasmer æ‹Ĩ有一ä¸Ēį”ąå‡ē色įš„åŧ€å‘äēēå‘˜å’Œč´ĄįŒŽč€…įģ„成įš„į¤žåŒē。 æŦĸčŋŽäŊ īŧŒč¯ˇåŠ å…Ĩ我äģŦ! 👋 + +### éĸ‘道 + +- [Slack](https://slack.wasmer.io/) +- [Twitter](https://twitter.com/wasmerio) +- [Facebook](https://www.facebook.com/wasmerio) +- [Email](mailto:hello@wasmer.io) diff --git a/arbitrator/tools/module_roots/wasmer/docs/de/README.md b/arbitrator/tools/module_roots/wasmer/docs/de/README.md new file mode 100644 index 0000000..da66f04 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/docs/de/README.md @@ -0,0 +1,230 @@ + + +
+ +Wasmer ist eine _schnelle_ und _sichere_ [**WebAssembly**](https://webassembly.org) Runtime, die das AusfÃŧhren von +_schlanken Containern_ Ãŧberall ermÃļglicht: auf dem *Desktop* in der *Cloud*, so wie auf *Edge* und *IoT* Geräten. + +> _Die README ist auch in folgenden Sprachen verfÃŧgbar: +[🇨đŸ‡ŗ 中文-Chinesisch](https://github.com/wasmerio/wasmer/blob/master/docs/cn/README.md) â€ĸ +[đŸ‡Ŧ🇧 English-Englisch](https://github.com/wasmerio/wasmer/blob/master/README.md) â€ĸ +[đŸ‡Ē🇸 EspaÃąol-Spanisch](https://github.com/wasmerio/wasmer/blob/master/docs/es/README.md) â€ĸ +[đŸ‡Ģ🇷 Français-FranzÃļsisch](https://github.com/wasmerio/wasmer/blob/master/docs/fr/README.md) â€ĸ +[đŸ‡¯đŸ‡ĩ æ—ĨæœŦčĒž-Japanisch](https://github.com/wasmerio/wasmer/blob/master/docs/ja/README.md)_. + +### Leistungsmerkmale + +* Standardmäßig sicher. Kein Datei-, Netzwerk- oder Umgebungszugriff, sofern nicht explizit aktiviert. +* UnterstÃŧtzt [WASI](https://github.com/WebAssembly/WASI) und [Emscripten](https://emscripten.org/) standardmäßig. +* Schnell. FÃŧhrt WebAssembly in nahezu nativer Geschwindigkeit aus. +* Einbettbar in [mehrere Programmiersprachen](https://github.com/wasmerio/wasmer/#-language-integrations) +* Kompatibel mit den neuesten Empfehlungen fÃŧr WebAssembly (SIMD, Referenztypen, Threads, ...) + +### Installation + +Wasmer CLI wird als eine einzige ausfÃŧhrbare Datei ohne Abhängigkeiten ausgeliefert. + +```sh +curl https://get.wasmer.io -sSfL | sh +``` + + +
+ Weitere InstallationsmÃļglichkeiten (Powershell, Brew, Cargo, ...) + + _Wasmer kann Ãŧber verschiedene Paketmanager installiert werden. Wählen Sie den fÃŧr Ihre Umgebung am besten geeigneten aus:_ + + * Powershell (Windows) + ```powershell + iwr https://win.wasmer.io -useb | iex + ``` + + * Homebrew (macOS, Linux) + + ```sh + brew install wasmer + ``` + + * Scoop (Windows) + + ```sh + scoop install wasmer + ``` + + * Chocolatey (Windows) + + ```sh + choco install wasmer + ``` + + * Cargo + + _Note: All the available + features are described in the [`wasmer-cli` + crate docs](https://github.com/wasmerio/wasmer/tree/master/lib/cli/README.md)_ + + ```sh + cargo install wasmer-cli + ``` + + > Suchen Sie nach weiteren InstallationsmÃļglichkeiten? Im [`wasmer-install` + Repository](https://github.com/wasmerio/wasmer-install) kÃļnnen Si mehr erfahren! +
+ +### Schnellstart + +Sie kÃļnnen beginnen, +[QuickJS](https://github.com/bellard/quickjs/) auszufÃŧhren, eine kleine und +einbettbare Javascript Engine, die als WebAssembly Modul kompiliert ist: ([`qjs.wasm`](https://registry-cdn.wapm.io/contents/_/quickjs/0.0.3/build/qjs.wasm)): + +```bash +$ wasmer qjs.wasm +QuickJS - Type "\h" for help +qjs > const i = 1 + 2; +qjs > console.log("hello " + i); +hello 3 +``` + +#### Folgendes kÃļnnen Sie als nächstes tun: + +- [Wasmer fÃŧr eine Rust Anwendung nutzen](https://docs.wasmer.io/integrations/rust) +- [Ein asm Paket auf WAPM verÃļffentlichen](https://docs.wasmer.io/ecosystem/wapm/publishing-your-package) +- [Mehr zu Wasmer lesen](https://medium.com/wasmer/) + +## đŸ“Ļ UnterstÃŧtzte Sprachen + +Die Wasmer-Laufzeit kann als Bibliothek **eingebettet in verschiedenen +Sprachen** verwendet werden, so dass Sie WebAssembly _Ãŧberall_ einsetzen kÃļnnen. + +| | Sprache | Paket | Dokumentation | +|-|-|-|-| +| ![Rust logo] | [**Rust**][Rust Integration] | [`wasmer` Rust crate] | [Lernen][rust docs] +| ![C logo] | [**C/C++**][C Integration] | [`wasmer.h` header] | [Lernen][c docs] | +| ![C# logo] | [**C#**][C# Integration] | [`WasmerSharp` NuGet Paket] | [Lernen][c# docs] | +| ![D logo] | [**D**][D Integration] | [`wasmer` Dub Paket] | [Lernen][d docs] | +| ![Python logo] | [**Python**][Python Integration] | [`wasmer` PyPI Paket] | [Lernen][python docs] | +| ![JS logo] | [**Javascript**][JS Integration] | [`@wasmerio` NPM Paket] | [Lernen][js docs] | +| ![Go logo] | [**Go**][Go Integration] | [`wasmer` Go Paket] | [Lernen][go docs] | +| ![PHP logo] | [**PHP**][PHP Integration] | [`wasm` PECL Paket] | [Lernen][php docs] | +| ![Ruby logo] | [**Ruby**][Ruby Integration] | [`wasmer` Ruby Gem] | [Lernen][ruby docs] | +| ![Java logo] | [**Java**][Java Integration] | [`wasmer/wasmer-jni` Bintray Paket] | [Lernen][java docs] | +| ![Elixir logo] | [**Elixir**][Elixir Integration] | [`wasmex` hex Paket] | [Lernen][elixir docs] | +| ![R logo] | [**R**][R Integration] | *kein Paket verÃļffentlicht* | [Lernen][r docs] | +| ![Postgres logo] | [**Postgres**][Postgres Integration] | *kein Paket verÃļffentlicht* | [Lernen][postgres docs] | +| | [**Swift**][Swift Integration] | *kein Paket verÃļffentlicht* | | +| ![Zig logo] | [**Zig**][Zig Integration] | *kein Paket verÃļffentlicht* | | +| ![Dart logo] | [**Dart**][Dart Integration] | [`wasm` pub Paket] | | +| ![Ocaml logo] | [**OCaml**][OCaml integration] | [`wasmer` OCaml package] | | + +[👋  Fehlt eine Sprache?](https://github.com/wasmerio/wasmer/issues/new?assignees=&labels=%F0%9F%8E%89+enhancement&template=---feature-request.md&title=) + +[rust logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/rust.svg +[rust integration]: https://github.com/wasmerio/wasmer/tree/master/lib/api +[`wasmer` rust crate]: https://crates.io/crates/wasmer/ +[rust docs]: https://wasmerio.github.io/wasmer/crates/wasmer + +[c logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/c.svg +[c integration]: https://github.com/wasmerio/wasmer/tree/master/lib/c-api +[`wasmer.h` header]: https://github.com/wasmerio/wasmer/blob/master/lib/c-api/wasmer.h +[c docs]: https://wasmerio.github.io/wasmer/crates/wasmer_c_api + +[c# logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/csharp.svg +[c# integration]: https://github.com/migueldeicaza/WasmerSharp +[`wasmersharp` nuget package]: https://www.nuget.org/packages/WasmerSharp/ +[c# docs]: https://migueldeicaza.github.io/WasmerSharp/ + +[d logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/d.svg +[d integration]: https://github.com/chances/wasmer-d +[`wasmer` Dub package]: https://code.dlang.org/packages/wasmer +[d docs]: https://chances.github.io/wasmer-d + +[python logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/python.svg +[python integration]: https://github.com/wasmerio/wasmer-python +[`wasmer` pypi package]: https://pypi.org/project/wasmer/ +[python docs]: https://wasmerio.github.io/wasmer-python/api/wasmer/ + +[go logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/go.svg +[go integration]: https://github.com/wasmerio/wasmer-go +[`wasmer` go package]: https://pkg.go.dev/github.com/wasmerio/wasmer-go/wasmer +[go docs]: https://pkg.go.dev/github.com/wasmerio/wasmer-go/wasmer?tab=doc + +[php logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/php.svg +[php integration]: https://github.com/wasmerio/wasmer-php +[`wasm` pecl package]: https://pecl.php.net/package/wasm +[php docs]: https://wasmerio.github.io/wasmer-php/wasm/ + +[js logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/js.svg +[js integration]: https://github.com/wasmerio/wasmer-js +[`@wasmerio` npm packages]: https://www.npmjs.com/org/wasmer +[js docs]: https://docs.wasmer.io/integrations/js/reference-api + +[ruby logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/ruby.svg +[ruby integration]: https://github.com/wasmerio/wasmer-ruby +[`wasmer` ruby gem]: https://rubygems.org/gems/wasmer +[ruby docs]: https://wasmerio.github.io/wasmer-ruby/wasmer_ruby/index.html + +[java logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/java.svg +[java integration]: https://github.com/wasmerio/wasmer-java +[`wasmer/wasmer-jni` bintray package]: https://bintray.com/wasmer/wasmer-jni/wasmer-jni +[java docs]: https://github.com/wasmerio/wasmer-java/#api-of-the-wasmer-library + +[elixir logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/elixir.svg +[elixir integration]: https://github.com/tessi/wasmex +[elixir docs]: https://hexdocs.pm/wasmex/api-reference.html +[`wasmex` hex package]: https://hex.pm/packages/wasmex + +[r logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/r.svg +[r integration]: https://github.com/dirkschumacher/wasmr +[r docs]: https://github.com/dirkschumacher/wasmr#example + +[postgres logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/postgres.svg +[postgres integration]: https://github.com/wasmerio/wasmer-postgres +[postgres docs]: https://github.com/wasmerio/wasmer-postgres#usage--documentation + +[swift integration]: https://github.com/AlwaysRightInstitute/SwiftyWasmer + +[zig logo]: https://raw.githubusercontent.com/ziglang/logo/master/zig-favicon.png +[zig integration]: https://github.com/zigwasm/wasmer-zig + +[dart logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/dart.svg +[dart integration]: https://github.com/dart-lang/wasm +[`wasm` pub package]: https://pub.dev/packages/wasm + +[OCaml logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/ocaml.svg +[OCaml integration]: https://github.com/wasmerio/wasmer-ocaml +[`wasmer` OCaml package]: https://opam.ocaml.org/packages/wasmer/ + +## UnterstÃŧtzen + +Wir sind dankbar fÃŧr Ihre Hilfe! 💜 + +Lesen Sie in unserer Dokumentation nach, wie man [Wasmer aus dem +Quellcode kompiliert](https://docs.wasmer.io/ecosystem/wasmer/building-from-source) oder [testen Sie Änderungen](https://docs.wasmer.io/ecosystem/wasmer/building-from-source/testing). + +## Community + +Wasmer hat eine wunderbare Community von Entwicklern und Mitwirkenden. Sie sind herzlich willkommen, bitte machen Sie mit! 👋 + +- [Wasmer Community auf Slack](https://slack.wasmer.io/) +- [Wasmer auf Twitter](https://twitter.com/wasmerio) +- [Wasmer auf Facebook](https://www.facebook.com/wasmerio) +- [Email](mailto:hello@wasmer.io) diff --git a/arbitrator/tools/module_roots/wasmer/docs/deps_dedup.dot b/arbitrator/tools/module_roots/wasmer/docs/deps_dedup.dot new file mode 100644 index 0000000..d86c4ef --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/docs/deps_dedup.dot @@ -0,0 +1,72 @@ +digraph dependencies { + newrank=true; + + n0 [label="wasmer", color=orange]; + n1 [label="wasmer-compiler", color=orange]; + n5 [label="wasmer-engine", color=orange]; + n6 [label="wasmer-engine-universal", color=orange]; + n8 [label="wasmer-types", color=orange]; + n9 [label="wasmer-vm", color=orange]; + n10 [label="wasmer-c-api", color=orange]; + n11 [label="wasmer-emscripten", color=orange]; + n12 [label="wasmer-wasi", color=orange]; + n13 [label="wasmer-cache", color=orange]; + n14 [label="wasmer-cli", color=orange]; + + + subgraph cluster_compiler { + label="Compilers"; + color=brown; + + n2 [label="wasmer-compiler-cranelift", color=orange]; + n3 [label="wasmer-compiler-llvm", color=orange]; + n4 [label="wasmer-compiler-singlepass", color=orange]; + } + + subgraph cluster_engine { + label="Engines"; + color=brown; + + n6 [label="wasmer-engine-universal", color=orange]; + } + + { + rank=same; + n2; + n3; + n4; + n6; + n7; + } + + + subgraph cluster_abi { + label="Provided ABIs"; + color=brown; + + n12 [label="wasmer-wasi", color=orange]; + n11 [label="wasmer-emscripten", color=orange]; + } + + n14 -> n13 [color=orange, style=dashed]; + n14 -> n12 [color=orange, style=dashed]; + n14 -> n11 [color=orange, style=dashed]; + n13 -> n0 [color=orange, style=dashed]; + n10 -> n11 [color=orange, style=dashed]; + n10 -> n12 [color=orange, style=dashed]; + n11 -> n0 [color=orange, style=dashed]; + n12 -> n0 [color=orange, style=dashed]; + n0 -> n2 [color=orange, style=dashed]; + n0 -> n3 [color=orange, style=dashed]; + n0 -> n4 [color=orange, style=dashed]; + n0 -> n6 [color=orange, style=dashed]; + n0 -> n7 [color=orange, style=dashed]; + n2 -> n1 [color=orange, style=dashed]; + n3 -> n1 [color=orange, style=dashed]; + n4 -> n1 [color=orange, style=dashed]; + n6 -> n5 [color=orange, style=dashed]; + n7 -> n5 [color=orange, style=dashed]; + n5 -> n1 [color=orange, style=dashed]; + n1 -> n9 [color=orange, style=dashed]; + n9 -> n8 [color=orange, style=dashed]; +} diff --git a/arbitrator/tools/module_roots/wasmer/docs/deps_dedup.svg b/arbitrator/tools/module_roots/wasmer/docs/deps_dedup.svg new file mode 100644 index 0000000..e05f2b1 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/docs/deps_dedup.svg @@ -0,0 +1,232 @@ + + + + + + +dependencies + + +cluster_compiler + +Compilers + + +cluster_engine + +Engines + + +cluster_abi + +Provided ABIs + + + +n0 + +wasmer + + + +n6 + +wasmer-engine-universal + + + +n0->n6 + + + + + +n2 + +wasmer-compiler-cranelift + + + +n0->n2 + + + + + +n3 + +wasmer-compiler-llvm + + + +n0->n3 + + + + + +n4 + +wasmer-compiler-singlepass + + + +n0->n4 + + + + + +n1 + +wasmer-compiler + + + +n9 + +wasmer-vm + + + +n1->n9 + + + + + +n5 + +wasmer-engine + + + +n5->n1 + + + + + +n6->n5 + + + + + +n7->n5 + + + + + +n8 + +wasmer-types + + + +n9->n8 + + + + + +n10 + +wasmer-c-api + + + +n11 + +wasmer-emscripten + + + +n10->n11 + + + + + +n12 + +wasmer-wasi + + + +n10->n12 + + + + + +n11->n0 + + + + + +n12->n0 + + + + + +n13 + +wasmer-cache + + + +n13->n0 + + + + + +n14 + +wasmer-cli + + + +n14->n11 + + + + + +n14->n12 + + + + + +n14->n13 + + + + + +n2->n1 + + + + + +n3->n1 + + + + + +n4->n1 + + + + + diff --git a/arbitrator/tools/module_roots/wasmer/docs/dev/create-exe.md b/arbitrator/tools/module_roots/wasmer/docs/dev/create-exe.md new file mode 100644 index 0000000..293b0a2 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/docs/dev/create-exe.md @@ -0,0 +1,114 @@ +# wasmer create-exe + +## Motivation and Goal + +The goal of create-exe is to create easily runnable executables on every +operating system. + +In order to speed up the compilation process, the compilation and the create-exe +steps are split into three separate functions: `create-obj`, `create-exe` and `gen-c-header`. + +By default, running `create-exe` is relatively simple: + +```sh +wasmer create-exe myfile.wasm -o myfile.exe +./myfile.exe +``` + +When running `create-exe` on a wapm package that contains more than one .wasm file, +the resulting executable requires a `--command` or `-c` flag to select which module to start: + +```sh +wasmer create-exe wabt@0.1.0.wasmer -o wabt.exe +./wabt.exe +No --command given, available commands are: + + wabt + wasm-interp + wasm-strip + wasm-validate + wasm2wat + wast2json + wat2wasm + +./wabt.exe -c wasm2wat --version +1.0.37 (git~v1.0.37) +``` + +## create-obj + +Some compilers have different advantages over other compilers, for example, the `-llvm` +compiler emits the best code, but is the slowest to compile, the `-singlepass` compiler +emits slow code, but is very fast to compile. + +In order to cache compiled object and re-use them for recompilation, `wasmer create-obj` +exists to cache the output of the `wasm -> obj` compilation + +```sh +# Requires wasmer >= 3.2.0-alpha.1 +wasmer create-obj myfile.wasm -llvm -o myfile.o +# Run create-exe with the cached object file +wasmer create-exe myfile.wasm --precompiled-atom=myfile:myfile.o -o myfile.exe +``` + +The WebAssembly module (atom) name specified in the `--precompiled-atom` flag is the `.wasm` filename +without the extension or the module name in a multi-webassembly wapm package. + +## Multi-command executables + +When compiling multiple `.wasm` atoms into one executable, it could happen that +the function names generated by wasmer would clash with each other (ex. +`wasmer_function_1` from `object1.o` and `wasmer_function_1` from `object2.o`). + +Therefore, wasmer inserts a prefix for these functions, which is apparent when running +`wasmer gen-c-header`: + +```c +wasmer gen-c-header myfile.wasm -o myfile.h +cat myfile.h + +// ... + +// Compiled Wasm function pointers ordered by function index: the order they +// appeared in in the Wasm module. +extern void wasmer_function_6f62a6bc5c8f8e3e12a54e2ecbc5674ccfe1c75f91d8e4dd6ebb3fec422a4d6c_0(void); +extern void wasmer_function_6f62a6bc5c8f8e3e12a54e2ecbc5674ccfe1c75f91d8e4dd6ebb3fec422a4d6c_1(void); +extern void wasmer_function_6f62a6bc5c8f8e3e12a54e2ecbc5674ccfe1c75f91d8e4dd6ebb3fec422a4d6c_2(void); + +// ... +``` + +By default, this "prefix" is the Sha256 hash of the input `.wasm` file and can be changed with the +`--prefix` flag on both `gen-c-header` and `create-obj`: + +```c +wasmer gen-c-header myfile.wasm -o myfile.h --prefix abc123 +cat myfile.h + +// ... + +// Compiled Wasm function pointers ordered by function index: the order they +// appeared in in the Wasm module. +extern void wasmer_function_abc123_0(void); +extern void wasmer_function_abc123_1(void); +extern void wasmer_function_abc123_2(void); + +// ... +``` + +In order to instruct `create-exe` to use these object files, we can use the `--precompiled-atom` syntax +with `ATOM_NAME:PREFIX:PATH_TO_FILE` where `PREFIX` is optional and defaults to the Sha256 hash of the wasm file. + +```sh +wasmer create-obj myfile.wasm -o myfile.o --prefix abc123 +wasmer create-exe myfile.wasm -o myfile.exe --precompiled-atom myfile:abc123:myfile.o +``` + +The speedup is apparent when timing this command against the non-cached version: + +``` +time wasmer create-exe myfile.wasm -o myfile.exe +1,53s user 0,24s system 157% cpu 1,124 total +time wasmer create-exe myfile.wasm -o myfile.exe --precompiled-atom myfile:myfile.o +0,72s user 0,19s system 105% cpu 0,856 total +``` diff --git a/arbitrator/tools/module_roots/wasmer/docs/dev/release.md b/arbitrator/tools/module_roots/wasmer/docs/dev/release.md new file mode 100644 index 0000000..025ca5e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/docs/dev/release.md @@ -0,0 +1,85 @@ +# Release Process + +The release process of wasmer is mostly automated, including generating the CHANGELOG, +tagging the release and starting the `build.yaml` action + +In the `/scripts` folder, you will see three files: + +- `update-version.py`: iterates through all `Cargo.toml` files and bumps the version number +according to `PREVIOUS_VERSION` and `NEXT_VERSION` +- `publish.py`: resolves the dependency order and publishes all crates to crates.io +- `make-release.py`: creates a new pull request from the current master branch, generates the +CHANGELOG, waits until all checks have passed and the release PR is merged, then starts the +GitHub action to trigger the actual release on GitHub. + +In theory, all you need to do to create a new release is to look that master is green, then +run `python3 scripts/make-release.py 3.2.0` - in practice the script can sometimes lock up or +break due to unexpected delays for example it takes a couple of seconds between a pull request +being merged and master being updated. Therefore it's best to run each step individually and +make sure that every step finishes properly. + +```sh +# required before starting +gh login + +# Script will create a release PR (release-3.2.0) and loop until the +# release PR is merged into master (after checks are green) +python3 scripts/make-release.py 3.2.0 + +# After the release PR is merged, the build.yml workflow should be +# triggered automatically - if it isn't, trigger it manually +git checkout master +git tag v3.2.0 && git push origin v3.2.0 +gh workflow run build.yml --ref v3.2.0 --field release=v3.2.0 + +# After the release is done on GitHub, run the script again +# to update the release notes +python3 scripts/make-release.py 3.2.0 + +# Once the release on GitHub is properly done and verified that all +# artifacts exist, checkout the tag and run the publishing to crates.io +git checkout v3.2.0 +python3 scripts/publish.py publish +``` + +After the GitHub release (first command), the crates need to be +published to crates.io - the order is important because if anything +goes wrong in the first command or a release needs to be amended +because of last-minute fixes, we can still revert the GitHub release, +but publishing on crates.io is final because we can't yank crates +(this has caused a lot of version-renumbering issues in the past). + +## Issues to watch out for + +There are a couple of problems with the scripts that you should watch out for: + +- On the release pull request, the CHANGELOG might be generated incorrectly or with wrong line endings +- If the script fails, there should be an audible message (implemented using the `say` command), so that you + can leave the script running in the background and get notified if anything goes wrong. +- The script might not trigger the `build.yml` action, in some cases it has to be run manually +- Publishing to crates.io might fail because of new crates that have to be published manually. + - It is important to adjust the `SETTINGS` in the `publish.py` script if some crates need default-features + to be enabled when publishing + - crates that were never published before need to usually be published for the first time + by `cd lib/crate && cargo publish` +- After publishing new crates, check that the crate ownership is set to `github:wasmerio:wasmer-core`. +- The CHANGELOG is generated from the pull request titles since the last release. Sometimes these titles need + to be fixed up to make any sense for a reader +- The release notes should just highlight the most important changes for a release, not dump everything. +- The following files should be released (TODO: more consistent naming schema): + - wasmer-darwin-amd64.tar.gz + - wasmer-darwin-arm64.tar.gz + - wasmer-linux-aarch64.tar.gz + - wasmer-linux-amd64.tar.gz + - wasmer-linux-musl-amd64.tar.gz + - wasmer-windows-amd64.tar.gz + - wasmer-windows-gnu64.tar.gz + - wasmer-windows.exe + +## Videos + +- [Creating the release pull request](https://www.youtube.com/watch?v=RMPTT-rnykA) +- [Triggering the build.yml action manually](https://www.youtube.com/watch?v=7mF0nlfpQfA) + - Note that the version should always be tagged with a "v" tag + - The commit to tag for the version should be the merge commit of the release PR +- [Publishing to crates.io](https://www.youtube.com/watch?v=uLdxIr6YwuY) diff --git a/arbitrator/tools/module_roots/wasmer/docs/en/examples-coredump.md b/arbitrator/tools/module_roots/wasmer/docs/en/examples-coredump.md new file mode 100644 index 0000000..19a7754 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/docs/en/examples-coredump.md @@ -0,0 +1,62 @@ +# Using Wasm coredump + +The following steps describe how to debug using Wasm coredump in Wasmer: + +1. Compile your WebAssembly with debug info enabled; for example: + + ```sh + $ rustc foo.rs --target=wasm32-wasi -C debuginfo=2 + ``` + +
+ foo.rs + + fn c(v: usize) { + a(v - 3); + } + + fn b(v: usize) { + c(v - 3); + } + + fn a(v: usize) { + b(v - 3); + } + + pub fn main() { + a(10); + } +
+ +2. Run with Wasmer and Wasm coredump enabled: + + ```sh + $ wasmer --coredump-on-trap=/tmp/coredump foo.wasm + + thread 'main' panicked at 'attempt to subtract with overflow', foo.rs:10:7 + note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + Error: failed to run main module `foo.wasm` + + Caused by: + 0: Core dumped at /tmp/coredump + 1: failed to invoke command default + 2: error while executing at wasm backtrace: + ... + ``` + +3. Use [wasmgdb] to debug: + ```sh + $ wasmgdb foo.wasm /tmp/coredump + + wasmgdb> bt + ... + #13 000175 as panic () at library/core/src/panicking.rs + #12 000010 as a (v=???) at /path/to/foo.rs + #11 000009 as c (v=???) at /path/to/foo.rs + #10 000011 as b (v=???) at /path/to/foo.rs + #9 000010 as a (v=???) at /path/to/foo.rs + #8 000012 as main () at /path/to/foo.rs + ... + ``` + +[wasmgdb]: https://crates.io/crates/wasmgdb diff --git a/arbitrator/tools/module_roots/wasmer/docs/es/README.md b/arbitrator/tools/module_roots/wasmer/docs/es/README.md new file mode 100644 index 0000000..34fbafc --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/docs/es/README.md @@ -0,0 +1,200 @@ +
+ + Wasmer logo + + +

+ + Build Status + + + License + + + Slack channel + +

+ +

+ Web + â€ĸ + DocumentaciÃŗn + â€ĸ + Chat +

+ +
+ +
+ +[Wasmer](https://wasmer.io/) hace posible tener contenedores ultraligeros basados en [WebAssembly](https://webassembly.org/) que pueden ser ejecutados en cualquier sitio: desde tu ordenador hasta la nube y dispositivos de IoT, ademÃĄs de poder ser ejecutados [*en cualquier lenguaje de programaciÃŗn*](https://github.com/wasmerio/wasmer#language-integrations). + +> This README is also available in: [🇩đŸ‡Ē Deutsch-AlemÃĄn](https://github.com/wasmerio/wasmer/blob/master/docs/de/README.md) â€ĸ [đŸ‡Ŧ🇧 English-InglÊs](https://github.com/wasmerio/wasmer/blob/master/README.md) â€ĸ [đŸ‡Ģ🇷 Français-FrancÊs](https://github.com/wasmerio/wasmer/blob/master/docs/fr/README.md) â€ĸ [🇨đŸ‡ŗ 中文-Chino](https://github.com/wasmerio/wasmer/blob/master/docs/cn/README.md) â€ĸ [đŸ‡¯đŸ‡ĩ æ—ĨæœŦčĒž-japonÊs](https://github.com/wasmerio/wasmer/blob/master/docs/ja/README.md). + +## Funcionalidades + +* **RÃĄpido y Seguro**. Wasmer ejecuta WebAssembly a velocidades *nativas* en un entorno completamente protegido. + +* **Extendible**. Wasmer soporta diferentes mÊtodos de compilaciÃŗn dependiendo de tus necesidades (LLVM, Cranelift...). + +* **Universal**. Puedes ejecutar Wasmer en cualquier *platforma* (macOS, Linux y Windows) y *chip*. + +* **Respeta los estÃĄndares**. Wasmer pasa los [tests oficiales de WebAssembly](https://github.com/WebAssembly/testsuite) siendo compatible con [WASI](https://github.com/WebAssembly/WASI) y [Emscripten](https://emscripten.org/). + +## Empezamos? + +Wasmer no requiere ninguna dependencia. Puedes instalarlo con uno de estos instaladores: + +```sh +curl https://get.wasmer.io -sSfL | sh +``` + +
+ Con PowerShell (Windows) +

+ +```powershell +iwr https://win.wasmer.io -useb | iex +``` + +

+
+ +> Visita [wasmer-install](https://github.com/wasmerio/wasmer-install) para mÃĄs opciones de instalaciÃŗn: Homebrew, Scoop, Cargo... + + +#### Ejecuta un archivo WebAssembly + +ÂĄDespuÊs de instalar Wasmer deberías estar listo para ejecutar tu primer mÃŗdulo de WebAssembly! 🎉 + +Puedes empezar corriendo QuickJS: [qjs.wasm](https://registry-cdn.wapm.io/contents/_/quickjs/0.0.3/build/qjs.wasm) + +```bash +$ wasmer qjs.wasm +QuickJS - Type "\h" for help +qjs > +``` + +#### Esto es lo que puedes hacer: + +- [Usa Wasmer desde tu aplicaciÃŗn de Rust](https://docs.wasmer.io/integrations/rust) +- [Publica un paquete de Wasm en WAPM](https://docs.wasmer.io/ecosystem/wapm/publishing-your-package) +- [Lee mÃĄs sobre Wasmer](https://medium.com/wasmer/) + +## Integraciones en diferentes Lenguajes + +đŸ“Ļ Wasmer puede ser usado como una librería **integrada en diferentes lenguajes de programaciÃŗn**, para que puedas ejecutar WebAssembly _en cualquier sitio_. + +|   | Lenguaje | Librería | DocumentaciÃŗn | +|-|-|-|-| +| ![Rust logo] | [**Rust**][Rust integration] | [`wasmer` en crates.io] | [DocumentaciÃŗn][rust docs] +| ![C logo] | [**C/C++**][C integration] | [cabecera `wasmer.h`] | [DocumentaciÃŗn][c docs] | +| ![C# logo] | [**C#**][C# integration] | [`WasmerSharp` en NuGet] | [DocumentaciÃŗn][c# docs] | +| ![D logo] | [**D**][D integration] | [`wasmer` en Dug] | [DocumentaciÃŗn][d docs] | +| ![Python logo] | [**Python**][Python integration] | [`wasmer` en PyPI] | [DocumentaciÃŗn][python docs] | +| ![JS logo] | [**Javascript**][JS integration] | [`@wasmerio` en NPM] | [DocumentaciÃŗn][js docs] | +| ![Go logo] | [**Go**][Go integration] | [`wasmer` en Go] | [DocumentaciÃŗn][go docs] | +| ![PHP logo] | [**PHP**][PHP integration] | [`wasm` en PECL] | [DocumentaciÃŗn][php docs] | +| ![Ruby logo] | [**Ruby**][Ruby integration] | [`wasmer` en Ruby Gems] | [DocumentaciÃŗn][ruby docs] | +| ![Java logo] | [**Java**][Java integration] | [`wasmer/wasmer-jni` en Bintray] | [DocumentaciÃŗn][java docs] | +| ![Elixir logo] | [**Elixir**][Elixir integration] | [`wasmex` en hex] | [DocumentaciÃŗn][elixir docs] | +| ![R logo] | [**R**][R integration] | *sin paquete publicado* | [DocumentaciÃŗn][r docs] | +| ![Postgres logo] | [**Postgres**][Postgres integration] | *sin paquete publicado* | [DocumentaciÃŗn][postgres docs] | +| | [**Swift**][Swift integration] | *sin paquete publicado* | | +| ![Zig logo] | [**Zig**][Zig integration] | *no published package* | | +| ![Ocaml logo] | [**OCaml**][OCaml integration] | [`wasmer` OCaml package] | | + +[👋 Falta algÃēn lenguaje?](https://github.com/wasmerio/wasmer/issues/new?assignees=&labels=%F0%9F%8E%89+enhancement&template=---feature-request.md&title=) + +[rust logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/rust.svg +[rust integration]: https://github.com/wasmerio/wasmer/tree/master/lib/api +[`wasmer` en crates.io]: https://crates.io/crates/wasmer/ +[rust docs]: https://wasmerio.github.io/wasmer/crates/wasmer + +[c logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/c.svg +[c integration]: https://github.com/wasmerio/wasmer/tree/master/lib/c-api +[cabecera `wasmer.h`]: https://wasmerio.github.io/wasmer/c/ +[c docs]: https://wasmerio.github.io/wasmer/c/ + +[c# logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/csharp.svg +[c# integration]: https://github.com/migueldeicaza/WasmerSharp +[`wasmersharp` en NuGet]: https://www.nuget.org/packages/WasmerSharp/ +[c# docs]: https://migueldeicaza.github.io/WasmerSharp/ + +[d logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/d.svg +[d integration]: https://github.com/chances/wasmer-d +[`wasmer` en Dub]: https://code.dlang.org/packages/wasmer +[d docs]: https://chances.github.io/wasmer-d + +[python logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/python.svg +[python integration]: https://github.com/wasmerio/wasmer-python +[`wasmer` en pypi]: https://pypi.org/project/wasmer/ +[python docs]: https://github.com/wasmerio/wasmer-python#api-of-the-wasmer-extensionmodule + +[go logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/go.svg +[go integration]: https://github.com/wasmerio/wasmer-go +[`wasmer` en go]: https://pkg.go.dev/github.com/wasmerio/wasmer-go/wasmer +[go docs]: https://pkg.go.dev/github.com/wasmerio/wasmer-go/wasmer?tab=doc + +[php logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/php.svg +[php integration]: https://github.com/wasmerio/wasmer-php +[php docs]: https://wasmerio.github.io/wasmer-php/wasm/ +[`wasm` en pecl]: https://pecl.php.net/package/wasm + +[js logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/js.svg +[js integration]: https://github.com/wasmerio/wasmer-js +[`@wasmerio` en npm]: https://www.npmjs.com/org/wasmer +[js docs]: https://docs.wasmer.io/integrations/js/reference-api + +[ruby logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/ruby.svg +[ruby integration]: https://github.com/wasmerio/wasmer-ruby +[`wasmer` en ruby gems]: https://rubygems.org/gems/wasmer +[ruby docs]: https://www.rubydoc.info/gems/wasmer/ + +[java logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/java.svg +[java integration]: https://github.com/wasmerio/wasmer-java +[`wasmer/wasmer-jni` en bintray]: https://bintray.com/wasmer/wasmer-jni/wasmer-jni +[java docs]: https://github.com/wasmerio/wasmer-java/#api-of-the-wasmer-library + +[elixir logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/elixir.svg +[elixir integration]: https://github.com/tessi/wasmex +[elixir docs]: https://hexdocs.pm/wasmex/api-reference.html +[`wasmex` en hex]: https://hex.pm/packages/wasmex + +[r logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/r.svg +[r integration]: https://github.com/dirkschumacher/wasmr +[r docs]: https://github.com/dirkschumacher/wasmr#example + +[postgres logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/postgres.svg +[postgres integration]: https://github.com/wasmerio/wasmer-postgres +[postgres docs]: https://github.com/wasmerio/wasmer-postgres#usage--documentation + +[swift integration]: https://github.com/AlwaysRightInstitute/SwiftyWasmer + +[zig logo]: https://raw.githubusercontent.com/ziglang/logo/master/zig-favicon.png +[zig integration]: https://github.com/zigwasm/wasmer-zig + +[OCaml logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/ocaml.svg +[OCaml integration]: https://github.com/wasmerio/wasmer-ocaml +[`wasmer` OCaml package]: https://opam.ocaml.org/packages/wasmer/ + +## Contribuye + +**Damos la bienvenida a cualquier forma de contribuciÃŗn, especialmente a los nuevos miembros de la comunidad** 💜 + +ÂĄPuedes ver cÃŗmo crear el binario de Wasmer con [nuestros increíbles documentos](https://docs.wasmer.io/ecosystem/wasmer/building-from-source)! + +### Tests + +ÂŋQuieres testear? Los [documentos de Wasmer te enseÃąarÃĄn cÃŗmo](https://docs.wasmer.io/ecosystem/wasmer/building-from-source/testing). + +## Comunidad + +Wasmer tiene una comunidad increíble de desarrolladores y colaboradores ÂĄBienvenido, Ãēnete a nosotros! 👋 + +### Medios + +- [Slack](https://slack.wasmer.io/) +- [Twitter](https://twitter.com/wasmerio) +- [Facebook](https://www.facebook.com/wasmerio) +- [Email](mailto:hello@wasmer.io) diff --git a/arbitrator/tools/module_roots/wasmer/docs/fr/README.md b/arbitrator/tools/module_roots/wasmer/docs/fr/README.md new file mode 100644 index 0000000..409daf1 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/docs/fr/README.md @@ -0,0 +1,200 @@ +
+ + Logo Wasmer + + +

+ + État des tests + + + Licence + + + Salon Slack + +

+ +

+ Web + â€ĸ + Documentation + â€ĸ + Chat +

+ +
+ +
+ +[Wasmer](https://wasmer.io/) permet l'utilisation de conteneurs super lÊgers basÊs sur [WebAssembly](https://webassembly.org/) qui peuvent fonctionner n'importe oÚ : du bureau au cloud en passant par les appareils IoT, et Êgalement intÊgrÊs dans [*une multitude de langages de programmation*](https://github.com/wasmerio/wasmer#language-integrations). + +> This readme is also available in: [🇩đŸ‡Ē Deutsch-Allemand](https://github.com/wasmerio/wasmer/blob/master/docs/de/README.md) â€ĸ [đŸ‡Ŧ🇧 English-Anglaise](https://github.com/wasmerio/wasmer/blob/master/README.md) â€ĸ [đŸ‡Ē🇸 EspaÃąol-Espagnol](https://github.com/wasmerio/wasmer/blob/master/docs/es/README.md) â€ĸ [🇨đŸ‡ŗ 中文-Chinoise](https://github.com/wasmerio/wasmer/blob/master/docs/cn/README.md) â€ĸ [đŸ‡¯đŸ‡ĩ æ—ĨæœŦčĒž-japonais](https://github.com/wasmerio/wasmer/blob/master/docs/ja/README.md) + +## FonctionnalitÊs + +* **Rapide et sÃģr**. Wasmer exÊcute WebAssembly à une vitesse *quasi native* dans un environnement entièrement contrôlÊ (bac à sable, _sandbox_). + +* **Modulaire**. Wasmer prend en charge diffÊrents frameworks de compilation pour rÊpondre au mieux à vos besoins (LLVM, Cranelift ...). + +* **Universel**. Vous pouvez exÊcuter Wasmer sur n'importe quelle *plate-forme* (macOS, Linux et Windows) et *processeur*. + +* **Conforme aux normes**. Wasmer passe [la suite de tests officielle de WebAssembly](https://github.com/WebAssembly/testsuite) prenant en charge [WASI](https://github.com/WebAssembly/WASI) et [Emscripten](https://emscripten.org/) + +## Quickstart + +Wasmer est livrÊ sans aucune dÊpendance. Vous pouvez l'installer à l'aide des programmes d'installation ci-dessous : + +```sh +curl https://get.wasmer.io -sSfL | sh +``` + +
+ Avec PowerShell (Windows) +

+ +```powershell +iwr https://win.wasmer.io -useb | iex +``` + +

+
+ +> Voir [wasmer-install](https://github.com/wasmerio/wasmer-install) pour plus d'options d'installation: Homebrew, Scoop, Cargo... + + +#### ExÊcution d'un fichier WebAssembly + +Après avoir installÊ Wasmer, vous devriez ÃĒtre prÃĒt à exÊcuter votre premier fichier WebAssemby ! 🎉 + +Vous pouvez commencer par exÊcuter QuickJS : [qjs.wasm](https://registry-cdn.wapm.io/contents/_/quickjs/0.0.3/build/qjs.wasm) + +```bash +$ wasmer qjs.wasm +QuickJS - Type "\h" for help +qjs > +``` + +#### Voici ce que vous pouvez faire ensuite + +- [Utilisez Wasmer depuis votre application Rust](https://docs.wasmer.io/integrations/rust) +- [Publier un paquet Wasm sur WAPM](https://docs.wasmer.io/ecosystem/wapm/publishing-your-package) +- [En savoir plus sur Wasmer](https://medium.com/wasmer/) + +## IntÊgrations + +đŸ“Ļ Wasmer peut ÃĒtre utilisÊ comme une bibliothèque **intÊgrÊe dans diffÊrents langages**, vous pouvez donc utiliser WebAssembly _n'import oÚ_. + +|   | Langage de programmation | Package | Docs | +|-|-|-|-| +| ![Rust logo] | [**Rust**][Rust integration] | [`wasmer` Rust crate] | [Docs][rust docs] +| ![C logo] | [**C/C++**][C integration] | [`wasmer.h` headers] | [Docs][c docs] | +| ![C# logo] | [**C#**][C# integration] | [`WasmerSharp` NuGet package] | [Docs][c# docs] | +| ![D logo] | [**D**][D integration] | [`wasmer` Dub package] | [Docs][d docs] | +| ![Python logo] | [**Python**][Python integration] | [`wasmer` PyPI package] | [Docs][python docs] | +| ![JS logo] | [**Javascript**][JS integration] | [`@wasmerio` NPM packages] | [Docs][js docs] | +| ![Go logo] | [**Go**][Go integration] | [`wasmer` Go package] | [Docs][go docs] | +| ![PHP logo] | [**PHP**][PHP integration] | [`wasm` PECL package] | [Docs][php docs] | +| ![Ruby logo] | [**Ruby**][Ruby integration] | [`wasmer` Ruby Gem] | [Docs][ruby docs] | +| ![Java logo] | [**Java**][Java integration] | [`wasmer/wasmer-jni` Bintray package] | [Docs][java docs] | +| ![Elixir logo] | [**Elixir**][Elixir integration] | [`wasmex` hex package] | [Docs][elixir docs] | +| ![R logo] | [**R**][R integration] | *no published package* | [Docs][r docs] | +| ![Postgres logo] | [**Postgres**][Postgres integration] | *no published package* | [Docs][postgres docs] | +| | [**Swift**][Swift integration] | *no published package* | | +| ![Zig logo] | [**Zig**][Zig integration] | *no published package* | | +| ![Ocaml logo] | [**OCaml**][OCaml integration] | [`wasmer` OCaml package] | | + +[👋 Il manque un langage ?](https://github.com/wasmerio/wasmer/issues/new?assignees=&labels=%F0%9F%8E%89+enhancement&template=---feature-request.md&title=) + +[rust logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/rust.svg +[rust integration]: https://github.com/wasmerio/wasmer/tree/master/lib/api +[`wasmer` rust crate]: https://crates.io/crates/wasmer/ +[rust docs]: https://wasmerio.github.io/wasmer/crates/wasmer + +[c logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/c.svg +[c integration]: https://github.com/wasmerio/wasmer/tree/master/lib/c-api +[`wasmer.h` headers]: https://wasmerio.github.io/wasmer/c/ +[c docs]: https://wasmerio.github.io/wasmer/c/ + +[c# logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/csharp.svg +[c# integration]: https://github.com/migueldeicaza/WasmerSharp +[`wasmersharp` nuget package]: https://www.nuget.org/packages/WasmerSharp/ +[c# docs]: https://migueldeicaza.github.io/WasmerSharp/ + +[d logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/d.svg +[d integration]: https://github.com/chances/wasmer-d +[`wasmer` Dub package]: https://code.dlang.org/packages/wasmer +[d docs]: https://chances.github.io/wasmer-d + +[python logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/python.svg +[python integration]: https://github.com/wasmerio/wasmer-python +[`wasmer` pypi package]: https://pypi.org/project/wasmer/ +[python docs]: https://github.com/wasmerio/wasmer-python#api-of-the-wasmer-extensionmodule + +[go logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/go.svg +[go integration]: https://github.com/wasmerio/wasmer-go +[`wasmer` go package]: https://pkg.go.dev/github.com/wasmerio/wasmer-go/wasmer +[go docs]: https://pkg.go.dev/github.com/wasmerio/wasmer-go/wasmer?tab=doc + +[php logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/php.svg +[php integration]: https://github.com/wasmerio/wasmer-php +[`wasm` pecl package]: https://pecl.php.net/package/wasm +[php docs]: https://wasmerio.github.io/wasmer-php/wasm/ + +[js logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/js.svg +[js integration]: https://github.com/wasmerio/wasmer-js +[`@wasmerio` npm packages]: https://www.npmjs.com/org/wasmer +[js docs]: https://docs.wasmer.io/integrations/js/reference-api + +[ruby logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/ruby.svg +[ruby integration]: https://github.com/wasmerio/wasmer-ruby +[`wasmer` ruby gem]: https://rubygems.org/gems/wasmer +[ruby docs]: https://www.rubydoc.info/gems/wasmer/ + +[java logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/java.svg +[java integration]: https://github.com/wasmerio/wasmer-java +[`wasmer/wasmer-jni` bintray package]: https://bintray.com/wasmer/wasmer-jni/wasmer-jni +[java docs]: https://github.com/wasmerio/wasmer-java/#api-of-the-wasmer-library + +[elixir logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/elixir.svg +[elixir integration]: https://github.com/tessi/wasmex +[elixir docs]: https://hexdocs.pm/wasmex/api-reference.html +[`wasmex` hex package]: https://hex.pm/packages/wasmex + +[r logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/r.svg +[r integration]: https://github.com/dirkschumacher/wasmr +[r docs]: https://github.com/dirkschumacher/wasmr#example + +[postgres logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/postgres.svg +[postgres integration]: https://github.com/wasmerio/wasmer-postgres +[postgres docs]: https://github.com/wasmerio/wasmer-postgres#usage--documentation + +[swift integration]: https://github.com/AlwaysRightInstitute/SwiftyWasmer + +[zig logo]: https://raw.githubusercontent.com/ziglang/logo/master/zig-favicon.png +[zig integration]: https://github.com/zigwasm/wasmer-zig + +[OCaml logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/ocaml.svg +[OCaml integration]: https://github.com/wasmerio/wasmer-ocaml +[`wasmer` OCaml package]: https://opam.ocaml.org/packages/wasmer/ + +## Contribuer + +**Nous accueillons toutes formes de contributions, en particulier de la part des nouveaux membres de notre communautÊ**. 💜 + +Vous pouvez vÊrifier comment compiler Wasmer dans [notre documentation](https://docs.wasmer.io/ecosystem/wasmer/building-from-source)! + +### Test + +Vous voulez des tests ? La [documentation de Wasmer](https://docs.wasmer.io/ecosystem/wasmer/building-from-source/testing) vous montrera comment les exÊcuter. + +## CommunautÊ + +Wasmer a une incroyable communautÊ de dÊveloppeurs et de contributeurs. Bienvenue et rejoignez-nous ! 👋 + +### Canaux de communications + +- [Slack](https://slack.wasmer.io/) +- [Twitter](https://twitter.com/wasmerio) +- [Facebook](https://www.facebook.com/wasmerio) +- [Email](mailto:hello@wasmer.io) diff --git a/arbitrator/tools/module_roots/wasmer/docs/ja/README.md b/arbitrator/tools/module_roots/wasmer/docs/ja/README.md new file mode 100644 index 0000000..c7110dd --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/docs/ja/README.md @@ -0,0 +1,200 @@ +
+ + Wasmerロゴ + + +

+ + ビãƒĢド゚テãƒŧã‚ŋã‚š + + + ナイã‚ģãƒŗã‚š + + + SlackチãƒŖãƒŗネãƒĢ + +

+ +

+ Website + â€ĸ + Docs + â€ĸ + Chat +

+ +
+ +
+ +[Wasmer](https://wasmer.io/) は、[WebAssembly](https://webassembly.org/) をベãƒŧ゚とした非常ãĢčģŊ量ãĒã‚ŗãƒŗテナを原įžã—ぞす。デ゚クトップからクナã‚Ļドや IoT デバイ゚上ぞで、おんãĒį’°åĸƒã§ã‚‚åŽŸčĄŒã§ãã€ã•ã‚‰ãĢ[*äģģ意ぎプログナミãƒŗã‚°č¨€čĒž*](#äģ–ãŽč¨€čĒžã¨ãŽã‚¤ãƒŗテグãƒŦãƒŧã‚ˇãƒ§ãƒŗ)ãĢ埋めčžŧむこともできぞす。 + +> こぎ readme は、æŦĄãŽč¨€čĒžã§ã‚‚刊į”¨å¯čƒŊです。[🇩đŸ‡Ē Deutsch-ドイツčĒž](https://github.com/wasmerio/wasmer/blob/master/docs/de/README.md) â€ĸ [🇨đŸ‡ŗ 中文-Chinese](https://github.com/wasmerio/wasmer/blob/master/docs/cn/README.md) â€ĸ [đŸ‡Ŧ🇧 English-英čĒž](https://github.com/wasmerio/wasmer/blob/master/README.md) â€ĸ [đŸ‡Ē🇸 EspaÃąol-Spanish](https://github.com/wasmerio/wasmer/blob/master/docs/es/README.md) â€ĸ [đŸ‡Ģ🇷 Français-French](https://github.com/wasmerio/wasmer/blob/master/docs/fr/README.md) + +## 抟čƒŊ + +* **éĢ˜é€Ÿã‹ã¤åŽ‰å…¨**。WebAssembly を厌全ãĒã‚ĩãƒŗドボック゚į’°åĸƒå†…で*ネイテã‚ŖブãĢčŋ‘い*゚ピãƒŧãƒ‰ã§åŽŸčĄŒã—ãžã™ã€‚ + +* **プナã‚ŦブãƒĢ**。į•°ãĒるã‚ŗãƒŗパイãƒĢフãƒŦãƒŧムワãƒŧク (LLVM、Cranelift ãĒお...) をã‚ĩポãƒŧトしãĻいるため、ニãƒŧã‚ēãĢ合ãŖた最遊ãĒフãƒŦãƒŧムワãƒŧクを選択できぞす。 + +* **ãƒĻニバãƒŧã‚ĩãƒĢ**。おんãĒプナットフりãƒŧム上 (macOS、Linux、Windows) でも、おんãĒ*チップã‚ģット*ä¸Šã§ã‚‚åŽŸčĄŒã§ããžã™ã€‚ + +* **標æē–ãĢæē–æ‹ **。ナãƒŗã‚ŋイムは[å…ŦåŧãŽ WebAssembly テ゚ト゚イãƒŧト](https://github.com/WebAssembly/testsuite)ãĢ通ãŖãĻおり、[WASI](https://github.com/WebAssembly/WASI) と [Emscripten](https://emscripten.org/) をã‚ĩポãƒŧトしぞす。 + +## クイック゚ã‚ŋãƒŧト + +Wasmer は䞝存é–ĸäŋ‚ãĒしで動äŊœã—ぞす。äģĨ下ぎã‚ŗマãƒŗドでイãƒŗ゚トãƒŧナãƒŧをäŊŋį”¨ã—ãĻイãƒŗ゚トãƒŧãƒĢできぞす。 + +```sh +curl https://get.wasmer.io -sSfL | sh +``` + +
+ PowerShell ぎ場合 (Windows) +

+ +```powershell +iwr https://win.wasmer.io -useb | iex +``` + +

+
+ +> Homebrew、Scoop、Cargo ãĒお、äģ–ぎイãƒŗ゚トãƒŧãƒĢæ–šæŗ•ãĢついãĻは、[wasmer-install](https://github.com/wasmerio/wasmer-install) を参į…§ã—ãĻください。 + + +#### WebAssembly ãƒ•ã‚Ąã‚¤ãƒĢãŽåŽŸčĄŒ + +Wasmer をイãƒŗ゚トãƒŧãƒĢしたら、初めãĻぎ WebAssembly ãƒ•ã‚Ąã‚¤ãƒĢãŽåŽŸčĄŒæē–備が厌äē†ã§ã™īŧ 🎉 + +QuickJS ([qjs.wasm](https://registry-cdn.wapm.io/contents/_/quickjs/0.0.3/build/qjs.wasm)) ã‚’åŽŸčĄŒã™ã‚‹ã“ã¨ã§ã€ã™ããĢ始められぞす。 + +```bash +$ wasmer qjs.wasm +QuickJS - Type "\h" for help +qjs > +``` + +#### æŦĄãĢできること + +- [Rust ã‚ĸプãƒĒã‚ąãƒŧã‚ˇãƒ§ãƒŗから Wasmer をäŊŋį”¨ã™ã‚‹](https://docs.wasmer.io/integrations/rust) +- [WAPM で Wasm ãƒ‘ãƒƒã‚ąãƒŧジをå…Ŧ開する](https://docs.wasmer.io/ecosystem/wapm/publishing-your-package) +- [Wasmer ãĢついãĻさらãĢå­Ļãļ](https://medium.com/wasmer/) + +## äģ–ãŽč¨€čĒžã¨ãŽã‚¤ãƒŗテグãƒŦãƒŧã‚ˇãƒ§ãƒŗ + +đŸ“Ļ Wasmer ナãƒŗã‚ŋイムは**äģ–ãŽč¨€čĒžãĢįĩ„ãŋčžŧんで**äŊŋį”¨ã§ãã‚‹ãŸã‚ã€WebAssembly は*おんãĒ場所でも*刊į”¨ã§ããžã™ã€‚ + +|   | Language | Package | Docs | +|-|-|-|-| +| ![Rust logo] | [**Rust**][Rust integration] | [`wasmer` Rust crate] | [Docs][rust docs] +| ![C logo] | [**C/C++**][C integration] | [`wasmer.h` headers] | [Docs][c docs] | +| ![C# logo] | [**C#**][C# integration] | [`WasmerSharp` NuGet package] | [Docs][c# docs] | +| ![D logo] | [**D**][D integration] | [`wasmer` Dub package] | [Docs][d docs] | +| ![Python logo] | [**Python**][Python integration] | [`wasmer` PyPI package] | [Docs][python docs] | +| ![JS logo] | [**Javascript**][JS integration] | [`@wasmerio` NPM packages] | [Docs][js docs] | +| ![Go logo] | [**Go**][Go integration] | [`wasmer` Go package] | [Docs][go docs] | +| ![PHP logo] | [**PHP**][PHP integration] | [`wasm` PECL package] | [Docs][php docs] | +| ![Ruby logo] | [**Ruby**][Ruby integration] | [`wasmer` Ruby Gem] | [Docs][ruby docs] | +| ![Java logo] | [**Java**][Java integration] | [`wasmer/wasmer-jni` Bintray package] | [Docs][java docs] | +| ![Elixir logo] | [**Elixir**][Elixir integration] | [`wasmex` hex package] | [Docs][elixir docs] | +| ![R logo] | [**R**][R integration] | *å…Ŧé–‹ãƒ‘ãƒƒã‚ąãƒŧジãĒし* | [Docs][r docs] | +| ![Postgres logo] | [**Postgres**][Postgres integration] | *å…Ŧé–‹ãƒ‘ãƒƒã‚ąãƒŧジãĒし* | [Docs][postgres docs] | +| | [**Swift**][Swift integration] | *å…Ŧé–‹ãƒ‘ãƒƒã‚ąãƒŧジãĒし* | | +| ![Zig logo] | [**Zig**][Zig integration] | *no published package* | | +| ![Ocaml logo] | [**OCaml**][OCaml integration] | [`wasmer` OCaml package] | | + +[👋 言čĒžãŒčĻ‹åŊ“たらãĒいīŧŸ](https://github.com/wasmerio/wasmer/issues/new?assignees=&labels=%F0%9F%8E%89+enhancement&template=---feature-request.md&title=) + +[rust logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/rust.svg +[rust integration]: https://github.com/wasmerio/wasmer/tree/master/lib/api +[`wasmer` rust crate]: https://crates.io/crates/wasmer/ +[rust docs]: https://wasmerio.github.io/wasmer/crates/wasmer + +[c logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/c.svg +[c integration]: https://github.com/wasmerio/wasmer/tree/master/lib/c-api +[`wasmer.h` headers]: https://wasmerio.github.io/wasmer/c/ +[c docs]: https://wasmerio.github.io/wasmer/c/ + +[c# logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/csharp.svg +[c# integration]: https://github.com/migueldeicaza/WasmerSharp +[`wasmersharp` nuget package]: https://www.nuget.org/packages/WasmerSharp/ +[c# docs]: https://migueldeicaza.github.io/WasmerSharp/ + +[d logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/d.svg +[d integration]: https://github.com/chances/wasmer-d +[`wasmer` Dub package]: https://code.dlang.org/packages/wasmer +[d docs]: https://chances.github.io/wasmer-d + +[python logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/python.svg +[python integration]: https://github.com/wasmerio/wasmer-python +[`wasmer` pypi package]: https://pypi.org/project/wasmer/ +[python docs]: https://github.com/wasmerio/wasmer-python#api-of-the-wasmer-extensionmodule + +[go logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/go.svg +[go integration]: https://github.com/wasmerio/wasmer-go +[`wasmer` go package]: https://pkg.go.dev/github.com/wasmerio/wasmer-go/wasmer +[go docs]: https://pkg.go.dev/github.com/wasmerio/wasmer-go/wasmer?tab=doc + +[php logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/php.svg +[php integration]: https://github.com/wasmerio/wasmer-php +[`wasm` pecl package]: https://pecl.php.net/package/wasm +[php docs]: https://wasmerio.github.io/wasmer-php/wasm/ + +[js logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/js.svg +[js integration]: https://github.com/wasmerio/wasmer-js +[`@wasmerio` npm packages]: https://www.npmjs.com/org/wasmer +[js docs]: https://docs.wasmer.io/integrations/js/reference-api + +[ruby logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/ruby.svg +[ruby integration]: https://github.com/wasmerio/wasmer-ruby +[`wasmer` ruby gem]: https://rubygems.org/gems/wasmer +[ruby docs]: https://www.rubydoc.info/gems/wasmer/ + +[java logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/java.svg +[java integration]: https://github.com/wasmerio/wasmer-java +[`wasmer/wasmer-jni` bintray package]: https://bintray.com/wasmer/wasmer-jni/wasmer-jni +[java docs]: https://github.com/wasmerio/wasmer-java/#api-of-the-wasmer-library + +[elixir logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/elixir.svg +[elixir integration]: https://github.com/tessi/wasmex +[elixir docs]: https://hexdocs.pm/wasmex/api-reference.html +[`wasmex` hex package]: https://hex.pm/packages/wasmex + +[r logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/r.svg +[r integration]: https://github.com/dirkschumacher/wasmr +[r docs]: https://github.com/dirkschumacher/wasmr#example + +[postgres logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/postgres.svg +[postgres integration]: https://github.com/wasmerio/wasmer-postgres +[postgres docs]: https://github.com/wasmerio/wasmer-postgres#usage--documentation + +[swift integration]: https://github.com/AlwaysRightInstitute/SwiftyWasmer + +[zig logo]: https://raw.githubusercontent.com/ziglang/logo/master/zig-favicon.png +[zig integration]: https://github.com/zigwasm/wasmer-zig + +[OCaml logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/ocaml.svg +[OCaml integration]: https://github.com/wasmerio/wasmer-ocaml +[`wasmer` OCaml package]: https://opam.ocaml.org/packages/wasmer/ + +## ã‚ŗãƒŗトãƒĒビãƒĨãƒŧã‚ˇãƒ§ãƒŗ + +**おんãĒåŊĸでぎč˛ĸįŒŽã‚‚æ­“čŋŽã§ã™ã€‚ã‚ŗミãƒĨニテã‚ŖãŽæ–°ã—ã„ãƒĄãƒŗバãƒŧからぎč˛ĸįŒŽã¯į‰šãĢ歓čŋŽã—ぞす。** 💜 + +Wasmer ナãƒŗã‚ŋイムぎビãƒĢド斚æŗ•ã¯ã€[į´ æ™´ã‚‰ã—いドキãƒĨãƒĄãƒŗト](https://docs.wasmer.io/ecosystem/wasmer/building-from-source)でįĸēčĒã§ããžã™īŧ + +### テ゚ト + +ãƒ†ã‚šãƒˆã‚’åŽŸčĄŒã—ãŸã„ã§ã™ã‹īŧŸ [Wasmer docs で斚æŗ•ã‚’čĒŦ明](https://docs.wasmer.io/ecosystem/wasmer/building-from-source/testing)しãĻいぞす。 + +## ã‚ŗミãƒĨニテã‚Ŗ + +Wasmer ãĢは、開į™ēč€…ã¨ã‚ŗãƒŗトãƒĒビãƒĨãƒŧã‚ŋãƒŧぎį´ æ™´ã‚‰ã—いã‚ŗミãƒĨニテã‚Ŗがありぞす。ようこそīŧ あãĒたも是非参加しãĻくださいīŧ 👋 + +### チãƒŖãƒŗネãƒĢ + +- [Slack](https://slack.wasmer.io/) +- [Twitter](https://twitter.com/wasmerio) +- [Facebook](https://www.facebook.com/wasmerio) +- [Email](mailto:hello@wasmer.io) diff --git a/arbitrator/tools/module_roots/wasmer/docs/journal.md b/arbitrator/tools/module_roots/wasmer/docs/journal.md new file mode 100644 index 0000000..70c1d89 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/docs/journal.md @@ -0,0 +1,148 @@ +# WASM Journal Functionality + +Wasmer now supports journals for the state of a WASM process. This gives the ability +to persist changes made to the temporary file system and to save and store snapshots +of the running process. + +The journal file is a linear history of events that occurred when the process was +running that if replayed will bring the process made to a discrete and deterministic +state. + +Journal files can be concatenated, compacted and filtered to change the discrete state. + +These journals are maintained in a consistent and durable way thus ensuring that +failures of the system while the process is running does not corrupt the journal. + +# Snapshot Triggers + +The journal will record state changes to the sandbox built around the WASM process as +it runs however it may be important to certain use-cases to take explicit snapshot +restoration points in the journal at key points that make sense. + +When a snapshot is triggered all the running threads of the process are paused and +the state of the WASM memory and thread stacks are recorded into the journal so that +they can be restored. + +In order to use the snapshot functionality the WASM process must be compiled with +the `asyncify` modifications, this can be done using the `wasm-opt` tool. + +Note: If a process does not have the `asyncify` modifications you can still use +the journal functionality for recording the file system and WASM memory state +however the stacks of the threads will be omitted meaning a restoration will +restart the main thread. + +Various triggers are possible that will cause a snapshot to be taken at a specific +point in time, these are as follows: + +## On Idle + +Triggered when all the threads in the process go into an idle state. This trigger +is useful to take snapshots at convenient moments without causing unnecessary overhead. + +For processes that have TTY/STDIN input this is particularly useful. + +## On FirstListen + +Triggered when a listen syscall is invoked on a socket. This can be an important +milestone to take a snapshot when one wants to speed up the boot time of a WASM process +up to the moment where it is ready to accept requests. + +## On FirstStdin + +Triggered when the process reads stdin for the first time. This can be useful to +speed up the boot time of a WASM process. + +## On FirstEnviron + +Triggered when the process reads an environment variable for the first time. This can +be useful to speed up the boot time of a CGI WASM process which reads the environment +variables to parse the request that it must execute. + +## On Timer Interval + +Triggered periodically based on a timer (default 10 seconds) which can be specified +using the `journal-interval` option. This can be useful for asynchronous replication +of a WASM process from one machine to another with a particular lag latency. + +## On Sigint (Ctrl+C) + +Issued if the user sends an interrupt signal (Ctrl + C). + +## On Sigalrm + +Alarm clock signal (used for timers) +(see `man alarm`) + +## On Sigtstp + +The SIGTSTP signal is sent to a process by its controlling terminal to request it to stop +temporarily. It is commonly initiated by the user pressing Ctrl-Z. + +# On Sigstop + +The SIGSTOP signal instructs the operating system to stop a process for later resumption + +# On Non Deterministic Call + +When a non-deterministic call is made from WASM process to the outside world (i.e. it reaches +out of the sandbox) + +# Limitations + +- The WASM process that wish to record the state of the threads must have had the `asyncify` + post processing step applied to the binary (see `wasm-opt`). +- Taking a snapshot can consume large amounts of memory while its processing. +- Snapshots are not instant and have overhead when generating. +- The layout of the memory must be known by the runtime in order to take snapshots. + +# Design + +On startup if the restore journal file is specified then the runtime will restore the +state of the WASM process by reading and processing the log entries in the snapshot +journal. This restoration will bring the memory and the thread stacks back to a previous +point in time and then resume all the threads. + +When a trigger occurs a new journal will be taken of the WASM process which will +take the following steps: + +1. Pause all threads +2. Capture the stack of each thread +3. Write the thread state to the journal +4. Write the memory (excluding stacks) to the journal +5. Resume execution. + +The implementation is currently able to save and restore the following: + +- WASM Memory +- Stack memory +- Call stack +- Open sockets +- Open files +- Terminal text + +# Journal Capturer Implementations + +## Log File Journal + +Writes the log events to a linear log file on the local file system +as they are received by the trait. Log files can be concatenated +together to make larger log files. + +## Unsupported Journal + +The default implementation does not support snapshots and will error +out if an attempt is made to send it events. Using the unsupported +capturer as a restoration point will restore nothing but will not +error out. + +## Compacting Journal + +Deduplicates memory and stacks to reduce the number of volume of +log events sent to its inner capturer. Compacting the events occurs +in line as the events are generated + +## Filtered Journal + +Filters out a specific set of log events and drops the rest, this +capturer can be useful for restoring to a previous call point but +retaining the memory changes (e.g. WCGI runner). diff --git a/arbitrator/tools/module_roots/wasmer/docs/ko/README.md b/arbitrator/tools/module_roots/wasmer/docs/ko/README.md new file mode 100644 index 0000000..81843fe --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/docs/ko/README.md @@ -0,0 +1,228 @@ + + +
+ +Wasmer는 _ė´ˆę˛Ŋ량 ėģ¨í…Œė´ë„ˆ_ ëĨŧ *Desktop*ė—ė„œëļ€í„° *Cloud*, *Edge*, *IoT* 기기들까ė§€ ė–´ë””ė—ė„œë‚˜ ė‹¤í–‰í•  ėˆ˜ ėžˆëŠ” _ëš ëĨ´ęŗ  ė•ˆė „í•œ_ [**WebAssembly**](https://webassembly.org) 런타ėž„ ėž…니다. + +> _ė´ ëŦ¸ė„œëŠ” ė•„래ė™€ 같ė€ ė–¸ė–´ë“¤ė„ ė§€ė›í•Šë‹ˆë‹¤.: +[🇨đŸ‡ŗ 中 文 -Chinese](https://github.com/wasmerio/wasmer/blob/master/docs/cn/README.md) â€ĸ +[🇩đŸ‡Ē Deutsch-German](https://github.com/wasmerio/wasmer/blob/master/docs/de/README.md) â€ĸ +[đŸ‡Ē🇸 EspaÃąol-Spanish](https://github.com/wasmerio/wasmer/blob/master/docs/es/README.md) â€ĸ +[đŸ‡Ģ🇷 Français-French](https://github.com/wasmerio/wasmer/blob/master/docs/fr/README.md) â€ĸ +[đŸ‡¯đŸ‡ĩ æ—ĨæœŦ čĒž -Japanese](https://github.com/wasmerio/wasmer/blob/master/docs/ja/README.md)_. +[🇰🇷 한ęĩ­ė–´ -Korean](https://github.com/wasmerio/wasmer/blob/master/docs/ko/README.md)_. + +### 특ė§• + +* 기ëŗ¸ė ėœŧ로 ė•ˆė „핊니다. ëĒ…ė‹œė ėœŧ로 ė„¤ė •í•˜ė§€ ė•ŠëŠ” 한 파ėŧ, 네트ė›ŒíŦ 또는 환ę˛Ŋė— ė•Ąė„¸ėŠ¤í•  ėˆ˜ ė—†ėŠĩ니다. +* [WASI](https://github.com/WebAssembly/WASI)ė™€ [Emscripten](https://emscripten.org/)ė„ ėĻ‰ė‹œ ė§€ė›í•Šë‹ˆë‹¤. +* ëš ëĻ…니다. nativeė— 가까ėš´ ė†ë„ëĄœ WebAssemblyëĨŧ ė‹¤í–‰í•Šë‹ˆë‹¤. +* [ė—ŦëŸŦ 프로그래밍 ė–¸ė–´](https://github.com/wasmerio/wasmer/#-language-integrations)ė— ėž„ë˛ ë””ë“œ 가ëŠĨ합니다. +* ėĩœė‹  WebAssembly ė œė•ˆ(SIMD, Reference Types, Threads, ...)ė„ ė¤€ėˆ˜í•Šë‹ˆë‹¤. + +### ė„¤ėš˜ + +Wasmer CLI는 ėĸ…ė†ė„ąė´ ė—†ëŠ” 단ėŧ ė‹¤í–‰ 파ėŧ로 ė œęŗĩ됩니다. + +```sh +curl https://get.wasmer.io -sSfL | sh +``` + + +
+ 다ëĨ¸ ė„¤ėš˜ ė˜ĩė…˜ (Powershell, Brew, Cargo, ...) + + _Wasmer는 다ė–‘í•œ 패키ė§€ 매니ė €ëĨŧ í†ĩ해 ė„¤ėš˜ 할 ėˆ˜ ėžˆėŠĩ니다. 환ę˛Ŋė— 가ėžĨ ė í•Ší•œ 것ė„ ė„ íƒí•˜ė‹­ė‹œė˜¤.:_ + + * Powershell (Windows) + ```powershell + iwr https://win.wasmer.io -useb | iex + ``` + + * Homebrew (macOS, Linux) + + ```sh + brew install wasmer + ``` + + * Scoop (Windows) + + ```sh + scoop install wasmer + ``` + + * Chocolatey (Windows) + + ```sh + choco install wasmer + ``` + + * Cargo + + _Note: ė‚ŦėšŠ 가ëŠĨ한 ëĒ¨ë“  기ëŠĨė€ [`wasmer-cli` + crate docs](https://github.com/wasmerio/wasmer/tree/master/lib/cli/README.md) ëŦ¸ė„œė— ė„¤ëĒ…되ė–´ ėžˆėŠĩ니다._ + + ```sh + cargo install wasmer-cli + ``` + + > 더 많ė€ ė„¤ėš˜ ė˜ĩė…˜ė„ ė°žęŗ  ęŗ„ė‹­ë‹ˆęšŒ? ėžė„¸í•œ 내ėšŠė€ [the `wasmer-install` + repository](https://github.com/wasmerio/wasmer-install)ëĨŧ ė°¸ėĄ°í•˜ė‹­ė‹œė˜¤! +
+ +### ëš ëĨ¸ ė‹œėž‘ + +WebAssembly ëĒ¨ë“ˆ([`qjs.wasm`](https://registry-cdn.wapm.io/contents/_/quickjs/0.0.3/build/qjs.wasm))로 ėģ´íŒŒėŧ된 +ėž‘ęŗ  íŦ함 가ëŠĨ한 Javascript ė—”ė§„ė¸ [QuickJS](https://github.com/bellard/quickjs/)ëĨŧ ė‹¤í–‰í•˜ė—Ŧ ė‹œėž‘í•  ėˆ˜ ėžˆėŠĩ니다.: + +```bash +$ wasmer qjs.wasm +QuickJS - Type "\h" for help +qjs > const i = 1 + 2; +qjs > console.log("hello " + i); +hello 3 +``` + +#### 다ėŒė— 할 ėˆ˜ ėžˆëŠ” ėŧ : + +- [ė–´í”ŒëĻŦėŧ€ė´ė…˜ė—ė„œ wasmer ė‚ŦėšŠ](https://docs.wasmer.io/integrations/rust) +- [WAPMė— wasm 패키ė§€ 게ė‹œ](https://docs.wasmer.io/ecosystem/wapm/publishing-your-package) +- [Wasmerė— 대해 ėžė„¸ížˆ ė•Œė•„ëŗ´ę¸°](https://medium.com/wasmer/) + +## đŸ“Ļ 다ëĨ¸ ė–¸ė–´ė™€ė˜ í†ĩ합 + +Wasmer 런타ėž„ė€ **다ëĨ¸ ė–¸ė–´ė— 내ėžĨ된** ëŧė´ë¸ŒëŸŦëĻŦ로 ė‚ŦėšŠí•  ėˆ˜ ėžˆėœŧë¯€ëĄœ _ė–´ë””ė—ė„œë‚˜_ WebAssemblyëĨŧ ė‚ŦėšŠí•  ėˆ˜ ėžˆėŠĩ니다. + +| | Language | Package | Documentation | +|-|-|-|-| +| ![Rust logo] | [**Rust**][Rust integration] | [`wasmer` Rust crate] | [Learn][rust docs] +| ![C logo] | [**C/C++**][C integration] | [`wasmer.h` header] | [Learn][c docs] | +| ![C# logo] | [**C#**][C# integration] | [`WasmerSharp` NuGet package] | [Learn][c# docs] | +| ![D logo] | [**D**][D integration] | [`wasmer` Dub package] | [Learn][d docs] | +| ![Python logo] | [**Python**][Python integration] | [`wasmer` PyPI package] | [Learn][python docs] | +| ![JS logo] | [**Javascript**][JS integration] | [`@wasmerio` NPM packages] | [Learn][js docs] | +| ![Go logo] | [**Go**][Go integration] | [`wasmer` Go package] | [Learn][go docs] | +| ![PHP logo] | [**PHP**][PHP integration] | [`wasm` PECL package] | [Learn][php docs] | +| ![Ruby logo] | [**Ruby**][Ruby integration] | [`wasmer` Ruby Gem] | [Learn][ruby docs] | +| ![Java logo] | [**Java**][Java integration] | [`wasmer/wasmer-jni` Bintray package] | [Learn][java docs] | +| ![Elixir logo] | [**Elixir**][Elixir integration] | [`wasmex` hex package] | [Learn][elixir docs] | +| ![R logo] | [**R**][R integration] | *ęŗĩ개 패키ė§€ ė—†ėŒ* | [Learn][r docs] | +| ![Postgres logo] | [**Postgres**][Postgres integration] | *ęŗĩ개 패키ė§€ ė—†ėŒ* | [Learn][postgres docs] | +| | [**Swift**][Swift integration] | *ęŗĩ개 패키ė§€ ė—†ėŒ* | | +| ![Zig logo] | [**Zig**][Zig integration] | *ęŗĩ개 패키ė§€ ė—†ėŒ* | | +| ![Dart logo] | [**Dart**][Dart integration] | [`wasm` pub package] | | +| | [**Lisp**][Lisp integration] | *under heavy development - no published package* | | +| ![Ocaml logo] | [**OCaml**][OCaml integration] | [`wasmer` OCaml package] | | + +[👋  ė—†ëŠ” ė–¸ė–´ę°€ ėžˆėŠĩ니까?](https://github.com/wasmerio/wasmer/issues/new?assignees=&labels=%F0%9F%8E%89+enhancement&template=---feature-request.md&title=) + +[rust logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/rust.svg +[rust integration]: https://github.com/wasmerio/wasmer/tree/master/lib/api +[`wasmer` rust crate]: https://crates.io/crates/wasmer/ +[rust docs]: https://docs.rs/wasmer/ + +[c logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/c.svg +[c integration]: https://github.com/wasmerio/wasmer/tree/master/lib/c-api +[`wasmer.h` header]: https://github.com/wasmerio/wasmer/blob/master/lib/c-api/wasmer.h +[c docs]: https://docs.rs/wasmer-c-api/*/wasmer_c_api/wasm_c_api/index.html + +[c# logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/csharp.svg +[c# integration]: https://github.com/migueldeicaza/WasmerSharp +[`wasmersharp` nuget package]: https://www.nuget.org/packages/WasmerSharp/ +[c# docs]: https://migueldeicaza.github.io/WasmerSharp/ + +[d logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/d.svg +[d integration]: https://github.com/chances/wasmer-d +[`wasmer` Dub package]: https://code.dlang.org/packages/wasmer +[d docs]: https://chances.github.io/wasmer-d + +[python logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/python.svg +[python integration]: https://github.com/wasmerio/wasmer-python +[`wasmer` pypi package]: https://pypi.org/project/wasmer/ +[python docs]: https://wasmerio.github.io/wasmer-python/api/wasmer/ + +[go logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/go.svg +[go integration]: https://github.com/wasmerio/wasmer-go +[`wasmer` go package]: https://pkg.go.dev/github.com/wasmerio/wasmer-go/wasmer +[go docs]: https://pkg.go.dev/github.com/wasmerio/wasmer-go/wasmer?tab=doc + +[php logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/php.svg +[php integration]: https://github.com/wasmerio/wasmer-php +[`wasm` pecl package]: https://pecl.php.net/package/wasm +[php docs]: https://wasmerio.github.io/wasmer-php/ + +[js logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/js.svg +[js integration]: https://github.com/wasmerio/wasmer-js +[`@wasmerio` npm packages]: https://www.npmjs.com/org/wasmer +[js docs]: https://docs.wasmer.io/integrations/js/reference-api + +[ruby logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/ruby.svg +[ruby integration]: https://github.com/wasmerio/wasmer-ruby +[`wasmer` ruby gem]: https://rubygems.org/gems/wasmer +[ruby docs]: https://wasmerio.github.io/wasmer-ruby/wasmer_ruby/index.html + +[java logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/java.svg +[java integration]: https://github.com/wasmerio/wasmer-java +[`wasmer/wasmer-jni` bintray package]: https://bintray.com/wasmer/wasmer-jni/wasmer-jni +[java docs]: https://github.com/wasmerio/wasmer-java/#api-of-the-wasmer-library + +[elixir logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/elixir.svg +[elixir integration]: https://github.com/tessi/wasmex +[elixir docs]: https://hexdocs.pm/wasmex/api-reference.html +[`wasmex` hex package]: https://hex.pm/packages/wasmex + +[r logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/r.svg +[r integration]: https://github.com/dirkschumacher/wasmr +[r docs]: https://github.com/dirkschumacher/wasmr#example + +[postgres logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/postgres.svg +[postgres integration]: https://github.com/wasmerio/wasmer-postgres +[postgres docs]: https://github.com/wasmerio/wasmer-postgres#usage--documentation + +[swift integration]: https://github.com/AlwaysRightInstitute/SwiftyWasmer + +[zig logo]: https://raw.githubusercontent.com/ziglang/logo/master/zig-favicon.png +[zig integration]: https://github.com/zigwasm/wasmer-zig + +[dart logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/dart.svg +[dart integration]: https://github.com/dart-lang/wasm +[`wasm` pub package]: https://pub.dev/packages/wasm + +[lisp integration]: https://github.com/helmutkian/cl-wasm-runtime + +[OCaml logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/ocaml.svg +[OCaml integration]: https://github.com/wasmerio/wasmer-ocaml +[`wasmer` OCaml package]: https://opam.ocaml.org/packages/wasmer/ + +## 기ė—Ŧ + +도ė›€ė„ ėŖŧė…”ė„œ 감ė‚Ŧ합니다! 💜 + +[WasmerëĨŧ 빌드](https://docs.wasmer.io/ecosystem/wasmer/building-from-source)하거나 [ëŗ€ę˛Ŋ ė‚Ŧ항ė„ 테ėŠ¤íŠ¸](https://docs.wasmer.io/ecosystem/wasmer/building-from-source/testing)하는 방법ė— 대한 ëŦ¸ė„œëĨŧ 확ė¸í•˜ė‹­ė‹œė˜¤. + +## ėģ¤ëŽ¤ë‹ˆí‹° +Wasmerė—ëŠ” 개발ėžė˜ 기ė—Ŧ가 ėžˆëŠ” 훌ëĨ­í•œ ėģ¤ëŽ¤ë‹ˆí‹°ę°€ ėžˆėŠĩ니다. 환ė˜í•Šë‹ˆë‹¤! ęŧ­ ė°¸ė—Ŧ해ėŖŧė„¸ėš”! 👋 + +- [Wasmer Community Slack](https://slack.wasmer.io/) +- [Wasmer on Twitter](https://twitter.com/wasmerio) +- [Wasmer on Facebook](https://www.facebook.com/wasmerio) +- [Email](mailto:hello@wasmer.io) diff --git a/arbitrator/tools/module_roots/wasmer/docs/migration_to_1.0.0.md b/arbitrator/tools/module_roots/wasmer/docs/migration_to_1.0.0.md new file mode 100644 index 0000000..5da6c35 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/docs/migration_to_1.0.0.md @@ -0,0 +1,358 @@ +# Migrating from Wasmer 0.x to Wasmer 1.0.0 + +Wasmer 1.0.0 is getting ready for a full release and is our primary focus. This document will +describe the differences between Wasmer 0.x and Wasmer 1.0.0 and provide examples +to make migrating to the new API as simple as possible. + +## Table of Contents + +- [Rationale for changes in 1.0.0](#rationale-for-changes-in-100) +- [How to use Wasmer 1.0.0](#how-to-use-wasmer-100) + - [Installing Wasmer CLI](#installing-wamser-cli) + - [Using Wasmer 1.0.0](#using-wamser-100) +- [Project structure](#project-structure) +- [Differences](#differences) + - [Instantiating modules](#instantiating-modules) + - [Passing host functions](#passing-host-functions) + - [Accessing the environment as a host function](#accessing-the-environment-as-a-host-function) + - [Error handling](#error-handling) + - [Caching modules](#caching-modules) + +## Rationale for changes in 1.0.0 + +Wasmer 0.x was great but as the Wasm community and standards evolve we felt the need to make Wasmer also follow these +changes. + +Wasmer 1.x is what we think a necessary rewrite of a big part of the project to make it more future-proof. + +This version introduces many new features and makes using Wasmer more natural. We did a hard work making it as close +to the standard API as possible while always providing good performances, flexibility and stability. + +The rewrite of the Wasmer Runtime also comes with a rewrite of the languages integrations to achieve the same goals: +providing a clearer API and improving the feature set. + +In this document you will discover the major changes between Wasmer 0.x and Wasmer 1.x by highlighting how to use the +new Rust API. + +## How to use Wasmer 1.0.0 + +### Installing Wasmer CLI + +See [wasmer.io] for installation instructions. + +If you already have wasmer installed, run `wasmer self-update`. + +Install the latest versions of Wasmer with [wasmer-nightly] or by following the steps described in the +documentation: [Getting Started][getting-started]. + +### Using Wasmer 1.0.0 + +The CLI interface for Wasmer 1.0.0 is mostly the same as it was in Wasmer 0.x. + +One difference is that rather than specifying the compiler with `--backend=cranelift`, +in Wasmer 1.0.0 we prefer using the name of the backend as a flag directly, +for example: `--cranelift`. + +The top-level crates that users will usually interface with are: + +- [wasmer] - Wasmer's core runtime API +- [wasmer-wasi] - Wasmer's WASI implementation +- [wasmer-emscripten] - Wasmer's Emscripten implementation +- TODO: + +See the [examples] to find out how to do specific things in Wasmer 1.0.0. + +## Project Structure + +![Wasmer dependencies graph](./deps_dedup.svg) + +The figure above shows the core Wasmer crates and their dependencies with transitive dependencies deduplicated. + +Wasmer 1.0.0 has two core architectural abstractions: engines and compilers. + +An engine is a system that processes Wasm with a compiler and prepares it to be executed. + +A compiler is a system that translates Wasm into a format that can be understood +more directly by a real computer: machine code. + +For example, in the [examples] you'll see that we are using the JIT engine and the Cranelift compiler. The JIT engine +will generate machine code at runtime, using Cranelift, and then execute it. + +For most uses, users will primarily use the [wasmer] crate directly, perhaps with one of our +provided ABIs such as [wasmer-wasi]. However, for users that need finer grained control over +the behavior of wasmer, other crates such as [wasmer-compiler] and [wasmer-engine] may be used +to implement custom compilers and engines respectively. + +## Differences + +### Instantiating modules + +With Wasmer 0.x, instantiating a module was a matter of calling `wasmer::compiler::compile` and then calling +`instantiate` on the compiled module. + +While simple, this did not give you full-control over Wasmer's configuration. For example, choosing another compiler +was not straightforward. + +With Wasmer 1.x, we changed this part and made the API look more like how Wasmer works internally to give you more +control: + +```diff +- let module = compile(&wasm_bytes[..])?; ++ let engine = JIT::new(Cranelift::default()).engine(); ++ let store = Store::new(&engine); ++ let module = Module::new(&store, wasm_bytes)?; +- let instance = module.instantiate(&imports)?; ++ let instance = Instance::new(&module, &import_object)?; +``` + +Note that we did not cover how to create the import object here. This is because this part works the same as it used to +with Wasmer 0.x. + +To get more information on how instantiation now works, have a look at the [dedicated example][instance-example] + +### Passing host functions + +With Wasmer 0.x passing host functions to the guest was primarily done using the `func!` macro or by directly using +`Func::new` or `DynamicFunc::new`. + +In Wasmer 1.0 the equivalent of `Func::new` is `Function::new_native` / +`Function::new_native_with_env` and the equivalent of `DynamicFunc::new` +is `Function::new` / `Function::new_with_env`. + +Given we have a function like: + +```rust +fn sum(a: i32, b: i32) -> i32 { + a + b +} +``` + +We want to import this function in the guest module, let's have a look at how it differs between Wasmer 0.x and +Wasmer 1.x: + +```diff +let import_object = imports! { + "env" => { +- "sum" => func!(sum), ++ "sum" => Function::new_native(&store, sum), + } +} +``` + +The above example illustrates how to import what we call "native functions". There were already available in Wasmer +0.x through the `func!` macro or with `Func::new`. + +There is a second flavor for imported functions: dynamic functions. With Wasmer 0.x you would have created such a +function using `DynamicFunc::new`, here is how it's done with Wasmer 1.x: + +```rust +let sum_signature = FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]); +let sum = Function::new(&store, &sum_signature, |args| { + let result = args[0].unwrap_I32() + args[1].unwrap_i32(); + + Ok(vec![Value::I32(result)]) +}); +``` + +Both examples address different needs and have their own pros and cons. We encourage you to have a look at the +dedicated example: [Exposing host functions][host-functions-example]. + +Note that having this API for functions now looks more like the other entities APIs: globals, memories, tables. Here is +a quick example introducing each of them: [Imports & Exports][imports-exports-example] + +### Accessing the environment as a host function + +With Wasmer 0.x each function had its own `vm::Ctx`. This was your entrypoint to the module internals and allowed +access to the context of the currently running instance. + +With Wasmer 1.0.0 this was changed to provide a simpler yet powerful API. + +Let's see an example where we want to have access to the module memory. Here is how that changed from 0.x to 1.0.0: + +```diff ++ #[derive(WasmerEnv, Clone, Default)] ++ struct MyEnv { ++ #[wasmer(export)] ++ memory: LazyInit, ++ } ++ let env = MyEnv::default(); ++ +- let get_at = |ctx: &mut vm::Ctx, ptr: WasmPtr, len: u32| { ++ let get_at = |env: &MyEnv, ptr: WasmPtr, len: u32| { +- let mem_desc = ctx.memory(0); +- let mem = mem_desc.deref(); ++ let mem = env.memory_ref().unwrap(); + + println!("Memory: {:?}", mem); + + let string = ptr.get_utf8_string(mem, len).unwrap(); + println!("string: {}", string); + }; + + let import_object = imports! { + "env" => { +- "get_at" => func!(get_at), ++ "get_at" => Function::new_native_with_env(&store, env.clone(), get_at), + } + }; + +- let instance = instantiate(wasm_bytes, &import_object)?; ++ let instance = Instance::new(&wasm_bytes, &import_object)?; +``` + +Here we have a module which provides one exported function: `get`. Each time we call this function it will in turn +call our imported function `get_at`. + +The `get_at` function is responsible for reading the guest module-s memory through the `vm::Ctx`. + +With Wasmer 1.0.0 (where the `vm::Ctx` does not exist anymore) we can achieve the same result with something more +natural: we only use imports and exports to read from the memory and write to it. + +However in order to provide an easy to use interface, we now have a trait +that can be implemented with a derive macro: `WasmerEnv`. We must make our +env types implement `WasmerEnv` and `Clone`. We mark an internal field +wrapped with `LazyInit` with `#[wasmer(export)]` to indicate that the type +should be found in the Wasm exports with the name of the field +(`"memory"`). The derive macro then generates helper functions such as +`memory_ref` on the type for easy access to these fields. + +See the [`WasmerEnv`](https://docs.rs/wasmer/*/wasmer/trait.WasmerEnv.html) +docs for more information. + +Take a look at the following examples to get more details: +* [Interacting with memory][memory] +* [Using memory pointers][memory-pointers] + +The other thing where `vm::Ctx` was useful was to pass data from and to host functions. This has also been made simpler +with Wasmer 1.x: + +```rust +let shared_counter: Arc> = Arc::new(RefCell::new(0)); + +#[derive(WasmerEnv, Clone)] +struct Env { + counter: Arc>, +} + +fn get_counter(env: &Env) -> i32 { + *env.counter.borrow() +} + +let get_counter_func = Function::new_native_with_env( + &store, + Env { counter: shared_counter.clone() }, + get_counter +); +``` + +A dedicated example describes how to use this feature: [Exposing host functions][host-functions]. + +### Error handling + +Handling errors with Wasmer 0.x was a bit hard, especially, the `wasmer_runtime::error::RuntimeError`. It was rather +complex: it had many variants that you had to handle when pattern matching results. This has been made way simpler with +Wasmer 1.0.0: + +```diff +// Retrieve the `get` function from module's exports and then call it +let result = get.call(0, 13); + +match result { +- Err(RuntimeError::InvokeError(InvokeError::TrapCode { .. })) => { +- // ... +- } +- Err(RuntimeError::InvokeError(InvokeError::FailedWithNoError)) => { +- // ... +- } +- Err(RuntimeError::InvokeError(InvokeError::UnknownTrap { .. })) => { +- // ... +- } +- Err(RuntimeError::InvokeError(InvokeError::UnknownTrapCode { .. })) => { +- // ... +- } +- Err(RuntimeError::InvokeError(InvokeError::EarlyTrap(_))) => { +- // ... +- } +- Err(RuntimeError::InvokeError(InvokeError::Breakpoint(_))) => { +- // ... +- } +- Err(RuntimeError::Metering(_)) => { +- // ... +- } +- Err(RuntimeError::InstanceImage(_)) => { +- // ... +- } +- Err(RuntimeError::User(_)) => { +- // ... +- } ++ Error(e) => { ++ println!("Error caught from `div_by_zero`: {}", e.message()); ++ ++ let frames = e.trace(); ++ let frames_len = frames.len(); ++ ++ // ... ++ } + Ok(_) => { + // ... + }, +} +``` + +As you can see here, handling errors is really easy now! You may find the following examples useful to get more familiar +with this topic: +* [Handling Errors][errors] +* [Interrupting Execution][exit-early] + +Note that with Wasmer 1.0.0, each function that is part of the API has its own kind of error. For example: +* Instantiating a module may return `InstantiationError`s; +* Getting exports from the guest module may return `ExportError`s; +* Calling an exported function may return `RuntimeError`s; +* ... + +### Caching modules + +You may be aware Wasmer, since 0.x, allows you to cache compiled module so that further executions of your program +will be faster. + +Because caching may bring a significant boost when running Wasm modules we wanted to make it easier to use with +Wasmer 1.0.0. + +With Wasmer 0.x you had to handle the whole caching process inside your program's code. With Wasmer 1.0.0 +you'll be able to delegate most of the work to Wasmer: + +```diff +- let artifact = module.cache().unwrap(); +- let bytes = artifact.serialize().unwrap(); +- +- let path = "module_cached.so"; +- fs::write(path, bytes).unwrap(); ++ module.serialize_to_file(path)?; + +- let mut file = File::open(path).unwrap(); +- let cached_bytes = &mut vec![]; +- file.read_to_end(cached_bytes); +- drop(file); +- +- let cached_artifact = Artifact::deserialize(&cached_bytes).unwrap(); +- let cached_module = unsafe { load_cache_with(cached_artifact, &default_compiler()) }.unwrap(); ++ let cached_module = unsafe { Module::deserialize_from_file(&store, path) }?; +``` + +[examples]: https://docs.wasmer.io/integrations/examples +[wasmer]: https://crates.io/crates/wasmer +[wasmer-wasi]: https://crates.io/crates/wasmer-wasi +[wasmer-emscripten]: https://crates.io/crates/wasmer-emscripten +[wasmer-engine]: https://crates.io/crates/wasmer-engine +[wasmer-compiler]: https://crates.io/crates/wasmer-compiler +[wasmer.io]: https://wasmer.io +[wasmer-nightly]: https://github.com/wasmerio/wasmer-nightly/ +[getting-started]: https://docs.wasmer.io/ecosystem/wasmer/getting-started +[instance-example]: https://docs.wasmer.io/integrations/examples/instance +[imports-exports-example]: https://docs.wasmer.io/integrations/examples/imports-and-exports +[host-functions-example]: https://docs.wasmer.io/integrations/examples/host-functions +[memory]: https://docs.wasmer.io/integrations/examples/memory +[memory-pointers]: https://docs.wasmer.io/integrations/examples/memory-pointers +[host-functions]: https://docs.wasmer.io/integrations/examples/host-functions +[errors]: https://docs.wasmer.io/integrations/examples/errors +[exit-early]: https://docs.wasmer.io/integrations/examples/exit-early diff --git a/arbitrator/tools/module_roots/wasmer/docs/migration_to_3.0.0.md b/arbitrator/tools/module_roots/wasmer/docs/migration_to_3.0.0.md new file mode 100644 index 0000000..91582cf --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/docs/migration_to_3.0.0.md @@ -0,0 +1,312 @@ +# Migrating from Wasmer 2.x to Wasmer 3.0.0 + +This document will describe the differences between Wasmer 2.x and Wasmer 3.0.0 +and provide examples to make migrating to the new API as simple as possible. + +## Table of Contents + +- [Rationale for changes in 3.0.0](#rationale-for-changes-in-300) +- [How to use Wasmer 3.0.0](#how-to-use-wasmer-300) + - [Installing Wasmer CLI](#installing-wamser-cli) + - [Using Wasmer 3.0.0](#using-wamser-300) +- [Project structure](#project-structure) +- [Differences](#differences) + - [Managing imports](#managing-imports) + - [Engines](#engines) + - [C-API changes](#c-api) + +## Rationale for changes in 3.0.0 + +This version introduces the following changes to make the Wasmer API more ergonomic and safe: + +1. `ImportsObject` and the traits `Resolver`, `NamedResolver`, etc have been removed and replaced with a single simple type `Imports`. This reduces the complexity of setting up an `Instance`. The helper macro `imports!` can still be used. +2. The `Store` will keep track of all memory and functions used, removing old tracking and Weak/Strong pointer usage. Every function and memory that can be defined is associated to a specific `Store`, and cannot be mixed with another `Store` +3. `NativeFunc` has been renamed to `TypedFunction`, accordingly the following functions have been renamed: + * `Function::native(â€Ļ)` → `Function::typed(â€Ļ)` + * `Function::new_native(â€Ļ)` → `Function::new_typed(â€Ļ)` + * `Function::new_native_with_env(â€Ļ)` → `Function::new_typed_with_env(â€Ļ)` + The previous variants still exist in order to support the migration, but they have been deprecated. +4. `WasmerEnv` and associated traits and macro have been removed. To use a function environment, you will need to create a `FunctionEnv` object and pass it along when you construct the function. For convenience, these functions also exist in a variant without the environment for simpler use cases that don't need it. For the variants with the environment, it can be retrieved from the first argument of the function. Because the `WasmerEnv` and all helpers don't exists anymore, you have to import memory yourself, there isn't any per instance initialisation automatically done anymore. It's especially important in wasi use with `WasiEnv`. `Env` can be accessed from a `FunctionEnvMut<'_, WasiEnv>` using `FunctionEnvMut::data()` or `FunctionEnvMut::data_mut()`. +5. The `Engine`s API has been simplified, Instead of the user choosing and setting up an engine explicitly, everything now uses a single engine. All functionalities of the `universal`, `staticlib` and `dylib` engines should be available in this new engine unless explicitly stated as unsupported. +6. Loupe has been removed. Memory inspection should be done manually + +## How to use Wasmer 3.0.0 + +### Installing Wasmer CLI + +See [wasmer.io] for installation instructions. + +If you already have wasmer installed, run `wasmer self-update`. + +Install the latest versions of Wasmer with [wasmer-nightly] or by following the +steps described in the documentation: [Getting Started][getting-started]. + +### Using Wasmer 3.0.0 + +One of the main changes in 3.0.0 is that `Store` now owns all WebAssembly objects; thus exports like a `Memory` are merely handles to the actual memory object inside the store. To read/write any such value you will always need a `Store` reference. + +If you define your own function, when the function is called it will hence need a reference to the store in order to access WebAssembly objects. This is achieved by the `StoreRef<'_>` and `StoreMut<'_>` types, which borrow from the store and provide access to its data. Furthermore, to prevent borrowing issues you can create new `StoreRef` and `StoreMut`s whenever you need to pass one at another function. This is done with the `AsStoreRef`, `AsStoreMut` traits. + +See the [examples] to find out how to do specific things in Wasmer 3.0.0. + +## Project Structure + +A lot of types were moved to `wasmer-types` crate. There are no `engine` crates anymore; all the logic is included in `wasmer-compiler`. + +## Differences + +### `WasmerEnv` is removed in favor of `FunctionEnv` + +`WasmerEnv` has been removed in Wasmer 3.0 in favor of `FunctionEnv`, which is now shareable automatically between functions without requiring the environment to be clonable. + +```rust +let my_counter = 0_i32; +let env = FunctionEnv::new(&mut store, my_counter); +``` + +Note: Any type can be passed as the environment: (*Nota bene* the passed type `T` must implement the `Any` trait, that is, any type which contains a non-`'static` reference.) + +```rust +struct Env { + counter: i32, +} +let env = FunctionEnv::new(&mut store, Env {counter: 0}); +``` + +Here's how the code depending on `WasmerEnv` should evolve: + +#### Before + +```rust +#[derive(wasmer::WasmerEnv, Clone)] +pub struct MyEnv { + #[wasmer(export)] + pub memory: wasmer::LazyInit, + #[wasmer(export(name = "__alloc"))] + pub alloc_guest_memory: LazyInit>, + + pub multiply_by: u32, +} + +let my_env = MyEnv { + memory: Default::default(), + alloc_guest_memory: Default::default(), + multiply_by: 10, +}; + +let instance = Instance::new(&module, &imports); +``` + +#### After + +```rust +pub struct MyEnv { + pub memory: Option, + pub alloc_guest_memory: Option>, + pub multiply_by: u32, +} + +let env = FunctionEnv::new(&mut store, MyEnv { + memory: None, + alloc_guest_memory: None, + multiply_by: 10, +}); + +let instance = Instance::new(&mut store, &module, &imports)?; +let mut env_mut = env.into_mut(&mut store); // change to a FunctionEnvMut +let (mut data_mut, mut store_mut) = env_mut.data_and_store_mut(); // grab data and a new store_mut +data_mut.memory = Some(instance.exports.get_memory("memory")?.clone()); +data_mut.alloc_guest_memory = Some(instance.exports.get_typed_function(&mut store_mut, "__alloc")?); +``` + +### New `MemoryView` API (preparation for shared memory) + +Reading from memory has slightly changed compared to 2.x: + +```rust +// 2.x +let memory = instance.exports.get_memory("mem")?; +println!("Memory size (pages) {:?}", memory.size()); +println!("Memory size (bytes) {:?}", memory.data_size()); + +let load = instance + .exports + .get_native_function::<(), (WasmPtr, i32)>("load")?; + +let (ptr, length) = load.call(&mut store)?; +let str = ptr.get_utf8_string(memory, length as u32).unwrap(); +println!("Memory contents: {:?}", str); +``` + +```rust +// 3.x +let memory = instance.exports.get_memory("mem")?; +let memory_view = memory.view(&store); +println!("Memory size (pages) {:?}", memory_view.size()); +println!("Memory size (bytes) {:?}", memory_view.data_size()); + +let load: TypedFunction<(), (WasmPtr, i32)> = + instance.exports.get_typed_function(&mut store, "load")?; + +let (ptr, length) = load.call(&mut store)?; +let memory_view = memory.view(&store); +let str = ptr.read_utf8_string(&memory_view, length as u32).unwrap(); +println!("Memory contents: {:?}", str); +``` + +The reason for this change is that in the future this will enable +safely sharing memory across threads. The same thing goes for reading slices: + +```rust +// 2.x +let new_str = b"Hello, Wasmer!"; +let values = ptr.deref(memory, 0, new_str.len() as u32).unwrap(); +for i in 0..new_str.len() { + values[i].set(new_str[i]); +} +``` + +```rust +// 3.x +let memory_view = memory.view(&store); // (can be reused) +let new_str = b"Hello, Wasmer!"; +let values = ptr.slice(&memory_view, new_str.len() as u32).unwrap(); +for i in 0..new_str.len() { + values.index(i as u64).write(new_str[i]).unwrap(); +} +``` + +### Managing imports + +Instantiating a Wasm module is similar to 2.x; + +```rust +let import_object: Imports = imports! { + "env" => { + "host_function" => host_function, + }, +}; +let instance = Instance::new(&mut store, &module, &import_object).expect("Could not instantiate module."); +``` + +You can also build the `Imports` object manually: + +```rust +let mut import_object: Imports = Imports::new(); +import_object.define("env", "host_function", host_function); +let instance = Instance::new(&mut store, &module, &import_object).expect("Could not instantiate module."); +``` + +For WASI, don't forget to initialize the `WasiEnv` (it will import the memory) + +```rust +let mut wasi_env = WasiState::builder("hello").finalize()?; +let import_object = wasi_env.import_object(&mut store, &module)?; +let instance = Instance::new(&mut store, &module, &import_object).expect("Could not instantiate module."); +wasi_env.initialize(&mut store, &instance).unwrap(); +``` + +#### `ChainableNamedResolver` is removed + +Chaining imports with a trait has been deemed too complex for what it does; it's possible to chain (i.e. override) an `Imports`' contents by using its implementation of `std::iter::Extend` from the Rust standard library: + +```rust +let imports1: Imports = todo!(); +let mut imports2: Imports = todo!(); + +imports2.extend(&imports); +// This is equivalent to the following: +// for ((ns, name), ext) in imports1.into_iter() { +// imports2.define(&ns &name, ext); +// } +``` + +### Engines + +#### Before + +In Wasmer 2.0, you had to explicitly define the Engine you want to use: + +```rust +let wasm_bytes = wat2wasm( + "..".as_bytes(), +)?; + +let compiler_config = Cranelift::default(); +let engine = Universal::new(compiler_config).engine(); +let mut store = Store::new(&engine); +let module = Module::new(&store, wasm_bytes)?; +let instance = Instance::new(&module, &imports! {})?; +``` + +#### After + +In Wasmer 3.0, there's only one engine. The user can ignore the engine details when using the API: + +```rust +let wasm_bytes = wat2wasm( + "..".as_bytes(), +)?; + +let compiler = Cranelift::default(); +let mut store = Store::new(compiler); +let module = Module::new(&store, wasm_bytes)?; +let instance = Instance::new(&mut store, &module, &imports! {})?; +``` + +#### Advanced configuration + +The previous ability to define target and features remains in a new `EngineBuilder` interface: + +```rust +let compiler = Cranelift::default(); + +let mut features = Features::new(); +// Enable the multi-value feature. +features.multi_value(true); + +let engine = EngineBuilder::new(compiler).set_features(Some(features)); +let store = Store::new(engine); +``` + +### C-API + +The WASM C-API hasn't changed. Some wasmer-specific functions have changed, that relate to setting up WASI environments. + +- `wasi_env_new` function changed input parameters to accommodate the new Store API, it now is: + ```C + struct wasi_env_t *wasi_env_new(wasm_store_t *store, struct wasi_config_t *config); + ``` +- `wasi_get_imports` function changed input parameters to accommodate the new Store API, it now is: + ```c + bool wasi_get_imports(const wasm_store_t *_store, + struct wasi_env_t *wasi_env, + const wasm_module_t *module, + wasm_extern_vec_t *imports); + ``` +- `wasi_env_set_memory` was added. It's necessary to set the `WasiEnv` memory by getting it from `Instance`s memory exports after its initialization. This must be performed in a specific order: + 1. Create WasiEnv + 2. Create Instance + 3. Get Instance Exports + 4. Find Memory from Instance Exports and store it to WasiEnv + The function's signature is: + ```c + void wasi_env_set_memory(struct wasi_env_t *env, const wasm_memory_t *memory); + ``` + +[examples]: https://docs.wasmer.io/integrations/examples +[wasmer]: https://crates.io/crates/wasmer +[wasmer-wasi]: https://crates.io/crates/wasmer-wasi +[wasmer-emscripten]: https://crates.io/crates/wasmer-emscripten +[wasmer-compiler]: https://crates.io/crates/wasmer-compiler +[wasmer.io]: https://wasmer.io +[wasmer-nightly]: https://github.com/wasmerio/wasmer-nightly/ +[getting-started]: https://docs.wasmer.io/ecosystem/wasmer/getting-started +[instance-example]: https://docs.wasmer.io/integrations/examples/instance +[imports-exports-example]: https://docs.wasmer.io/integrations/examples/imports-and-exports +[host-functions-example]: https://docs.wasmer.io/integrations/examples/host-functions +[memory]: https://docs.wasmer.io/integrations/examples/memory +[memory-pointers]: https://docs.wasmer.io/integrations/examples/memory-pointers +[host-functions]: https://docs.wasmer.io/integrations/examples/host-functions +[errors]: https://docs.wasmer.io/integrations/examples/errors +[exit-early]: https://docs.wasmer.io/integrations/examples/exit-early diff --git a/arbitrator/tools/module_roots/wasmer/examples/README.md b/arbitrator/tools/module_roots/wasmer/examples/README.md new file mode 100644 index 0000000..8502a7d --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/README.md @@ -0,0 +1,385 @@ +# Wasmer Examples + +This directory contains a collection of examples. This isn't an +exhaustive collection though, if one example is missing, please ask, +we will be happy to fulfill your needs! + +## Handy Diagrams + +As a quick introduction to Wasmer's main workflows, here are three +diagrams. We hope it provides an overview of how the crates assemble +together. + +1. **Module compilation**, illustrates how WebAssembly bytes are + validated, parsed, and compiled, with the help of the + `wasmer::Module`, `wasmer_engine::Engine`, and + `wasmer_compiler::Compiler` API. + + ![Module compilation](../assets/diagrams/Diagram_module_compilation.png) + +2. **Module serialization**, illustrates how a module can be + serialized and deserialized, with the help of + `wasmer::Module::serialize` and `wasmer::Module::deserialize`. The + important part is that the engine can changed between those two + steps, and thus how a headless engine can be used for the + deserialization. + + ![Module serialization](../assets/diagrams/Diagram_module_serialization.png) + +3. **Module instantiation**, illustrates what happens when + `wasmer::Instance::new` is called. + + ![Module instantiation](../assets/diagrams/Diagram_module_instantiation.png) + +## Examples + +The examples are written in a difficulty/discovery order. Concepts that +are explained in an example is not necessarily re-explained in a next +example. + +### Basics + +1. [**Hello World**][hello-world], explains the core concepts of the Wasmer + API for compiling and executing WebAssembly. + + _Keywords_: introduction, instance, module. + +
+ Execute the example + + ```shell + $ cargo run --example hello-world --release --features "cranelift" + ``` + +
+ +2. [**Instantiating a module**][instance], explains the basics of using Wasmer + and how to create an instance out of a Wasm module. + + _Keywords_: instance, module. + +
+ Execute the example + + ```shell + $ cargo run --example instance --release --features "cranelift" + ``` + +
+ +3. [**Handling errors**][errors], explains the basics of interacting with + Wasm module memory. + + _Keywords_: instance, error. + +
+ Execute the example + + ```shell + $ cargo run --example errors --release --features "cranelift" + ``` + +
+ +4. [**Interacting with memory**][memory], explains the basics of interacting with + Wasm module memory. + + _Keywords_: memory, module. + +
+ Execute the example + + ```shell + $ cargo run --example memory --release --features "cranelift" + ``` + +
+ +### Exports + +1. [**Exported global**][exported-global], explains how to work with + exported globals: get/set their value, have information about their + type. + + _Keywords_: export, global. + +
+ Execute the example + + ```shell + $ cargo run --example exported-global --release --features "cranelift" + ``` + +
+ +2. [**Exported function**][exported-function], explains how to get and + how to call an exported function. They come in 2 flavors: dynamic, + and “static”/native. The pros and cons are discussed briefly. + + _Keywords_: export, function, dynamic, static, native. + +
+ Execute the example + + ```shell + $ cargo run --example exported-function --release --features "cranelift" + ``` + +
+ + +3. [**Exported memory**][exported-memory], explains how to read from + and write to exported memory. + + _Keywords_: export, memory. + +
+ Execute the example + + ```shell + $ cargo run --example exported-memory --release --features "cranelift" + ``` + +
+ +### Imports + +1. [**Imported global**][imported-global], explains how to work with + imported globals: create globals, import them, get/set their value. + + _Keywords_: import, global. + +
+ Execute the example + + ```shell + $ cargo run --example imported-global --release --features "cranelift" + ``` + +
+ +2. [**Imported function**][imported-function], explains how to define + an imported function. They come in 2 flavors: dynamic, + and “static”/native. + + _Keywords_: import, function, dynamic, static, native. + +
+ Execute the example + + ```shell + $ cargo run --example imported-function --release --features "cranelift" + ``` + +
+ +### Externs + +1. [**Table**][table], explains how to use Wasm Tables from the Wasmer API. + + _Keywords_: basic, table, call_indirect + +
+ Execute the example + + ```shell + $ cargo run --example table --release --features "cranelift" + ``` + +
+ +2. [**Memory**][memory], explains how to use Wasm Memories from + the Wasmer API. Memory example is a work in progress. + + _Keywords_: basic, memory + +
+ Execute the example + + ```shell + $ cargo run --example memory --release --features "cranelift" + ``` + +
+ +### Tunables + +1. [**Limit memory**][tunables-limit-memory], explains how to use Tunables to limit the + size of an exported Wasm memory + + _Keywords_: basic, tunables, memory + +
+ Execute the example + + ```shell + $ cargo run --example tunables-limit-memory --release --features "cranelift" + ``` + +
+ +### Engines + +1. [**Engine**][engine], explains what an engine is and how to set it up. The + example completes itself with the compilation of the Wasm module, its + instantiation, and finally, by calling an exported function. + + _Keywords_: engine, in-memory, executable code. + +
+ Execute the example + + ```shell + $ cargo run --example engine --release --features "cranelift" + ``` + +
+ +2. [**Headless engines**][engine-headless], explains what a headless + engine is, what problem it does solve, and what are the benefits of + it. The example completes itself with the instantiation of a + pre-compiled Wasm module, and finally, by calling an exported + function. + + _Keywords_: native, engine, constrained environment, ahead-of-time + compilation, cross-compilation, executable code, serialization. + +
+ Execute the example + + ```shell + $ cargo run --example engine-headless --release --features "cranelift" + ``` + +
+ +4. [**Cross-compilation**][cross-compilation], illustrates the power + of the abstraction over the engines and the compilers, such as it + is possible to cross-compile a Wasm module for a custom target. + + _Keywords_: engine, compiler, cross-compilation. + +
+ Execute the example + + ```shell + $ cargo run --example cross-compilation --release --features "cranelift" + ``` + +
+ +5. [**Features**][features], illustrates how to enable WebAssembly + features that aren't yet stable. + + _Keywords_: engine, features. + +
+ Execute the example + + ```shell + $ cargo run --example features --release --features "cranelift" + ``` + +
+ +### Compilers + +1. [**Singlepass compiler**][compiler-singlepass], explains how to use + the [`wasmer-compiler-singlepass`] compiler. + + _Keywords_: compiler, singlepass. + +
+ Execute the example + + ```shell + $ cargo run --example compiler-singlepass --release --features "singlepass" + ``` + +
+ +2. [**Cranelift compiler**][compiler-cranelift], explains how to use + the [`wasmer-compiler-cranelift`] compiler. + + _Keywords_: compiler, cranelift. + +
+ Execute the example + + ```shell + $ cargo run --example compiler-cranelift --release --features "cranelift" + ``` + +
+ +3. [**LLVM compiler**][compiler-llvm], explains how to use the + [`wasmer-compiler-llvm`] compiler. + + _Keywords_: compiler, llvm. + +
+ Execute the example + + ```shell + $ cargo run --example compiler-llvm --release --features "llvm" + ``` + +
+ +### Integrations + +1. [**WASI**][wasi], explains how to use the [WebAssembly System + Interface][WASI] (WASI), i.e. the [`wasmer-wasi`] crate. + + _Keywords_: wasi, system, interface + +
+ Execute the example + + ```shell + $ cargo run --example wasi --release --features "cranelift,wasi" + ``` + +
+ +2. [**WASI Pipes**][wasi-pipes], builds on the WASI example to show off + stdio piping in Wasmer. + + _Keywords_: wasi, system, interface + +
+ Execute the example + + ```shell + $ cargo run --example wasi-pipes --release --features "cranelift,wasi" + ``` + +
+ +[hello-world]: ./hello_world.rs +[engine]: ./engine.rs +[engine-headless]: ./engine_headless.rs +[compiler-singlepass]: ./compiler_singlepass.rs +[compiler-cranelift]: ./compiler_cranelift.rs +[compiler-llvm]: ./compiler_llvm.rs +[cross-compilation]: ./engine_cross_compilation.rs +[exported-global]: ./exports_global.rs +[exported-function]: ./exports_function.rs +[exported-memory]: ./exports_memory.rs +[imported-global]: ./imports_global.rs +[imported-function]: ./imports_function.rs +[imported-function-env]: ./imports_function_env.rs +[imported-function-env-global]: ./imports_function_env_global.rs +[instance]: ./instance.rs +[wasi]: ./wasi.rs +[wasi-pipes]: ./wasi_pipes.rs +[table]: ./table.rs +[memory]: ./memory.rs +[errors]: ./errors.rs +[tunables-limit-memory]: ./tunables_limit_memory.rs +[features]: ./features.rs +[`wasmer-compiler-singlepass`]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-singlepass +[`wasmer-compiler-cranelift`]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-cranelift +[`wasmer-compiler-llvm`]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-llvm +[`wasmer-wasi`]: https://github.com/wasmerio/wasmer/tree/master/lib/wasi +[WASI]: https://github.com/WebAssembly/WASI diff --git a/arbitrator/tools/module_roots/wasmer/examples/compiler_cranelift.rs b/arbitrator/tools/module_roots/wasmer/examples/compiler_cranelift.rs new file mode 100644 index 0000000..cb8b8f9 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/compiler_cranelift.rs @@ -0,0 +1,65 @@ +//! A Wasm module can be compiled with multiple compilers. +//! +//! This example illustrates how to use the Cranelift compiler. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example compiler-cranelift --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, Instance, Module, Store, Value}; +use wasmer_compiler_cranelift::Cranelift; + +fn main() -> Result<(), Box> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + r#" +(module + (type $sum_t (func (param i32 i32) (result i32))) + (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) + local.get $x + local.get $y + i32.add) + (export "sum" (func $sum_f))) +"# + .as_bytes(), + )?; + + // Use Cranelift compiler with the default settings + let compiler = Cranelift::default(); + + // Create the store + let mut store = Store::new(compiler); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // Create an empty import object. + let import_object = imports! {}; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + let sum = instance.exports.get_function("sum")?; + + println!("Calling `sum` function..."); + // Let's call the `sum` exported function. The parameters are a + // slice of `Value`s. The results are a boxed slice of `Value`s. + let results = sum.call(&mut store, &[Value::I32(1), Value::I32(2)])?; + + println!("Results: {:?}", results); + assert_eq!(results.to_vec(), vec![Value::I32(3)]); + + Ok(()) +} + +#[test] +#[cfg(feature = "cranelift")] +fn test_compiler_cranelift() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/compiler_llvm.rs b/arbitrator/tools/module_roots/wasmer/examples/compiler_llvm.rs new file mode 100644 index 0000000..2622227 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/compiler_llvm.rs @@ -0,0 +1,65 @@ +//! A Wasm module can be compiled with multiple compilers. +//! +//! This example illustrates how to use the LLVM compiler. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example compiler-llvm --release --features "llvm" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, Instance, Module, Store, Value}; +use wasmer_compiler_llvm::LLVM; + +fn main() -> Result<(), Box> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + r#" +(module + (type $sum_t (func (param i32 i32) (result i32))) + (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) + local.get $x + local.get $y + i32.add) + (export "sum" (func $sum_f))) +"# + .as_bytes(), + )?; + + // Use LLVM compiler with the default settings + let compiler = LLVM::default(); + + // Create the store + let mut store = Store::new(compiler); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // Create an empty import object. + let import_object = imports! {}; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + let sum = instance.exports.get_function("sum")?; + + println!("Calling `sum` function..."); + // Let's call the `sum` exported function. The parameters are a + // slice of `Value`s. The results are a boxed slice of `Value`s. + let results = sum.call(&mut store, &[Value::I32(1), Value::I32(2)])?; + + println!("Results: {:?}", results); + assert_eq!(results.to_vec(), vec![Value::I32(3)]); + + Ok(()) +} + +#[test] +#[cfg(feature = "llvm")] +fn test_compiler_llvm() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/compiler_singlepass.rs b/arbitrator/tools/module_roots/wasmer/examples/compiler_singlepass.rs new file mode 100644 index 0000000..01fb181 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/compiler_singlepass.rs @@ -0,0 +1,65 @@ +//! A Wasm module can be compiled with multiple compilers. +//! +//! This example illustrates how to use the Singlepass compiler. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example compiler-singlepass --release --features "singlepass" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, Instance, Module, Store, Value}; +use wasmer_compiler_singlepass::Singlepass; + +fn main() -> Result<(), Box> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + r#" +(module + (type $sum_t (func (param i32 i32) (result i32))) + (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) + local.get $x + local.get $y + i32.add) + (export "sum" (func $sum_f))) +"# + .as_bytes(), + )?; + + // Use Singlepass compiler with the default settings + let compiler = Singlepass::default(); + + // Create the store + let mut store = Store::new(compiler); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // Create an empty import object. + let import_object = imports! {}; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + let sum = instance.exports.get_function("sum")?; + + println!("Calling `sum` function..."); + // Let's call the `sum` exported function. The parameters are a + // slice of `Value`s. The results are a boxed slice of `Value`s. + let results = sum.call(&mut store, &[Value::I32(1), Value::I32(2)])?; + + println!("Results: {:?}", results); + assert_eq!(results.to_vec(), vec![Value::I32(3)]); + + Ok(()) +} + +#[test] +#[cfg(feature = "singlepass")] +fn test_compiler_singlepass() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/early_exit.rs b/arbitrator/tools/module_roots/wasmer/examples/early_exit.rs new file mode 100644 index 0000000..95d9a80 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/early_exit.rs @@ -0,0 +1,110 @@ +//! There are cases where you may want to interrupt this synchronous execution of the Wasm module +//! while the it is calling a host function. This can be useful for saving resources, and not +//! returning back to the guest Wasm for execution, when you already know the Wasm execution will +//! fail, or no longer be needed. +//! +//! In this example, we will run a Wasm module that calls the imported host function +//! interrupt_execution. This host function will immediately stop executing the WebAssembly module. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example early-exit --release --features "cranelift" +//! ``` +//! +//! Ready? + +use anyhow::bail; +use std::fmt; +use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, TypedFunction}; + +// First we need to create an error type that we'll use to signal the end of execution. +#[derive(Debug, Clone, Copy)] +struct ExitCode(u32); + +// This type must implement `std::error::Error` so we must also implement `std::fmt::Display` for it. +impl fmt::Display for ExitCode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +// And then we implement `std::error::Error`. +impl std::error::Error for ExitCode {} + +fn main() -> anyhow::Result<()> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + br#" +(module + (type $run_t (func (param i32 i32) (result i32))) + (type $early_exit_t (func (param) (result))) + (import "env" "early_exit" (func $early_exit (type $early_exit_t))) + (func $run (type $run_t) (param $x i32) (param $y i32) (result i32) + (call $early_exit) + (i32.add + local.get $x + local.get $y)) + (export "run" (func $run))) +"#, + )?; + + // Create a Store. + let mut store = Store::default(); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // We declare the host function that we'll use to terminate execution. + fn early_exit() -> Result<(), ExitCode> { + // This is where it happens. + Err(ExitCode(1)) + } + + // Create an import object. + let import_object = imports! { + "env" => { + "early_exit" => Function::new_typed(&mut store, early_exit), + } + }; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + // Here we go. + // + // Get the `run` function which we'll use as our entrypoint. + println!("Calling `run` function..."); + let run_func: TypedFunction<(i32, i32), i32> = + instance.exports.get_typed_function(&mut store, "run")?; + + // When we call a function it can either succeed or fail. We expect it to fail. + match run_func.call(&mut store, 1, 7) { + Ok(result) => { + bail!( + "Expected early termination with `ExitCode`, found: {}", + result + ); + } + // In case of a failure, which we expect, we attempt to downcast the error into the error + // type that we were expecting. + Err(e) => match e.downcast::() { + // We found the exit code used to terminate execution. + Ok(exit_code) => { + println!("Exited early with exit code: {}", exit_code); + + Ok(()) + } + Err(e) => { + bail!("Unknown error `{}` found. expected `ErrorCode`", e); + } + }, + } +} + +#[test] +fn test_early_exit() -> anyhow::Result<()> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/engine.rs b/arbitrator/tools/module_roots/wasmer/examples/engine.rs new file mode 100644 index 0000000..1bb2001 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/engine.rs @@ -0,0 +1,90 @@ +//! Defining an engine in Wasmer is one of the fundamental steps. +//! +//! This example illustrates how to use the `wasmer_compiler`, +//! aka the Universal engine. An engine applies roughly 2 steps: +//! +//! 1. It compiles the Wasm module bytes to executable code, through +//! the intervention of a compiler, +//! 2. It stores the executable code somewhere. +//! +//! In the particular context of the Universal engine, the executable +//! code is stored in memory. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example engine-universal --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, EngineBuilder, Instance, Module, Store, Value}; +use wasmer_compiler_cranelift::Cranelift; + +fn main() -> Result<(), Box> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + r#" +(module + (type $sum_t (func (param i32 i32) (result i32))) + (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) + local.get $x + local.get $y + i32.add) + (export "sum" (func $sum_f))) +"# + .as_bytes(), + )?; + + // Define a compiler configuration. + // + // In this situation, the compiler is + // `wasmer_compiler_cranelift`. The compiler is responsible to + // compile the Wasm module into executable code. + let compiler_config = Cranelift::default(); + + println!("Creating Universal engine..."); + // Define the engine that will drive everything. + // + // In this case, the engine is `wasmer_compiler` which roughly + // means that the executable code will live in memory. + let engine = EngineBuilder::new(compiler_config); + + // Create a store, that holds the engine. + let mut store = Store::new(engine); + + println!("Compiling module..."); + // Here we go. + // + // Let's compile the Wasm module. It is at this step that the Wasm + // text is transformed into Wasm bytes (if necessary), and then + // compiled to executable code by the compiler, which is then + // stored in memory by the engine. + let module = Module::new(&store, wasm_bytes)?; + + // Congrats, the Wasm module is compiled! Now let's execute it for + // the sake of having a complete example. + + // Create an import object. Since our Wasm module didn't declare + // any imports, it's an empty object. + let import_object = imports! {}; + + println!("Instantiating module..."); + // And here we go again. Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + println!("Calling `sum` function..."); + // The Wasm module exports a function called `sum`. + let sum = instance.exports.get_function("sum")?; + let results = sum.call(&mut store, &[Value::I32(1), Value::I32(2)])?; + + println!("Results: {:?}", results); + assert_eq!(results.to_vec(), vec![Value::I32(3)]); + + Ok(()) +} + +#[test] +fn test_engine_universal() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/engine_cross_compilation.rs b/arbitrator/tools/module_roots/wasmer/examples/engine_cross_compilation.rs new file mode 100644 index 0000000..9021694 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/engine_cross_compilation.rs @@ -0,0 +1,101 @@ +//! Defining an engine in Wasmer is one of the fundamental steps. +//! +//! As a reminder, an engine applies roughly 2 steps: +//! +//! 1. It compiles the Wasm module bytes to executable code, through +//! the intervention of a compiler, +//! 2. It stores the executable code somewhere. +//! +//! This example focuses on the first step: the compiler. It +//! illustrates how the abstraction over the compiler is so powerful +//! that it is possible to cross-compile a Wasm module. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example cross-compilation --release --features "cranelift" +//! ``` +//! +//! Ready? + +use std::str::FromStr; +use wasmer::{wat2wasm, EngineBuilder, Module, RuntimeError, Store}; +use wasmer_compiler_cranelift::Cranelift; +use wasmer_types::{CpuFeature, Target, Triple}; + +fn main() -> Result<(), Box> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + br#" +(module + (type $sum_t (func (param i32 i32) (result i32))) + (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) + local.get $x + local.get $y + i32.add) + (export "sum" (func $sum_f))) +"#, + )?; + + // Define a compiler configuration. + // + // In this situation, the compiler is + // `wasmer_compiler_cranelift`. The compiler is responsible to + // compile the Wasm module into executable code. + let compiler_config = Cranelift::default(); + + // Here we go. + // + // Let's define the target “triple”. Historically, such things had + // three fields, though additional fields have been added over + // time. + let triple = Triple::from_str("x86_64-linux-musl") + .map_err(|error| RuntimeError::new(error.to_string()))?; + + // Here we go again. + // + // Let's define a CPU feature. + let mut cpu_feature = CpuFeature::set(); + cpu_feature.insert(CpuFeature::from_str("sse2")?); + + // Here we go finally. + // + // Let's build the target. + let target = Target::new(triple, cpu_feature); + println!("Chosen target: {:?}", target); + + // Define the engine that will drive everything. + // + // That's where we specify the target for the compiler. + // + // Use the Universal engine. + let engine = EngineBuilder::new(compiler_config).set_target(Some(target)); + + // Create a store, that holds the engine. + let store = Store::new(engine); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let _module = Module::new(&store, wasm_bytes)?; + + println!("Module compiled successfully."); + + // Congrats, the Wasm module is cross-compiled! + // + // What to do with that? It is possible to use an engine (probably + // a headless engine) to execute the cross-compiled Wasm module an + // the targeted platform. + + Ok(()) +} + +#[test] +#[cfg(not(any( + windows, + // We don't support yet crosscompilation in macOS with Apple Silicon + all(target_os = "macos", target_arch = "aarch64"), + target_env = "musl", +)))] +fn test_cross_compilation() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/engine_dylib.rs b/arbitrator/tools/module_roots/wasmer/examples/engine_dylib.rs new file mode 100644 index 0000000..07fa3cd --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/engine_dylib.rs @@ -0,0 +1,96 @@ +//! Defining an engine in Wasmer is one of the fundamental steps. +//! +//! This example illustrates how to use the `wasmer_engine_dylib`, +//! aka the Dylib engine. An engine applies roughly 2 steps: +//! +//! 1. It compiles the Wasm module bytes to executable code, through +//! the intervention of a compiler, +//! 2. It stores the executable code somewhere. +//! +//! In the particular context of the Dylib engine, the executable code +//! is stored in a shared object (`.dylib`, `.so` or `.dll` file). +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example engine-dylib --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, Instance, Module, Store, Value}; +use wasmer_compiler_cranelift::Cranelift; +/* +use wasmer_engine_dylib::Dylib; +*/ + +fn main() -> Result<(), Box> { + /* + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + r#" + (module + (type $sum_t (func (param i32 i32) (result i32))) + (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) + local.get $x + local.get $y + i32.add) + (export "sum" (func $sum_f))) + "# + .as_bytes(), + )?; + + // Define a compiler configuration. + // + // In this situation, the compiler is + // `wasmer_compiler_cranelift`. The compiler is responsible to + // compile the Wasm module into executable code. + let compiler_config = Cranelift::default(); + + println!("Creating Dylib engine..."); + // Define the engine that will drive everything. + // + // In this case, the engine is `wasmer_engine_dylib` which means + // that a shared object is going to be generated. + let engine = Dylib::new(compiler_config); + + // Create a store, that holds the engine. + let mut store = Store::new(engine); + + println!("Compiling module..."); + // Here we go. + // + // Let's compile the Wasm module. It is at this step that the Wasm + // text is transformed into Wasm bytes (if necessary), and then + // compiled to executable code by the compiler, which is then + // stored into a shared object by the engine. + let module = Module::new(&store, wasm_bytes)?; + + // Congrats, the Wasm module is compiled! Now let's execute it for + // the sake of having a complete example. + + // Create an import object. Since our Wasm module didn't declare + // any imports, it's an empty object. + let import_object = imports! {}; + + println!("Instantiating module..."); + // And here we go again. Let's instantiate the Wasm module. + let instance = Instance::new(&module, &import_object)?; + + println!("Calling `sum` function..."); + // The Wasm module exports a function called `sum`. + let sum = instance.exports.get_function("sum")?; + let results = sum.call(&[Value::I32(1), Value::I32(2)])?; + + println!("Results: {:?}", results); + assert_eq!(results.to_vec(), vec![Value::I32(3)]); + */ + + Ok(()) +} + +#[test] +#[cfg(not(any(target_arch = "aarch64", target_env = "musl")))] +fn test_engine_dylib() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/engine_headless.rs b/arbitrator/tools/module_roots/wasmer/examples/engine_headless.rs new file mode 100644 index 0000000..c0cbf2b --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/engine_headless.rs @@ -0,0 +1,136 @@ +//! Defining an engine in Wasmer is one of the fundamental steps. +//! +//! This example illustrates a neat feature of engines: their ability +//! to run in a headless mode. At the time of writing, all engines +//! have a headless mode, but it's not a requirement of the `Engine` +//! trait (defined in the `wasmer_engine` crate). +//! +//! What problem does it solve, and what does it mean? +//! +//! Once a Wasm module is compiled into executable code and stored +//! somewhere (e.g. in memory with the Universal engine), the module +//! can be instantiated and executed. But imagine for a second the +//! following scenario: +//! +//! * Modules are compiled ahead of time, to be instantiated later +//! on. +//! * Modules are cross-compiled on a machine ahead of time +//! to be run on another machine later one. +//! +//! In both scenarios, the environment where the compiled Wasm module +//! will be executed can be very constrained. For such particular +//! contexts, Wasmer can be compiled _without_ the compilers, so that +//! the `wasmer` binary is as small as possible. Indeed, there is no +//! need for a compiler since the Wasm module is already compiled. All +//! we need is an engine that _only_ drives the instantiation and +//! execution of the Wasm module. +//! +//! And that, that's a headless engine. +//! +//! To achieve such a scenario, a Wasm module must be compiled, then +//! serialized —for example into a file—, then later, potentially on +//! another machine, deserialized. The next steps are classical: The +//! Wasm module is instantiated and executed. +//! +//! This example uses a `compiler` because it illustrates the entire +//! workflow, but keep in mind the compiler isn't required after the +//! compilation step. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example engine-headless --release --features "cranelift" +//! ``` +//! +//! Ready? + +use tempfile::NamedTempFile; +use wasmer::{imports, wat2wasm, EngineBuilder, Instance, Module, Store, Value}; +use wasmer_compiler_cranelift::Cranelift; + +fn main() -> Result<(), Box> { + // First step, let's compile the Wasm module and serialize it. + // Note: we need a compiler here. + let serialized_module_file = { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + r#" +(module + (type $sum_t (func (param i32 i32) (result i32))) + (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) + local.get $x + local.get $y + i32.add) + (export "sum" (func $sum_f))) +"# + .as_bytes(), + )?; + + // Define a compiler configuration. + // + // In this situation, the compiler is + // `wasmer_compiler_cranelift`. The compiler is responsible to + // compile the Wasm module into executable code. + let compiler = Cranelift::default(); + + // Create a store, that holds the engine. + let store = Store::new(compiler); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + println!("Serializing module..."); + // Here we go. Let's serialize the compiled Wasm module in a + // file. + let serialized_module_file = NamedTempFile::new()?; + module.serialize_to_file(&serialized_module_file)?; + + serialized_module_file + }; + + // Second step, deserialize the compiled Wasm module, and execute + // it, for example with Wasmer without a compiler. + { + println!("Creating headless Universal engine..."); + // We create a headless Universal engine. + let engine = EngineBuilder::headless(); + let mut store = Store::new(engine); + + println!("Deserializing module..."); + // Here we go. + // + // Deserialize the compiled Wasm module. This code is unsafe + // because Wasmer can't assert the bytes are valid (see the + // `wasmer::Module::deserialize`'s documentation to learn + // more). + let module = unsafe { Module::deserialize_from_file(&store, serialized_module_file) }?; + + // Congrats, the Wasm module has been deserialized! Now let's + // execute it for the sake of having a complete example. + + // Create an import object. Since our Wasm module didn't declare + // any imports, it's an empty object. + let import_object = imports! {}; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + println!("Calling `sum` function..."); + // The Wasm module exports a function called `sum`. + let sum = instance.exports.get_function("sum")?; + let results = sum.call(&mut store, &[Value::I32(1), Value::I32(2)])?; + + println!("Results: {:?}", results); + assert_eq!(results.to_vec(), vec![Value::I32(3)]); + } + + Ok(()) +} + +#[test] +#[cfg(not(any(windows, target_arch = "aarch64", target_env = "musl")))] +fn test_engine_headless() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/errors.rs b/arbitrator/tools/module_roots/wasmer/examples/errors.rs new file mode 100644 index 0000000..c034c40 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/errors.rs @@ -0,0 +1,101 @@ +//! A Wasm module can sometimes be invalid or trigger traps, and in those case we will get +//! an error back from the API. +//! +//! In this example we'll see how to handle such errors in the most +//! basic way. To do that we'll use a Wasm module that we know will +//! produce an error. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example errors --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, Instance, Module, Store, TypedFunction}; + +fn main() -> Result<(), Box> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + br#" +(module + (type $do_div_by_zero_t (func (result i32))) + (func $do_div_by_zero_f (type $do_div_by_zero_t) (result i32) + i32.const 4 + i32.const 0 + i32.div_s) + + (type $div_by_zero_t (func (result i32))) + (func $div_by_zero_f (type $div_by_zero_t) (result i32) + call $do_div_by_zero_f) + (export "div_by_zero" (func $div_by_zero_f))) +"#, + )?; + + // Create a Store. + // Note that we don't need to specify the engine/compiler if we want to use + // the default provided by Wasmer. + // You can use `Store::default()` for that. + let mut store = Store::default(); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // Create an import object. + let import_object = imports! {}; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + // Here we go. + // + // The Wasm module exports a function called `div_by_zero`. As its name + // implies, this function will try to do a division by zero and thus + // produce an error. + // + // Let's get it. + let div_by_zero: TypedFunction<(), i32> = instance + .exports + .get_function("div_by_zero")? + .typed(&mut store)?; + + println!("Calling `div_by_zero` function..."); + // Let's call the `div_by_zero` exported function. + let result = div_by_zero.call(&mut store); + + // When we call a function it can either succeed or fail. We expect it to fail. + match result { + Ok(_) => { + // This should have thrown an error, return an error + panic!("div_by_zero did not error"); + } + Err(e) => { + // Log the error + println!("Error caught from `div_by_zero`: {}", e.message()); + + // Errors come with a trace we can inspect to get more + // information on the execution flow. + let frames = e.trace(); + let frames_len = frames.len(); + + for i in 0..frames_len { + println!( + " Frame #{}: {:?}::{:?}", + frames_len - i, + frames[i].module_name(), + frames[i].function_name().or(Some("")).unwrap() + ); + } + } + } + + Ok(()) +} + +#[test] +fn test_exported_function() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/exports_function.rs b/arbitrator/tools/module_roots/wasmer/examples/exports_function.rs new file mode 100644 index 0000000..e984322 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/exports_function.rs @@ -0,0 +1,107 @@ +//! A Wasm module can export entities, like functions, memories, +//! globals and tables. +//! +//! This example illustrates how to use exported functions. They come +//! in 2 flavors: +//! +//! 1. Dynamic functions, where parameters and results are of a +//! slice of `Value`, +//! 2. Native function, where parameters and results are statically +//! typed Rust values. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example exported-function --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, Instance, Module, Store, TypedFunction, Value}; + +fn main() -> Result<(), Box> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + r#" +(module + (type $sum_t (func (param i32 i32) (result i32))) + (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) + local.get $x + local.get $y + i32.add) + (export "sum" (func $sum_f))) +"# + .as_bytes(), + )?; + + // Create a Store. + let mut store = Store::default(); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // Create an empty import object. + let import_object = imports! {}; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + // Here we go. + // + // The Wasm module exports a function called `sum`. Let's get + // it. Note that + // + // ``` + // get_function(name) + // ``` + // + // is just an alias to + // + // ``` + // get::(name)`. + // ``` + let sum = instance.exports.get_function("sum")?; + + println!("Calling `sum` function..."); + // Let's call the `sum` exported function. The parameters are a + // slice of `Value`s. The results are a boxed slice of `Value`s. + let args = [Value::I32(1), Value::I32(2)]; + let result = sum.call(&mut store, &args)?; + + println!("Results: {:?}", result); + assert_eq!(result.to_vec(), vec![Value::I32(3)]); + + // That was fun. But what if we can get rid of the `Value`s? Well, + // that's possible with the `TypedFunction` API. The function + // will use native Rust values. + // + // Note that `typed` takes 2 generic parameters: `Args` and + // `Rets`, respectively for the parameters and the results. If + // those values don't match the exported function signature, an + // error will be raised. + let sum_typed: TypedFunction<(i32, i32), i32> = sum.typed(&mut store)?; + + println!("Calling `sum` function (natively)..."); + // Let's call the `sum` exported function. The parameters are + // statically typed Rust values of type `i32` and `i32`. The + // result, in this case particular case, in a unit of type `i32`. + let result = sum_typed.call(&mut store, 3, 4)?; + + println!("Results: {:?}", result); + assert_eq!(result, 7); + + // Much nicer, isn't it? + // + // Those two API exist because they address different needs. The + // former has a more dynamic approach, while the second has a more + // static approach. + + Ok(()) +} + +#[test] +fn test_exported_function() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/exports_global.rs b/arbitrator/tools/module_roots/wasmer/examples/exports_global.rs new file mode 100644 index 0000000..cdb8455 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/exports_global.rs @@ -0,0 +1,140 @@ +//! A Wasm module can export entities, like functions, memories, +//! globals and tables. +//! +//! This example illustrates how to use exported globals. They come +//! in 2 flavors: +//! +//! 1. Immutable globals (const), +//! 2. Mutable globals. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example exported-global --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, Instance, Module, Mutability, Store, Type, TypedFunction, Value}; + +fn main() -> Result<(), Box> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + br#" +(module + (global $one (export "one") f32 (f32.const 1)) + (global $some (export "some") (mut f32) (f32.const 0)) + + (func (export "get_one") (result f32) (global.get $one)) + (func (export "get_some") (result f32) (global.get $some)) + + (func (export "set_some") (param f32) (global.set $some (local.get 0)))) +"#, + )?; + + // Create a Store. + let mut store = Store::default(); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // Create an empty import object. + let import_object = imports! {}; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + // Here we go. + // + // The Wasm module exports some globals. Let's get them. + // Note that + // + // ``` + // get_global(name) + // ``` + // + // is just an alias to + // + // ``` + // get::(name)`. + // ``` + let one = instance.exports.get_global("one")?; + let some = instance.exports.get_global("some")?; + + println!("Getting globals types information..."); + // Let's get the globals types. The results are `GlobalType`s. + let one_type = one.ty(&store); + let some_type = some.ty(&store); + + println!("`one` type: {:?} {:?}", one_type.mutability, one_type.ty); + assert_eq!(one_type.mutability, Mutability::Const); + assert_eq!(one_type.ty, Type::F32); + + println!("`some` type: {:?} {:?}", some_type.mutability, some_type.ty); + assert_eq!(some_type.mutability, Mutability::Var); + assert_eq!(some_type.ty, Type::F32); + + println!("Getting global values..."); + // Getting the values of globals can be done in two ways: + // 1. Through an exported function, + // 2. Using the Global API directly. + // + // We will use an exported function for the `one` global + // and the Global API for `some`. + let get_one: TypedFunction<(), f32> = instance + .exports + .get_function("get_one")? + .typed(&mut store)?; + + let one_value = get_one.call(&mut store)?; + let some_value = some.get(&mut store); + + println!("`one` value: {:?}", one_value); + assert_eq!(one_value, 1.0); + + println!("`some` value: {:?}", some_value); + assert_eq!(some_value, Value::F32(0.0)); + + println!("Setting global values..."); + // Trying to set the value of a immutable global (`const`) + // will result in a `RuntimeError`. + let result = one.set(&mut store, Value::F32(42.0)); + // The global is immutable + assert!(result.is_err()); + // assert_eq!( + // result.expect_err("Expected an error").message(), + // "Attempted to set an immutable global" + // ); + + let one_result = one.get(&mut store); + println!("`one` value after `set`: {:?}", one_result); + assert_eq!(one_result, Value::F32(1.0)); + + // Setting the values of globals can be done in two ways: + // 1. Through an exported function, + // 2. Using the Global API directly. + // + // We will use both for the `some` global. + let set_some: TypedFunction = instance + .exports + .get_function("set_some")? + .typed(&mut store)?; + set_some.call(&mut store, 21.0)?; + let some_result = some.get(&mut store); + println!("`some` value after `set_some`: {:?}", some_result); + assert_eq!(some_result, Value::F32(21.0)); + + some.set(&mut store, Value::F32(42.0))?; + let some_result = some.get(&mut store); + println!("`some` value after `set`: {:?}", some_result); + assert_eq!(some_result, Value::F32(42.0)); + + Ok(()) +} + +#[test] +fn test_exported_global() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/exports_memory.rs b/arbitrator/tools/module_roots/wasmer/examples/exports_memory.rs new file mode 100644 index 0000000..3990efc --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/exports_memory.rs @@ -0,0 +1,113 @@ +//! A Wasm module can export entities, like functions, memories, +//! globals and tables. +//! +//! This example illustrates how to use exported memories +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example exported-memory --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, Instance, Module, Store, TypedFunction, WasmPtr}; + +fn main() -> Result<(), Box> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + br#" +(module + (memory (export "mem") 1) + + (global $offset i32 (i32.const 42)) + (global $length (mut i32) (i32.const 13)) + + (func (export "load") (result i32 i32) + global.get $offset + global.get $length) + + (data (i32.const 42) "Hello, World!")) +"#, + )?; + + // Create a Store. + let mut store = Store::default(); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // Create an empty import object. + let import_object = imports! {}; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + let load: TypedFunction<(), (WasmPtr, i32)> = + instance.exports.get_typed_function(&mut store, "load")?; + + // Here we go. + // + // The Wasm module exports a memory under "mem". Let's get it. + let memory = instance.exports.get_memory("mem")?; + + // Now that we have the exported memory, let's get some + // information about it. + // + // The first thing we might be intersted in is the size of the memory. + // Let's get it! + let memory_view = memory.view(&store); + println!("Memory size (pages) {:?}", memory_view.size()); + println!("Memory size (bytes) {:?}", memory_view.data_size()); + + // Oh! Wait, before reading the contents, we need to know + // where to find what we are looking for. + // + // Fortunately, the Wasm module exports a `load` function + // which will tell us the offset and length of the string. + let (ptr, length) = load.call(&mut store)?; + println!("String offset: {:?}", ptr.offset()); + println!("String length: {:?}", length); + + // We now know where to find our string, let's read it. + // + // We will get bytes out of the memory so we need to + // decode them into a string. + let memory_view = memory.view(&store); + let str = ptr.read_utf8_string(&memory_view, length as u32).unwrap(); + println!("Memory contents: {:?}", str); + + // What about changing the contents of the memory with a more + // appropriate string? + // + // To do that, we'll make a slice from our pointer and change the content + // of each element. + let new_str = b"Hello, Wasmer!"; + let values = ptr.slice(&memory_view, new_str.len() as u32).unwrap(); + for i in 0..new_str.len() { + values.index(i as u64).write(new_str[i]).unwrap(); + } + + // And now, let's see the result. + // + // Since the new strings is bigger than the older one, we + // query the length again. The offset remains the same as + // before. + println!("New string length: {:?}", new_str.len()); + + let str = ptr + .read_utf8_string(&memory_view, new_str.len() as u32) + .unwrap(); + println!("New memory contents: {:?}", str); + + // Much better, don't you think? + + Ok(()) +} + +#[test] +fn test_exported_memory() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/features.rs b/arbitrator/tools/module_roots/wasmer/examples/features.rs new file mode 100644 index 0000000..28ea86e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/features.rs @@ -0,0 +1,60 @@ +//! WebAssembly is a living standard. Wasmer integrates some +//! WebAssembly features that aren't yet stable but can still be +//! turned on. This example explains how. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example features --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, EngineBuilder, Features, Instance, Module, Store, Value}; +use wasmer_compiler_cranelift::Cranelift; + +fn main() -> anyhow::Result<()> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + br#" +(module + (type $swap_t (func (param i32 i64) (result i64 i32))) + (func $swap (type $swap_t) (param $x i32) (param $y i64) (result i64 i32) + (local.get $y) + (local.get $x)) + (export "swap" (func $swap))) +"#, + )?; + + // Set up the compiler. + let compiler = Cranelift::default(); + + // Let's declare the features. + let mut features = Features::new(); + // Enable the multi-value feature. + features.multi_value(true); + + // Set up the engine. That's where we define the features! + let engine = EngineBuilder::new(compiler).set_features(Some(features)); + + // Now, let's define the store, and compile the module. + let mut store = Store::new(engine); + let module = Module::new(&store, wasm_bytes)?; + + // Finally, let's instantiate the module, and execute something + // :-). + let import_object = imports! {}; + let instance = Instance::new(&mut store, &module, &import_object)?; + let swap = instance.exports.get_function("swap")?; + + let results = swap.call(&mut store, &[Value::I32(1), Value::I64(2)])?; + + assert_eq!(results.to_vec(), vec![Value::I64(2), Value::I32(1)]); + + Ok(()) +} + +#[test] +fn test_features() -> anyhow::Result<()> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/hello_world.rs b/arbitrator/tools/module_roots/wasmer/examples/hello_world.rs new file mode 100644 index 0000000..83d16ff --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/hello_world.rs @@ -0,0 +1,80 @@ +//! This is a simple example introducing the core concepts of the Wasmer API. +//! +//! You can run the example directly by executing the following in the Wasmer root: +//! +//! ```shell +//! cargo run --example hello-world --release --features "cranelift" +//! ``` + +use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, TypedFunction}; + +fn main() -> anyhow::Result<()> { + // First we create a simple Wasm program to use with Wasmer. + // We use the WebAssembly text format and use `wasmer::wat2wasm` to compile + // it into a WebAssembly binary. + // + // Most WebAssembly programs come from compiling source code in a high level + // language and will already be in the binary format. + let wasm_bytes = wat2wasm( + br#" +(module + ;; First we define a type with no parameters and no results. + (type $no_args_no_rets_t (func (param) (result))) + + ;; Then we declare that we want to import a function named "env" "say_hello" with + ;; that type signature. + (import "env" "say_hello" (func $say_hello (type $no_args_no_rets_t))) + + ;; Finally we create an entrypoint that calls our imported function. + (func $run (type $no_args_no_rets_t) + (call $say_hello)) + ;; And mark it as an exported function named "run". + (export "run" (func $run))) +"#, + )?; + + // Create a Store. + let mut store = Store::default(); + + // We then use our store and Wasm bytes to compile a `Module`. + // A `Module` is a compiled WebAssembly module that isn't ready to execute yet. + let module = Module::new(&store, wasm_bytes)?; + + // We define a function to act as our "env" "say_hello" function imported in the + // Wasm program above. + fn say_hello_world() { + println!("Hello, world!") + } + + // We then create an import object so that the `Module`'s imports can be satisfied. + let import_object = imports! { + // We use the default namespace "env". + "env" => { + // And call our function "say_hello". + "say_hello" => Function::new_typed(&mut store, say_hello_world), + } + }; + + // We then use the `Module` and the import object to create an `Instance`. + // + // An `Instance` is a compiled WebAssembly module that has been set up + // and is ready to execute. + let instance = Instance::new(&mut store, &module, &import_object)?; + + // We get the `TypedFunction` with no parameters and no results from the instance. + // + // Recall that the Wasm module exported a function named "run", this is getting + // that exported function from the `Instance`. + let run_func: TypedFunction<(), ()> = instance.exports.get_typed_function(&mut store, "run")?; + + // Finally, we call our exported Wasm function which will call our "say_hello" + // function and return. + run_func.call(&mut store)?; + + Ok(()) +} + +#[test] +fn test_hello_world() -> anyhow::Result<()> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/http_dynamic_size.rs b/arbitrator/tools/module_roots/wasmer/examples/http_dynamic_size.rs new file mode 100644 index 0000000..91317f9 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/http_dynamic_size.rs @@ -0,0 +1,130 @@ +use anyhow::Result; +use wasmer::{ + imports, wat2wasm, AsStoreRef, Function, FunctionEnv, FunctionEnvMut, Instance, Memory, + MemoryView, Module, Store, WasmPtr, +}; + +// Utils +pub fn read_string(view: &MemoryView, start: u32, len: u32) -> Result { + Ok(WasmPtr::::new(start).read_utf8_string(view, len)?) +} + +// Environment +pub struct ExampleEnv { + memory: Option, +} + +impl ExampleEnv { + fn set_memory(&mut self, memory: Memory) { + self.memory = Some(memory); + } + + fn get_memory(&self) -> &Memory { + self.memory.as_ref().unwrap() + } + + fn view<'a>(&'a self, store: &'a impl AsStoreRef) -> MemoryView<'a> { + self.get_memory().view(store) + } +} + +fn http_get(mut ctx: FunctionEnvMut, url: u32, url_len: u32) -> u32 { + // Setup environment + let (response, memory_size) = { + // Read url from memory + let view = ctx.data().view(&ctx); + let memory_size = view.data_size() as usize; + let address = read_string(&view, url, url_len).unwrap(); + + // Get request + let response = ureq::get(&address).call().unwrap(); + let capacity = match response + .header("Content-Length") + .map(|it| it.parse::()) + { + Some(Ok(len)) => len, + _ => 1024, + }; + let mut buffer = Vec::with_capacity(capacity); + let mut reader = response.into_reader(); + reader.read_to_end(&mut buffer).unwrap(); + (buffer, memory_size) + }; + + // If the response is too big, grow memory + if 1114112 + response.len() > memory_size { + let delta = (1114112 + response.len() - memory_size) / wasmer::WASM_PAGE_SIZE + 1; + let memory = ctx.data().get_memory().clone(); + memory.grow(&mut ctx, delta as u32).unwrap(); + } + + // Write response as string [ptr, cap, len] to wasm memory and return pointer + let view = ctx.data().view(&ctx); + view.write(1114112, &u32::to_le_bytes(1114124)).unwrap(); + view.write(1114116, &u32::to_le_bytes(response.len() as u32)) + .unwrap(); + view.write(1114120, &u32::to_le_bytes(response.len() as u32)) + .unwrap(); + view.write(1114124, &response).unwrap(); + 1114112 +} + +fn main() -> Result<()> { + let wasm_bytes = wat2wasm( + br#" +(module + (type (;0;) (func (param i32 i32) (result i32))) + (type (;1;) (func (result i32))) + (type (;2;) (func)) + (import "env" "http_get" (func (;0;) (type 0))) + (func (;1;) (type 1) (result i32) + i32.const 1048576 + i32.const 45 + call 0 + i32.const 8 + i32.add + i32.load) + (func (;2;) (type 2)) + (func (;3;) (type 2) + call 2 + call 2) + (func (;4;) (type 1) (result i32) + call 1 + call 3) + (table (;0;) 1 1 funcref) + (memory (;0;) 17) + (global (;0;) (mut i32) (i32.const 1048576)) + (export "memory" (memory 0)) + (export "fetch" (func 4)) + (data (;0;) (i32.const 1048576) "https://postman-echo.com/bytes/5/mb?type=json")) +"#, + )?; + + // Load module + let mut store = Store::default(); + let module = Module::new(&store, wasm_bytes)?; + + // Add host functions + let function_env = FunctionEnv::new(&mut store, ExampleEnv { memory: None }); + let import_object = imports! { + // We use the default namespace "env". + "env" => { + // And call our function "http_get". + "http_get" => Function::new_typed_with_env(&mut store, &function_env, http_get), + } + }; + + // Create instance + let instance = Instance::new(&mut store, &module, &import_object)?; + let memory = instance.exports.get_memory("memory")?; + + // Give reference to memory + function_env.as_mut(&mut store).set_memory(memory.clone()); + + // Call function + let fetch = instance.exports.get_function("fetch")?; + let result = fetch.call(&mut store, &[])?; + println!("Response size: {result:?}"); + + Ok(()) +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/imports_exports.rs b/arbitrator/tools/module_roots/wasmer/examples/imports_exports.rs new file mode 100644 index 0000000..bb430b1 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/imports_exports.rs @@ -0,0 +1,120 @@ +//! A Wasm module can import and export entities, like functions, memories, globals and tables. +//! This example illustrates the basics of using these entities. +//! +//! In this example we'll be using a sample Wasm module which exports some entities and requires us +//! to also import some of them. +//! +//! The goal here is to give you an idea of how to work with imports and exports. We won't go into +//! the details of each entities, they'll be covered in more details in the other examples. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example imports-exports --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{ + imports, wat2wasm, Function, FunctionType, Global, Instance, Memory, Module, Store, Table, + Type, Value, +}; + +fn main() -> Result<(), Box> { + // Let's declare the Wasm module. + // + // We are using the text representation of the module here but you can also load `.wasm` + // files using the `include_bytes!` macro. + let wasm_bytes = wat2wasm( + br#" +(module + (func $host_function (import "" "host_function") (result i32)) + (global $host_global (import "env" "host_global") i32) + + (func $function (export "guest_function") (result i32) (global.get $global)) + (global $global (export "guest_global") i32 (i32.const 42)) + (table $table (export "guest_table") 1 1 funcref) + (memory $memory (export "guest_memory") 1)) +"#, + )?; + + // Create a Store. + let mut store = Store::default(); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // Here we go. + // + // Before we can instantiate our module, we need to define + // the entities we will import. + // + // We won't go into details here as creating entities will be + // covered in more detail in other examples. + println!("Creating the imported function..."); + let host_function_signature = FunctionType::new(vec![], vec![Type::I32]); + let host_function = Function::new(&mut store, &host_function_signature, |_args| { + Ok(vec![Value::I32(42)]) + }); + + println!("Creating the imported global..."); + let host_global = Global::new(&mut store, Value::I32(42)); + + // Create an import object. + // + // Imports are stored in namespaces. We'll need to register each of the + // namespaces with a name and add the imported entities there. + // + // Note that the namespace can also have an empty name. + // + // Our module requires us to import: + // * A function `host_function` in a namespace with an empty name; + // * A global `host_global` in the `env` namespace. + // + // Let's do this! + let import_object = imports! { + "" => { + "host_function" => host_function, + }, + "env" => { + "host_global" => host_global, + }, + }; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + // Here we go. + // + // The Wasm module exports some entities: + // * A function: `guest_function` + // * A global: `guest_global` + // * A memory: `guest_memory` + // * A table: `guest_table` + // + // Let's get them. + println!("Getting the exported function..."); + let function = instance.exports.get::("guest_function")?; + println!("Got exported function of type: {:?}", function.ty(&store)); + + println!("Getting the exported global..."); + let global = instance.exports.get::("guest_global")?; + println!("Got exported global of type: {:?}", global.ty(&store)); + + println!("Getting the exported memory..."); + let memory = instance.exports.get::("guest_memory")?; + println!("Got exported memory of type: {:?}", memory.ty(&store)); + + println!("Getting the exported table..."); + let table = instance.exports.get::("guest_table")?; + println!("Got exported table of type: {:?}", table.ty(&store)); + + Ok(()) +} + +#[test] +fn test_imports_exports() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/imports_function.rs b/arbitrator/tools/module_roots/wasmer/examples/imports_function.rs new file mode 100644 index 0000000..b8eff99 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/imports_function.rs @@ -0,0 +1,106 @@ +//! A Wasm module can import entities, like functions, memories, +//! globals and tables. +//! +//! This example illustrates how to use imported functions. They come +//! in 2 flavors: +//! +//! 1. Dynamic functions, where parameters and results are of a +//! slice of `Value`, +//! 2. Native function, where parameters and results are statically +//! typed Rust values. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example imported-function --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{ + imports, wat2wasm, Function, FunctionEnv, FunctionEnvMut, FunctionType, Instance, Module, + Store, Type, TypedFunction, Value, +}; + +fn main() -> Result<(), Box> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + br#" +(module + (func $multiply_dynamic (import "env" "multiply_dynamic") (param i32) (result i32)) + (func $multiply_typed (import "env" "multiply_typed") (param i32) (result i32)) + + (type $sum_t (func (param i32) (param i32) (result i32))) + (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) + (call $multiply_dynamic (local.get $x)) + (call $multiply_typed (local.get $y)) + i32.add) + (export "sum" (func $sum_f))) +"#, + )?; + + // Create a Store. + let mut store = Store::default(); + + struct MyEnv; + let env = FunctionEnv::new(&mut store, MyEnv {}); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // Create the functions + let multiply_dynamic_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]); + let multiply_dynamic = Function::new(&mut store, &multiply_dynamic_signature, |args| { + println!("Calling `multiply_dynamic`..."); + + let result = args[0].unwrap_i32() * 2; + + println!("Result of `multiply_dynamic`: {:?}", result); + + Ok(vec![Value::I32(result)]) + }); + + fn multiply(_env: FunctionEnvMut, a: i32) -> i32 { + println!("Calling `multiply_typed`..."); + let result = a * 3; + + println!("Result of `multiply_typed`: {:?}", result); + + result + } + let multiply_typed = Function::new_typed_with_env(&mut store, &env, multiply); + + // Create an import object. + let import_object = imports! { + "env" => { + "multiply_dynamic" => multiply_dynamic, + "multiply_typed" => multiply_typed, + } + }; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + // Here we go. + // + // The Wasm module exports a function called `sum`. Let's get it. + let sum: TypedFunction<(i32, i32), i32> = + instance.exports.get_function("sum")?.typed(&mut store)?; + + println!("Calling `sum` function..."); + // Let's call the `sum` exported function. It will call each + // of the imported functions. + let result = sum.call(&mut store, 1, 2)?; + + println!("Results of `sum`: {:?}", result); + assert_eq!(result, 8); + + Ok(()) +} + +#[test] +fn test_exported_function() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/imports_function_env.rs b/arbitrator/tools/module_roots/wasmer/examples/imports_function_env.rs new file mode 100644 index 0000000..fa0f338 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/imports_function_env.rs @@ -0,0 +1,135 @@ +//! A Wasm module can import entities, like functions, memories, +//! globals and tables. +//! +//! In this example, we'll create a system for getting and adjusting a counter value. However, host +//! functions are not limited to storing data outside of Wasm, they're normal host functions and +//! can do anything that the host can do. +//! +//! 1. There will be a `get_counter` function that will return an i32 of +//! the current global counter, +//! 2. There will be an `add_to_counter` function will add the passed +//! i32 value to the counter, and return an i32 of the current +//! global counter. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example imported-function-env --release --features "cranelift" +//! ``` +//! +//! Ready? + +use std::sync::{Arc, Mutex}; +use wasmer::{ + imports, wat2wasm, Function, FunctionEnv, FunctionEnvMut, Instance, Module, Store, + TypedFunction, +}; + +fn main() -> Result<(), Box> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + br#" +(module + (func $get_counter (import "env" "get_counter") (result i32)) + (func $add_to_counter (import "env" "add_to_counter") (param i32) (result i32)) + + (type $increment_t (func (param i32) (result i32))) + (func $increment_f (type $increment_t) (param $x i32) (result i32) + (block + (loop + (call $add_to_counter (i32.const 1)) + (set_local $x (i32.sub (get_local $x) (i32.const 1))) + (br_if 1 (i32.eq (get_local $x) (i32.const 0))) + (br 0))) + call $get_counter) + (export "increment_counter_loop" (func $increment_f))) +"#, + )?; + + // Create a Store. + let mut store = Store::default(); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // We create some shared data here, `Arc` is required because we may + // move our WebAssembly instance to another thread to run it. Mutex + // lets us get shared mutabilty which is fine because we know we won't + // run host calls concurrently. If concurrency is a possibilty, we'd have + // to use a `Mutex`. + let shared_counter: Arc> = Arc::new(Mutex::new(0)); + + // Once we have our counter we'll wrap it inside en `Env` which we'll pass + // to our imported functionsvia the FunctionEnv. + // + // This struct may have been anything. The only constraint is it must be + // possible to know the size of the `Env` at compile time (i.e it has to + // implement the `Sized` trait). + // The Env is then accessed using `data()` or `data_mut()` method. + #[derive(Clone)] + struct Env { + counter: Arc>, + } + + // Create the functions + fn get_counter(env: FunctionEnvMut) -> i32 { + *env.data().counter.lock().unwrap() + } + fn add_to_counter(env: FunctionEnvMut, add: i32) -> i32 { + let mut counter_ref = env.data().counter.lock().unwrap(); + + *counter_ref += add; + *counter_ref + } + + let env = FunctionEnv::new( + &mut store, + Env { + counter: shared_counter.clone(), + }, + ); + + // Create an import object. + let import_object = imports! { + "env" => { + "get_counter" => Function::new_typed_with_env(&mut store, &env, get_counter), + "add_to_counter" => Function::new_typed_with_env(&mut store, &env, add_to_counter), + } + }; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + // Here we go. + // + // The Wasm module exports a function called `increment_counter_loop`. Let's get it. + let increment_counter_loop: TypedFunction = instance + .exports + .get_function("increment_counter_loop")? + .typed(&mut store)?; + + let counter_value: i32 = *shared_counter.lock().unwrap(); + println!("Initial ounter value: {:?}", counter_value); + + println!("Calling `increment_counter_loop` function..."); + // Let's call the `increment_counter_loop` exported function. + // + // It will loop five times thus incrementing our counter five times. + let result = increment_counter_loop.call(&mut store, 5)?; + + let counter_value: i32 = *shared_counter.lock().unwrap(); + println!("New counter value (host): {:?}", counter_value); + assert_eq!(counter_value, 5); + + println!("New counter value (guest): {:?}", result); + assert_eq!(result, 5); + + Ok(()) +} + +#[test] +fn test_imported_function_env() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/imports_function_env_global.rs b/arbitrator/tools/module_roots/wasmer/examples/imports_function_env_global.rs new file mode 100644 index 0000000..46126f9 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/imports_function_env_global.rs @@ -0,0 +1,153 @@ +//! A Wasm module can import entities, like functions, memories, +//! globals and tables. +//! +//! In this example, we'll create a system for getting and adjusting a counter value. However, host +//! functions are not limited to storing data outside of Wasm, they're normal host functions and +//! can do anything that the host can do. +//! we will also demonstrate how a Function can also get globals from within a wasm call +//! +//! 1. There will be a `get_counter` function that will return an i32 of +//! the current global counter, The function will also increment a global value +//! 2. There will be an `add_to_counter` function will add the passed +//! i32 value to the counter, and return an i32 of the current +//! global counter. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example imported-function-env-global --release --features "cranelift" +//! ``` +//! +//! Ready? + +use std::sync::{Arc, Mutex}; +use wasmer::{ + imports, wat2wasm, Function, FunctionEnv, FunctionEnvMut, Global, Instance, Module, Store, + TypedFunction, Value, +}; + +fn main() -> Result<(), Box> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + br#" +(module + (global $g_counter (import "env" "g_counter") (mut i32)) + (func $get_counter (import "env" "get_counter") (result i32)) + (func $add_to_counter (import "env" "add_to_counter") (param i32) (result i32)) + + (type $increment_t (func (param i32) (result i32))) + (func $increment_f (type $increment_t) (param $x i32) (result i32) + (block + (loop + (call $add_to_counter (i32.const 1)) + (set_local $x (i32.sub (get_local $x) (i32.const 1))) + (br_if 1 (i32.eq (get_local $x) (i32.const 0))) + (br 0))) + call $get_counter) + (export "increment_counter_loop" (func $increment_f))) +"#, + )?; + + // Create a Store. + let mut store = Store::default(); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // Create the global + let g_counter = Global::new_mut(&mut store, Value::I32(5)); + + // We create some shared data here, `Arc` is required because we may + // move our WebAssembly instance to another thread to run it. Mutex + // lets us get shared mutabilty which is fine because we know we won't + // run host calls concurrently. If concurrency is a possibilty, we'd have + // to use a `Mutex`. + let shared_counter: Arc> = Arc::new(Mutex::new(0)); + + // Once we have our counter we'll wrap it inside en `Env` which we'll pass + // to our imported functionsvia the FunctionEnv. + // + // This struct may have been anything. The only constraint is it must be + // possible to know the size of the `Env` at compile time (i.e it has to + // implement the `Sized` trait). + // The Env is then accessed using `data()` or `data_mut()` method. + #[derive(Clone)] + struct Env { + counter: Arc>, + g_counter: Global, + } + + // Create the functions + fn get_counter(env: FunctionEnvMut) -> i32 { + *env.data().counter.lock().unwrap() + } + fn add_to_counter(mut env: FunctionEnvMut, add: i32) -> i32 { + let (data, mut storemut) = env.data_and_store_mut(); + let mut counter_ref = data.counter.lock().unwrap(); + + let global_count = data.g_counter.get(&mut storemut).unwrap_i32(); + data.g_counter + .set(&mut storemut, Value::I32(global_count + add)) + .unwrap(); + + *counter_ref += add; + *counter_ref + } + + let env = FunctionEnv::new( + &mut store, + Env { + counter: shared_counter.clone(), + g_counter: g_counter.clone(), + }, + ); + + // Create an import object. + let import_object = imports! { + "env" => { + "get_counter" => Function::new_typed_with_env(&mut store, &env, get_counter), + "add_to_counter" => Function::new_typed_with_env(&mut store, &env, add_to_counter), + "g_counter" => g_counter.clone(), + } + }; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + // Here we go. + // + // The Wasm module exports a function called `increment_counter_loop`. Let's get it. + let increment_counter_loop: TypedFunction = instance + .exports + .get_function("increment_counter_loop")? + .typed(&mut store)?; + + let counter_value: i32 = *shared_counter.lock().unwrap(); + println!("Initial ounter value: {:?}", counter_value); + + println!("Calling `increment_counter_loop` function..."); + // Let's call the `increment_counter_loop` exported function. + // + // It will loop five times thus incrementing our counter five times. + let result = increment_counter_loop.call(&mut store, 5)?; + + let counter_value: i32 = *shared_counter.lock().unwrap(); + println!("New counter value (host): {:?}", counter_value); + assert_eq!(counter_value, 5); + + println!("New counter value (guest): {:?}", result); + assert_eq!(result, 5); + + let global_counter = g_counter.get(&mut store); + println!("New global counter value: {:?}", global_counter); + assert_eq!(global_counter.unwrap_i32(), 10); + + Ok(()) +} + +#[test] +fn test_imported_function_env() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/imports_global.rs b/arbitrator/tools/module_roots/wasmer/examples/imports_global.rs new file mode 100644 index 0000000..7080f6b --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/imports_global.rs @@ -0,0 +1,112 @@ +//! A Wasm module can import entities, like functions, memories, +//! globals and tables. +//! +//! This example illustrates how to use imported globals. They come +//! in 2 flavors: +//! +//! 1. Immutable globals (const), +//! 2. Mutable globals. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example imported-global --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, Global, Instance, Module, Store, TypedFunction, Value}; + +fn main() -> Result<(), Box> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + br#" +(module + (global $some (import "env" "some") f32) + (global $other (import "env" "other") (mut f32)) + + (func (export "get_some") (result f32) (global.get $some)) + (func (export "get_other") (result f32) (global.get $other)) + + (func (export "set_other") (param f32) (global.set $other (local.get 0)))) +"#, + )?; + + // Create a Store. + let mut store = Store::default(); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // Create the globals + let some = Global::new(&mut store, Value::F32(1.0)); + let other = Global::new_mut(&mut store, Value::F32(2.0)); + + // Create an import object. + // We add the two required globals in the `env` namespace. + let import_object = imports! { + "env" => { + "some" => some.clone(), + "other" => other.clone(), + } + }; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + // Here we go. + // + // The Wasm module only imports some globals. We'll have to interact + // with them either using the Global API or exported functions. + let get_some: TypedFunction<(), f32> = instance + .exports + .get_function("get_some")? + .typed(&mut store)?; + let get_other: TypedFunction<(), f32> = instance + .exports + .get_function("get_other")? + .typed(&mut store)?; + + let some_result = get_some.call(&mut store)?; + let other_result = get_other.call(&mut store)?; + + println!("some value (via `get_some`): {:?}", some_result); + println!("some value (via Global API): {:?}", some.get(&mut store)); + println!("other value (via `get_other`): {:?}", other_result); + println!("other value (via Global API): {:?}", other.get(&mut store)); + + assert_eq!(some_result, some.get(&mut store).f32().unwrap()); + assert_eq!(other_result, other.get(&mut store).f32().unwrap()); + + println!("Setting global values..."); + // Trying to set the value of a immutable global (`const`) + // will result in a `RuntimeError`. + let result = some.set(&mut store, Value::F32(42.0)); + assert!(result.is_err()); + + other.set(&mut store, Value::F32(21.0))?; + let other_result = other.get(&mut store); + println!("other value after `set`: {:?}", other_result); + assert_eq!(other_result, Value::F32(21.0)); + + println!("Altering global values through exported functions..."); + // Changes made to global through exported functions will + // be reflected on the host side. + let set_other: TypedFunction = instance + .exports + .get_function("set_other")? + .typed(&mut store)?; + set_other.call(&mut store, 42.0)?; + + println!("other value (via Global API): {:?}", other.get(&mut store)); + assert_eq!(other.get(&mut store), Value::F32(42.0)); + + Ok(()) +} + +#[test] +fn test_imported_global() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/instance.rs b/arbitrator/tools/module_roots/wasmer/examples/instance.rs new file mode 100644 index 0000000..35cc707 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/instance.rs @@ -0,0 +1,78 @@ +//! Wasmer will let you easily run Wasm module in a Rust host. +//! +//! This example illustrates the basics of using Wasmer through a "Hello World"-like project: +//! +//! 1. How to load a Wasm modules as bytes +//! 2. How to compile the module +//! 3. How to create an instance of the module +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example instance --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, Instance, Module, Store, TypedFunction}; + +fn main() -> Result<(), Box> { + // Let's declare the Wasm module. + // + // We are using the text representation of the module here but you can also load `.wasm` + // files using the `include_bytes!` macro. + let wasm_bytes = wat2wasm( + br#" +(module + (type $add_one_t (func (param i32) (result i32))) + (func $add_one_f (type $add_one_t) (param $value i32) (result i32) + local.get $value + i32.const 1 + i32.add) + (export "add_one" (func $add_one_f))) +"#, + )?; + + // Create a Store. + // Note that we don't need to specify the engine/compiler if we want to use + // the default provided by Wasmer. + // You can use `Store::default()` for that. + let mut store = Store::default(); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // Create an empty import object. + let import_object = imports! {}; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + // We now have an instance ready to be used. + // + // From an `Instance` we can retrieve any exported entities. + // Each of these entities is covered in others examples. + // + // Here we are retrieving the exported function. We won't go into details here + // as the main focus of this example is to show how to create an instance out + // of a Wasm module and have basic interactions with it. + let add_one: TypedFunction = instance + .exports + .get_function("add_one")? + .typed(&mut store)?; + + println!("Calling `add_one` function..."); + let result = add_one.call(&mut store, 1)?; + + println!("Results of `add_one`: {:?}", result); + assert_eq!(result, 2); + + Ok(()) +} + +#[test] +fn test_exported_function() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/memory.rs b/arbitrator/tools/module_roots/wasmer/examples/memory.rs new file mode 100644 index 0000000..15ed18c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/memory.rs @@ -0,0 +1,149 @@ +//! With Wasmer you'll be able to interact with guest module memory. +//! +//! This example illustrates the basics of interacting with Wasm module memory.: +//! +//! 1. How to load a Wasm modules as bytes +//! 2. How to compile the module +//! 3. How to create an instance of the module +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example memory --release --features "cranelift" +//! ``` +//! +//! Ready? + +use std::mem; +use wasmer::{imports, wat2wasm, Bytes, Instance, Module, Pages, Store, TypedFunction}; + +// this example is a work in progress: +// TODO: clean it up and comment it https://github.com/wasmerio/wasmer/issues/1749 + +fn main() -> anyhow::Result<()> { + // Let's declare the Wasm module. + // + // We are using the text representation of the module here but you can also load `.wasm` + // files using the `include_bytes!` macro. + let wasm_bytes = wat2wasm( + r#" +(module + (type $mem_size_t (func (result i32))) + (type $get_at_t (func (param i32) (result i32))) + (type $set_at_t (func (param i32) (param i32))) + + (memory $mem 1) + + (func $get_at (type $get_at_t) (param $idx i32) (result i32) + (i32.load (local.get $idx))) + + (func $set_at (type $set_at_t) (param $idx i32) (param $val i32) + (i32.store (local.get $idx) (local.get $val))) + + (func $mem_size (type $mem_size_t) (result i32) + (memory.size)) + + (export "get_at" (func $get_at)) + (export "set_at" (func $set_at)) + (export "mem_size" (func $mem_size)) + (export "memory" (memory $mem))) +"# + .as_bytes(), + )?; + + // Create a Store. + let mut store = Store::default(); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // Create an empty import object. + let import_object = imports! {}; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + // The module exports some utility functions, let's get them. + // + // These function will be used later in this example. + let mem_size: TypedFunction<(), i32> = instance + .exports + .get_typed_function(&mut store, "mem_size")?; + let get_at: TypedFunction = + instance.exports.get_typed_function(&mut store, "get_at")?; + let set_at: TypedFunction<(i32, i32), ()> = + instance.exports.get_typed_function(&mut store, "set_at")?; + let memory = instance.exports.get_memory("memory")?; + + // We now have an instance ready to be used. + // + // We will start by querying the most intersting information + // about the memory: its size. There are mainly two ways of getting + // this: + // * the size as a number of `Page`s + // * the size as a number of bytes + // + // The size in bytes can be found either by querying its pages or by + // querying the memory directly. + println!("Querying memory size..."); + let memory_view = memory.view(&store); + assert_eq!(memory_view.size(), Pages::from(1)); + assert_eq!(memory_view.size().bytes(), Bytes::from(65536 as usize)); + assert_eq!(memory_view.data_size(), 65536); + + // Sometimes, the guest module may also export a function to let you + // query the memory. Here we have a `mem_size` function, let's try it: + let result = mem_size.call(&mut store)?; + + let memory_view = memory.view(&store); + println!("Memory size: {:?}", result); + assert_eq!(Pages::from(result as u32), memory_view.size()); + + // Now that we know the size of our memory, it's time to see how wa + // can change this. + // + // A memory can be grown to allow storing more things into it. Let's + // see how we can do that: + println!("Growing memory..."); + + // Here we are requesting two more pages for our memory. + memory.grow(&mut store, 2)?; + + let memory_view = memory.view(&store); + assert_eq!(memory_view.size(), Pages::from(3)); + assert_eq!(memory_view.data_size(), 65536 * 3); + + // Now that we know how to query and adjust the size of the memory, + // let's see how wa can write to it or read from it. + // + // We'll only focus on how to do this using exported functions, the goal + // is to show how to work with memory addresses. Here we'll use absolute + // addresses to write and read a value. + let mem_addr = 0x2220; + let val = 0xFEFEFFE; + set_at.call(&mut store, mem_addr, val)?; + + let result = get_at.call(&mut store, mem_addr)?; + println!("Value at {:#x?}: {:?}", mem_addr, result); + assert_eq!(result, val); + + // Now instead of using hard coded memory addresses, let's try to write + // something at the end of the second memory page and read it. + let page_size = 0x1_0000; + let mem_addr = (page_size * 2) - mem::size_of_val(&val) as i32; + let val = 0xFEA09; + set_at.call(&mut store, mem_addr, val)?; + + let result = get_at.call(&mut store, mem_addr)?; + println!("Value at {:#x?}: {:?}", mem_addr, result); + assert_eq!(result, val); + + Ok(()) +} + +#[test] +fn test_memory() -> anyhow::Result<()> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/metering.rs b/arbitrator/tools/module_roots/wasmer/examples/metering.rs new file mode 100644 index 0000000..e5d8c70 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/metering.rs @@ -0,0 +1,172 @@ +//! Wasmer will let you easily run Wasm module in a Rust host. +//! +//! This example illustrates the basics of using Wasmer metering features: +//! +//! 1. How to enable metering in a module +//! 2. How to meter a specific function call +//! 3. How to make execution fails if cost exceeds a given limit +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example metering --release --features "cranelift" +//! ``` +//! +//! Ready? + +use anyhow::bail; +use std::sync::Arc; +use wasmer::wasmparser::Operator; +use wasmer::CompilerConfig; +use wasmer::{imports, wat2wasm, EngineBuilder, Instance, Module, Store, TypedFunction}; +use wasmer_compiler_cranelift::Cranelift; +use wasmer_middlewares::{ + metering::{get_remaining_points, set_remaining_points, MeteringPoints}, + Metering, +}; + +fn main() -> anyhow::Result<()> { + // Let's declare the Wasm module. + // + // We are using the text representation of the module here but you can also load `.wasm` + // files using the `include_bytes!` macro. + let wasm_bytes = wat2wasm( + br#" +(module + (type $add_t (func (param i32) (result i32))) + (func $add_one_f (type $add_t) (param $value i32) (result i32) + local.get $value + i32.const 1 + i32.add) + (export "add_one" (func $add_one_f))) +"#, + )?; + + // Let's define our cost function. + // + // This function will be called for each `Operator` encountered during + // the Wasm module execution. It should return the cost of the operator + // that it received as it first argument. + let cost_function = |operator: &Operator| -> u64 { + match operator { + Operator::LocalGet { .. } | Operator::I32Const { .. } => 1, + Operator::I32Add { .. } => 2, + _ => 0, + } + }; + + // Now let's create our metering middleware. + // + // `Metering` needs to be configured with a limit and a cost function. + // + // For each `Operator`, the metering middleware will call the cost + // function and subtract the cost from the remaining points. + let metering = Arc::new(Metering::new(10, cost_function)); + let mut compiler_config = Cranelift::default(); + compiler_config.push_middleware(metering); + + // Create a Store. + // + // We use our previously create compiler configuration + // with the Universal engine. + let mut store = Store::new(EngineBuilder::new(compiler_config)); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // Create an empty import object. + let import_object = imports! {}; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&mut store, &module, &import_object)?; + + // We now have an instance ready to be used. + // + // Our module exports a single `add_one` function. We want to + // measure the cost of executing this function. + let add_one: TypedFunction = instance + .exports + .get_function("add_one")? + .typed(&mut store)?; + + println!("Calling `add_one` function once..."); + add_one.call(&mut store, 1)?; + + // As you can see here, after the first call we have 6 remaining points. + // + // This is correct, here are the details of how it has been computed: + // * `local.get $value` is a `Operator::LocalGet` which costs 1 point; + // * `i32.const` is a `Operator::I32Const` which costs 1 point; + // * `i32.add` is a `Operator::I32Add` which costs 2 points. + let remaining_points_after_first_call = get_remaining_points(&mut store, &instance); + assert_eq!( + remaining_points_after_first_call, + MeteringPoints::Remaining(6) + ); + + println!( + "Remaining points after the first call: {:?}", + remaining_points_after_first_call + ); + + println!("Calling `add_one` function twice..."); + add_one.call(&mut store, 1)?; + + // We spent 4 more points with the second call. + // We have 2 remaining points. + let remaining_points_after_second_call = get_remaining_points(&mut store, &instance); + assert_eq!( + remaining_points_after_second_call, + MeteringPoints::Remaining(2) + ); + + println!( + "Remaining points after the second call: {:?}", + remaining_points_after_second_call + ); + + // Because calling our `add_one` function consumes 4 points, + // calling it a third time will fail: we already consume 8 + // points, there are only two remaining. + println!("Calling `add_one` function a third time..."); + match add_one.call(&mut store, 1) { + Ok(result) => { + bail!( + "Expected failure while calling `add_one`, found: {}", + result + ); + } + Err(_) => { + println!("Calling `add_one` failed."); + + // Because the last needed more than the remaining points, we should have an error. + let remaining_points = get_remaining_points(&mut store, &instance); + + match remaining_points { + MeteringPoints::Remaining(..) => { + bail!("No metering error: there are remaining points") + } + MeteringPoints::Exhausted => println!("Not enough points remaining"), + } + } + } + + // Now let's see how we can set a new limit... + println!("Set new remaining points to 10"); + let new_limit = 10; + set_remaining_points(&mut store, &instance, new_limit); + + let remaining_points = get_remaining_points(&mut store, &instance); + assert_eq!(remaining_points, MeteringPoints::Remaining(new_limit)); + + println!("Remaining points: {:?}", remaining_points); + + Ok(()) +} + +#[test] +fn test_metering() -> anyhow::Result<()> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/platform_ios_headless.rs b/arbitrator/tools/module_roots/wasmer/examples/platform_ios_headless.rs new file mode 100644 index 0000000..7b133b2 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/platform_ios_headless.rs @@ -0,0 +1,75 @@ +//! Defining an engine in Wasmer is one of the fundamental steps. +//! +//! This example builds on that of 'engine_headless.rs' but instead of +//! serializing a module and then deserializing it again for your host machines target, +//! We instead create an engine for our target architecture (In this case an ARM64 iOS device), +//! serialize a simple module to a .dylib file that can be copied to an iOS project and +//! deserialized/ran using the 'Headless C-API'. +//! +//! ```shell +//! cargo run --example platform-headless-ios --release --features "cranelift" +//! ``` +//! +//! Ready? +#![allow(unused)] +use std::path::Path; +use std::str::FromStr; +use wasmer::{wat2wasm, Module, RuntimeError, Store}; +use wasmer_compiler_cranelift::Cranelift; +use wasmer_types::{CpuFeature, Target, Triple}; +/* +use wasmer_engine_dylib::Dylib; +*/ + +fn main() -> Result<(), Box> { + /* + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + r#" + (module + (type $sum_t (func (param i32 i32) (result i32))) + (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) + local.get $x + local.get $y + i32.add) + (export "sum" (func $sum_f))) + "# + .as_bytes(), + )?; + + // Create a compiler for iOS + let compiler_config = Cranelift::default(); + // Change it to `x86_64-apple-ios` if you want to target the iOS simulator + let triple = Triple::from_str("aarch64-apple-ios") + .map_err(|error| RuntimeError::new(error.to_string()))?; + + // Let's build the target. + let mut cpu_feature = CpuFeature::set(); + cpu_feature.insert(CpuFeature::from_str("sse2")?); + let target = Target::new(triple, cpu_feature); + println!("Chosen target: {:?}", target); + + println!("Creating Dylib engine..."); + let engine = Dylib::new(compiler_config).target(target); + + // Create a store, that holds the engine. + let mut store = Store::new(engine); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + // Here we go. Let's serialize the compiled Wasm module in a + // file. + println!("Serializing module..."); + let dylib_file = Path::new("./sum.dylib"); + module.serialize_to_file(dylib_file)?; + */ + + Ok(()) +} + +#[test] +#[cfg(target_os = "macos")] +fn test_engine_headless_ios() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/table.rs b/arbitrator/tools/module_roots/wasmer/examples/table.rs new file mode 100644 index 0000000..cd74083 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/table.rs @@ -0,0 +1,161 @@ +use wasmer::{ + imports, wat2wasm, Function, Instance, Module, Store, TableType, Type, TypedFunction, Value, +}; + +/// A function we'll call through a table. +fn host_callback(arg1: i32, arg2: i32) -> i32 { + arg1 + arg2 +} + +fn main() -> anyhow::Result<()> { + let wasm_bytes = wat2wasm( + r#" +(module + ;; All our callbacks will take 2 i32s and return an i32. + ;; Wasm tables are not limited to 1 type of function, but the code using the + ;; table must have code to handle the type it finds. + (type $callback_t (func (param i32 i32) (result i32))) + + ;; We'll call a callback by passing a table index as an i32 and then the two + ;; arguments that the function expects. + (type $call_callback_t (func (param i32 i32 i32) (result i32))) + + ;; Our table of functions that's exactly size 3 (min 3, max 3). + (table $t1 3 6 funcref) + + ;; Call the function at the given index with the two supplied arguments. + (func $call_callback (type $call_callback_t) (param $idx i32) + (param $arg1 i32) (param $arg2 i32) + (result i32) + (call_indirect (type $callback_t) + (local.get $arg1) (local.get $arg2) + (local.get $idx))) + + ;; A default function that we'll pad the table with. + ;; This function doubles both its inputs and then sums them. + (func $default_fn (type $callback_t) (param $a i32) (param $b i32) (result i32) + (i32.add + (i32.mul (local.get $a) (i32.const 2)) + (i32.mul (local.get $b) (i32.const 2)))) + + ;; Fill our table with the default function. + (elem $t1 (i32.const 0) $default_fn $default_fn $default_fn) + + ;; Export things for the host to call. + (export "call_callback" (func $call_callback)) + (export "__indirect_function_table" (table $t1))) +"# + .as_bytes(), + )?; + + // Create a Store. + let mut store = Store::default(); + // Then compile our Wasm. + let module = Module::new(&store, wasm_bytes)?; + let import_object = imports! {}; + // And instantiate it with no imports. + let instance = Instance::new(&mut store, &module, &import_object)?; + + // We get our function that calls (i32, i32) -> i32 functions via table. + // The first argument is the table index and the next 2 are the 2 arguments + // to be passed to the function found in the table. + let call_via_table: TypedFunction<(i32, i32, i32), i32> = instance + .exports + .get_typed_function(&mut store, "call_callback")?; + + // And then call it with table index 1 and arguments 2 and 7. + let result = call_via_table.call(&mut store, 1, 2, 7)?; + // Because it's the default function, we expect it to double each number and + // then sum it, giving us 18. + assert_eq!(result, 18); + + // We then get the table from the instance. + let guest_table = instance.exports.get_table("__indirect_function_table")?; + // And demonstrate that it has the properties that we set in the Wasm. + assert_eq!(guest_table.size(&mut store), 3); + assert_eq!( + guest_table.ty(&store), + TableType { + ty: Type::FuncRef, + minimum: 3, + maximum: Some(6) + } + ); + + // == Setting elements in a table == + + // We first construct a `Function` over our host_callback. + let func = Function::new_typed(&mut store, host_callback); + + // And set table index 1 of that table to the host_callback `Function`. + guest_table.set(&mut store, 1, func.into())?; + + // We then repeat the call from before but this time it will find the host function + // that we put at table index 1. + let result = call_via_table.call(&mut store, 1, 2, 7)?; + // And because our host function simply sums the numbers, we expect 9. + assert_eq!(result, 9); + + // == Growing a table == + + // We again construct a `Function` over our host_callback. + let func = Function::new_typed(&mut store, host_callback); + + // And grow the table by 3 elements, filling in our host_callback in all the + // new elements of the table. + let previous_size = guest_table.grow(&mut store, 3, func.into())?; + assert_eq!(previous_size, 3); + + assert_eq!(guest_table.size(&mut store), 6); + assert_eq!( + guest_table.ty(&store), + TableType { + ty: Type::FuncRef, + minimum: 3, + maximum: Some(6) + } + ); + // Now demonstrate that the function we grew the table with is actually in the table. + for table_index in 3..6 { + if let Value::FuncRef(Some(f)) = guest_table.get(&mut store, table_index as _).unwrap() { + let result = f.call(&mut store, &[Value::I32(1), Value::I32(9)])?; + assert_eq!(result[0], Value::I32(10)); + } else { + panic!("expected to find funcref in table!"); + } + } + + // Call function at index 0 to show that it's still the same. + let result = call_via_table.call(&mut store, 0, 2, 7)?; + assert_eq!(result, 18); + + // Now overwrite index 0 with our host_callback. + let func = Function::new_typed(&mut store, host_callback); + guest_table.set(&mut store, 0, func.into())?; + // And verify that it does what we expect. + let result = call_via_table.call(&mut store, 0, 2, 7)?; + assert_eq!(result, 9); + + // Now demonstrate that the host and guest see the same table and that both + // get the same result. + for table_index in 3..6 { + if let Value::FuncRef(Some(f)) = guest_table.get(&mut store, table_index as _).unwrap() { + let result = f.call(&mut store, &[Value::I32(1), Value::I32(9)])?; + assert_eq!(result[0], Value::I32(10)); + } else { + panic!("expected to find funcref in table!"); + } + let result = call_via_table.call(&mut store, table_index, 1, 9)?; + assert_eq!(result, 10); + } + + Ok(()) +} + +// This test is currently failing with: +// not implemented: Native function definitions can't be directly called from the host yet +#[cfg(FALSE)] +#[test] +fn test_table() -> anyhow::Result<()> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/tunables_limit_memory.rs b/arbitrator/tools/module_roots/wasmer/examples/tunables_limit_memory.rs new file mode 100644 index 0000000..da2c7a4 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/tunables_limit_memory.rs @@ -0,0 +1,179 @@ +use std::ptr::NonNull; + +use wasmer::{ + imports, + vm::{self, MemoryError, MemoryStyle, TableStyle, VMMemoryDefinition, VMTableDefinition}, + wat2wasm, BaseTunables, Engine, Instance, Memory, MemoryType, Module, Pages, Store, TableType, + Target, Tunables, +}; +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. +/// +/// After adjusting the memory limits, it delegates all other logic +/// to the base tunables. +pub struct LimitingTunables { + /// The maximum a linear memory is allowed to be (in Wasm pages, 64 KiB each). + /// Since Wasmer ensures there is only none or one memory, this is practically + /// an upper limit for the guest memory. + limit: Pages, + /// The base implementation we delegate all the logic to + base: T, +} + +impl LimitingTunables { + pub fn new(base: T, limit: Pages) -> Self { + Self { limit, base } + } + + /// Takes an input memory type as requested by the guest and sets + /// a maximum if missing. The resulting memory type is final if + /// valid. However, this can produce invalid types, such that + /// validate_memory must be called before creating the memory. + fn adjust_memory(&self, requested: &MemoryType) -> MemoryType { + let mut adjusted = requested.clone(); + if requested.maximum.is_none() { + adjusted.maximum = Some(self.limit); + } + adjusted + } + + /// Ensures the a given memory type does not exceed the memory limit. + /// Call this after adjusting the memory. + fn validate_memory(&self, ty: &MemoryType) -> Result<(), MemoryError> { + if ty.minimum > self.limit { + return Err(MemoryError::Generic( + "Minimum exceeds the allowed memory limit".to_string(), + )); + } + + if let Some(max) = ty.maximum { + if max > self.limit { + return Err(MemoryError::Generic( + "Maximum exceeds the allowed memory limit".to_string(), + )); + } + } else { + return Err(MemoryError::Generic("Maximum unset".to_string())); + } + + Ok(()) + } +} + +impl Tunables for LimitingTunables { + /// Construct a `MemoryStyle` for the provided `MemoryType` + /// + /// Delegated to base. + fn memory_style(&self, memory: &MemoryType) -> MemoryStyle { + let adjusted = self.adjust_memory(memory); + self.base.memory_style(&adjusted) + } + + /// Construct a `TableStyle` for the provided `TableType` + /// + /// Delegated to base. + fn table_style(&self, table: &TableType) -> TableStyle { + self.base.table_style(table) + } + + /// Create a memory owned by the host given a [`MemoryType`] and a [`MemoryStyle`]. + /// + /// The requested memory type is validated, adjusted to the limited and then passed to base. + fn create_host_memory( + &self, + ty: &MemoryType, + style: &MemoryStyle, + ) -> Result { + let adjusted = self.adjust_memory(ty); + self.validate_memory(&adjusted)?; + self.base.create_host_memory(&adjusted, style) + } + + /// Create a memory owned by the VM given a [`MemoryType`] and a [`MemoryStyle`]. + /// + /// Delegated to base. + unsafe fn create_vm_memory( + &self, + ty: &MemoryType, + style: &MemoryStyle, + vm_definition_location: NonNull, + ) -> Result { + let adjusted = self.adjust_memory(ty); + self.validate_memory(&adjusted)?; + self.base + .create_vm_memory(&adjusted, style, vm_definition_location) + } + + /// 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 { + self.base.create_host_table(ty, style) + } + + /// Create a table owned by the VM given a [`TableType`] and a [`TableStyle`]. + /// + /// Delegated to base. + unsafe fn create_vm_table( + &self, + ty: &TableType, + style: &TableStyle, + vm_definition_location: NonNull, + ) -> Result { + self.base.create_vm_table(ty, style, vm_definition_location) + } +} + +fn main() -> Result<(), Box> { + // A Wasm module with one exported memory (min: 7 pages, max: unset) + let wat = br#"(module (memory 7) (export "memory" (memory 0)))"#; + + // Alternatively: A Wasm module with one exported memory (min: 7 pages, max: 80 pages) + // let wat = br#"(module (memory 7 80) (export "memory" (memory 0)))"#; + + let wasm_bytes = wat2wasm(wat)?; + + // Any compiler do the job here + let compiler = Cranelift::default(); + + // Here is where the fun begins + let base = BaseTunables::for_target(&Target::default()); + let tunables = LimitingTunables::new(base, Pages(24)); + let mut engine: Engine = compiler.into(); + engine.set_tunables(tunables); + + // Create a store, that holds the engine and our custom tunables + let mut store = Store::new(engine); + + println!("Compiling module..."); + let module = Module::new(&store, wasm_bytes)?; + + println!("Instantiating module..."); + let import_object = imports! {}; + + // Now at this point, our custom tunables are used + let instance = Instance::new(&mut store, &module, &import_object)?; + + // Check what happened + let mut memories: Vec = instance + .exports + .iter() + .memories() + .map(|pair| pair.1.clone()) + .collect(); + assert_eq!(memories.len(), 1); + + let first_memory = memories.pop().unwrap(); + println!("Memory of this instance: {:?}", first_memory); + assert_eq!(first_memory.ty(&store).maximum.unwrap(), Pages(24)); + + Ok(()) +} + +#[test] +fn test_tunables_limit_memory() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/wasi.rs b/arbitrator/tools/module_roots/wasmer/examples/wasi.rs new file mode 100644 index 0000000..c055d35 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/wasi.rs @@ -0,0 +1,60 @@ +//! Running a WASI compiled WebAssembly module with Wasmer. +//! +//! This example illustrates how to run WASI modules with +//! Wasmer. +//! +//! If you need more manual control over the instantiation, including custom +//! imports, then check out the ./wasi_manual_setup.rs example. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example wasi --release --features "cranelift,wasi" +//! ``` +//! +//! Ready? + +use std::io::Read; + +use wasmer::{Module, Store}; +use wasmer_wasix::{Pipe, WasiEnv}; + +fn main() -> Result<(), Box> { + let wasm_path = concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/wasi-wast/wasi/unstable/hello.wasm" + ); + // Let's declare the Wasm module with the text representation. + let wasm_bytes = std::fs::read(wasm_path)?; + + // Create a Store. + let mut store = Store::default(); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + let (stdout_tx, mut stdout_rx) = Pipe::channel(); + + // Run the module. + WasiEnv::builder("hello") + // .args(&["world"]) + // .env("KEY", "Value") + .stdout(Box::new(stdout_tx)) + .run_with_store(module, &mut store)?; + + eprintln!("Run complete - reading output"); + + let mut buf = String::new(); + stdout_rx.read_to_string(&mut buf).unwrap(); + + eprintln!("Output: {buf}"); + + Ok(()) +} + +#[test] +#[cfg(feature = "wasi")] +fn test_wasi() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/wasi_manual_setup.rs b/arbitrator/tools/module_roots/wasmer/examples/wasi_manual_setup.rs new file mode 100644 index 0000000..d08de6d --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/wasi_manual_setup.rs @@ -0,0 +1,77 @@ +//! Running a WASI compiled WebAssembly module with Wasmer. +//! +//! This example illustrates how to run WASI modules with +//! Wasmer. To run WASI we have to have to do mainly 3 steps: +//! +//! 1. Create a `WasiEnv` instance +//! 2. Attach the imports from the `WasiEnv` to a new instance +//! 3. Run the `WASI` module. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example wasi-manual-setup --release --features "cranelift,wasi" +//! ``` +//! +//! Ready? + +use wasmer::{Instance, Module, Store}; +use wasmer_wasix::WasiEnv; + +fn main() -> Result<(), Box> { + let wasm_path = concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/wasi-wast/wasi/unstable/hello.wasm" + ); + // Let's declare the Wasm module with the text representation. + let wasm_bytes = std::fs::read(wasm_path)?; + + // Create a Store. + let mut store = Store::default(); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + println!("Starting `tokio` runtime..."); + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(); + let _guard = runtime.enter(); + + println!("Creating `WasiEnv`..."); + // First, we create the `WasiEnv` + let mut wasi_env = WasiEnv::builder("hello") + // .args(&["world"]) + // .env("KEY", "Value") + .finalize(&mut store)?; + + println!("Instantiating module with WASI imports..."); + // Then, we get the import object related to our WASI + // and attach it to the Wasm instance. + let import_object = wasi_env.import_object(&mut store, &module)?; + let instance = Instance::new(&mut store, &module, &import_object)?; + + println!("Attach WASI memory..."); + // // Attach the memory export + // let memory = instance.exports.get_memory("memory")?; + // wasi_env.data_mut(&mut store).set_memory(memory.clone()); + + wasi_env.initialize(&mut store, instance.clone())?; + + println!("Call WASI `_start` function..."); + // And we just call the `_start` function! + let start = instance.exports.get_function("_start")?; + start.call(&mut store, &[])?; + + wasi_env.on_exit(&mut store, None); + + Ok(()) +} + +#[test] +#[cfg(feature = "wasi")] +fn test_wasi() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/examples/wasi_pipes.rs b/arbitrator/tools/module_roots/wasmer/examples/wasi_pipes.rs new file mode 100644 index 0000000..925c880 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/examples/wasi_pipes.rs @@ -0,0 +1,60 @@ +//! Piping to and from a WASI compiled WebAssembly module with Wasmer. +//! +//! This example builds on the WASI example, showing how you can pipe to and +//! from a WebAssembly module. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example wasi-pipes --release --features "cranelift,wasi" +//! ``` +//! +//! Ready? + +use std::io::{Read, Write}; +use wasmer::{Module, Store}; +use wasmer_wasix::{Pipe, WasiEnv}; + +fn main() -> Result<(), Box> { + let wasm_path = concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/wasi-wast/wasi/unstable/pipe_reverse.wasm" + ); + // Let's declare the Wasm module with the text representation. + let wasm_bytes = std::fs::read(wasm_path)?; + + // Create a Store. + let mut store = Store::default(); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + let msg = "racecar go zoom"; + println!("Writing \"{}\" to the WASI stdin...", msg); + let (mut stdin_sender, stdin_reader) = Pipe::channel(); + let (stdout_sender, mut stdout_reader) = Pipe::channel(); + + // To write to the stdin + writeln!(stdin_sender, "{}", msg)?; + + println!("Running module..."); + // First, we create the `WasiEnv` with the stdio pipes + WasiEnv::builder("hello") + .stdin(Box::new(stdin_reader)) + .stdout(Box::new(stdout_sender)) + .run_with_store(module, &mut store)?; + + // To read from the stdout + let mut buf = String::new(); + stdout_reader.read_to_string(&mut buf)?; + println!("Read \"{}\" from the WASI stdout!", buf.trim()); + + Ok(()) +} + +#[test] +#[cfg(feature = "wasi")] +fn test_wasi() -> Result<(), Box> { + main() +} diff --git a/arbitrator/tools/module_roots/wasmer/flake.lock b/arbitrator/tools/module_roots/wasmer/flake.lock new file mode 100644 index 0000000..3a9822e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/flake.lock @@ -0,0 +1,59 @@ +{ + "nodes": { + "flakeutils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1685518550, + "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1686089707, + "narHash": "sha256-LTNlJcru2qJ0XhlhG9Acp5KyjB774Pza3tRH0pKIb3o=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "af21c31b2a1ec5d361ed8050edd0303c31306397", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "flakeutils": "flakeutils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/arbitrator/tools/module_roots/wasmer/flake.nix b/arbitrator/tools/module_roots/wasmer/flake.nix new file mode 100644 index 0000000..dbecdfb --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/flake.nix @@ -0,0 +1,63 @@ +{ + description = "wasmer Webassembly runtime"; + + inputs = { + flakeutils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flakeutils }: + flakeutils.lib.eachDefaultSystem (system: + let + NAME = "wasmer"; + VERSION = "0.1"; + + pkgs = import nixpkgs { + inherit system; + }; + + in + rec { + + # packages.${NAME} = pkgs.stdenv.mkDerivation { + # pname = NAME; + # version = VERSION; + + # buildPhase = "echo 'no-build'"; + # }; + + # defaultPackage = packages.${NAME}; + + # # For `nix run`. + # apps.${NAME} = flakeutils.lib.mkApp { + # drv = packages.${NAME}; + # }; + # defaultApp = apps.${NAME}; + + devShell = pkgs.stdenv.mkDerivation { + name = NAME; + src = self; + buildInputs = with pkgs; [ + pkgconfig + openssl + llvmPackages_15.libllvm + # Snapshot testing + cargo-insta + wabt + binaryen + + # LLVM and related dependencies + llvmPackages_15.llvm + libxml2 + libffi + + # Test runner + cargo-nextest + ]; + runtimeDependencies = with pkgs; [ ]; + + LD_LIBRARY_PATH = "${pkgs.openssl.out}/lib"; + LLVM_SYS_150_PREFIX = "${pkgs.llvmPackages_15.llvm.dev}"; + }; + } + ); +} diff --git a/arbitrator/tools/module_roots/wasmer/fuzz/.gitignore b/arbitrator/tools/module_roots/wasmer/fuzz/.gitignore new file mode 100644 index 0000000..572e03b --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/fuzz/.gitignore @@ -0,0 +1,4 @@ + +target +corpus +artifacts diff --git a/arbitrator/tools/module_roots/wasmer/fuzz/Cargo.toml b/arbitrator/tools/module_roots/wasmer/fuzz/Cargo.toml new file mode 100644 index 0000000..d4a2e6a --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/fuzz/Cargo.toml @@ -0,0 +1,61 @@ +[package] +name = "wasmer-bin-fuzz" +version = "0.0.0" +authors = ["Automatically generated"] +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +anyhow = "1" +wasm-smith = "0.4.4" +libfuzzer-sys = "0.4.0" +wasmer = { path = "../lib/api" } +wasmer-compiler-cranelift = { path = "../lib/compiler-cranelift", optional = true } +wasmer-compiler-llvm = { path = "../lib/compiler-llvm", optional = true } +wasmer-compiler-singlepass = { path = "../lib/compiler-singlepass", optional = true } +wasmer-compiler = { path = "../lib/compiler", optional = true } +wasmer-middlewares = { path = "../lib/middlewares" } +wasmprinter = "0.2" + +[features] +cranelift = [ "wasmer-compiler-cranelift" ] +llvm = [ "wasmer-compiler-llvm" ] +singlepass = [ "wasmer-compiler-singlepass" ] +universal = [ "wasmer-compiler" ] +wasmer-artifact-load = ["wasmer-compiler/wasmer-artifact-load"] +wasmer-artifact-create = ["wasmer-compiler/wasmer-artifact-create"] +static-artifact-load = ["wasmer-compiler/static-artifact-load"] +static-artifact-create = ["wasmer-compiler/static-artifact-create"] + +[[bin]] +name = "equivalence_universal" +path = "fuzz_targets/equivalence_universal.rs" +required-features = ["universal"] + +[[bin]] +name = "universal_cranelift" +path = "fuzz_targets/universal_cranelift.rs" +required-features = ["universal", "cranelift"] + +[[bin]] +name = "universal_llvm" +path = "fuzz_targets/universal_llvm.rs" +required-features = ["universal", "llvm"] + +[[bin]] +name = "universal_singlepass" +path = "fuzz_targets/universal_singlepass.rs" +required-features = ["universal", "singlepass"] + +[[bin]] +name = "metering" +path = "fuzz_targets/metering.rs" +required-features = ["universal", "cranelift"] + +[[bin]] +name = "deterministic" +path = "fuzz_targets/deterministic.rs" +required-features = ["universal", "cranelift", "llvm", "singlepass"] diff --git a/arbitrator/tools/module_roots/wasmer/fuzz/README.md b/arbitrator/tools/module_roots/wasmer/fuzz/README.md new file mode 100644 index 0000000..13e40a3 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/fuzz/README.md @@ -0,0 +1,80 @@ +# Wasmer Fuzz Testing + +[Fuzz testing](https://en.wikipedia.org/wiki/Fuzzing) is: + +> An automated testing technique that involves providing invalid, +> unexpected, or random data as inputs to a program. + +We use fuzz testing to automatically discover bugs in the Wasmer runtime. + +This `fuzz/` directory contains the configuration and the fuzz tests +for Wasmer. To generate and to run the fuzz tests, we use the +[`cargo-fuzz`] library. + +## Installation + +You may need to install the [`cargo-fuzz`] library to get the `cargo +fuzz` subcommand. Use + +```sh +$ cargo install cargo-fuzz +``` + +`cargo-fuzz` is documented in the [Rust Fuzz +Book](https://rust-fuzz.github.io/book/cargo-fuzz.html). + +## Running a fuzzer + +This directory provides multiple fuzzers, like for example `validate`. You can run it with: + +```sh +$ cargo fuzz run validate +``` + +Another example with the `universal_cranelift` fuzzer: + +```sh +$ cargo fuzz run universal_cranelift +``` + +See the +[`fuzz/fuzz_targets`](https://github.com/wasmerio/wasmer/tree/fuzz/fuzz_targets/) +directory for the full list of fuzzers. + +You should see output that looks something like this: + +``` +#1408022 NEW cov: 115073 ft: 503843 corp: 4659/1807Kb lim: 4096 exec/s: 889 rss: 857Mb L: 2588/4096 MS: 1 ChangeASCIIInt- +#1408273 NEW cov: 115073 ft: 503844 corp: 4660/1808Kb lim: 4096 exec/s: 888 rss: 857Mb L: 1197/4096 MS: 1 ShuffleBytes- +#1408534 NEW cov: 115073 ft: 503866 corp: 4661/1809Kb lim: 4096 exec/s: 886 rss: 857Mb L: 977/4096 MS: 1 ShuffleBytes- +#1408540 NEW cov: 115073 ft: 503869 corp: 4662/1811Kb lim: 4096 exec/s: 886 rss: 857Mb L: 2067/4096 MS: 1 ChangeBit- +#1408831 NEW cov: 115073 ft: 503945 corp: 4663/1811Kb lim: 4096 exec/s: 885 rss: 857Mb L: 460/4096 MS: 1 CMP- DE: "\x16\x00\x00\x00\x00\x00\x00\x00"- +#1408977 NEW cov: 115073 ft: 503946 corp: 4664/1813Kb lim: 4096 exec/s: 885 rss: 857Mb L: 1972/4096 MS: 1 ShuffleBytes- +#1408999 NEW cov: 115073 ft: 503949 corp: 4665/1814Kb lim: 4096 exec/s: 884 rss: 857Mb L: 964/4096 MS: 2 ChangeBit-ShuffleBytes- +#1409040 NEW cov: 115073 ft: 503950 corp: 4666/1814Kb lim: 4096 exec/s: 884 rss: 857Mb L: 90/4096 MS: 1 ChangeBit- +#1409042 NEW cov: 115073 ft: 503951 corp: 4667/1814Kb lim: 4096 exec/s: 884 rss: 857Mb L: 174/4096 MS: 2 ChangeByte-ChangeASCIIInt- +``` + +It will continue to generate random inputs forever, until it finds a +bug or is terminated. The testcases for bugs it finds go into +`fuzz/artifacts/universal_cranelift` and you can rerun the fuzzer on a +single input by passing it on the command line `cargo fuzz run +universal_cranelift /path/to/testcase`. + +## The corpus + +Each fuzzer has an individual corpus under `fuzz/corpus/test_name`, +created on first run if not already present. The fuzzers use +`wasm-smith` which means that the testcase files are random number +seeds input to the Wasm generator, not `.wasm` files themselves. In +order to debug a testcase, you may find that you need to convert it +into a `.wasm` file. Using the standalone `wasm-smith` tool doesn't +work for this purpose because we use a custom configuration to our +`wasm_smith::Module`. Instead, our fuzzers use an environment variable +`DUMP_TESTCASE=path`. For example: + +```sh +$ DUMP_TESTCASE=/tmp/crash.wasm cargo fuzz run --features=universal,singlepass universal_singlepass fuzz/artifacts/universal_singlepass/crash-0966412eab4f89c52ce5d681807c8030349470f6 +``` + +[`cargo-fuzz`]: https://github.com/rust-fuzz/cargo-fuzz diff --git a/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/deterministic.rs b/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/deterministic.rs new file mode 100644 index 0000000..d2e8212 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/deterministic.rs @@ -0,0 +1,69 @@ +#![no_main] + +use libfuzzer_sys::{arbitrary, arbitrary::Arbitrary, fuzz_target}; +use wasm_smith::{Config, ConfiguredModule}; +use wasmer::{CompilerConfig, EngineBuilder, Module, Store}; +use wasmer_compiler::Engine; +use wasmer_compiler_cranelift::Cranelift; +use wasmer_compiler_llvm::LLVM; +use wasmer_compiler_singlepass::Singlepass; + +#[derive(Arbitrary, Debug, Default, Copy, Clone)] +struct NoImportsConfig; +impl Config for NoImportsConfig { + fn max_imports(&self) -> usize { + 0 + } + fn max_memory_pages(&self) -> u32 { + // https://github.com/wasmerio/wasmer/issues/2187 + 65535 + } + fn allow_start_export(&self) -> bool { + false + } +} + +fn compile_and_compare(name: &str, engine: Engine, wasm: &[u8]) { + let store = Store::new(engine); + + // compile for first time + let module = Module::new(&store, wasm).unwrap(); + let first = module.serialize().unwrap(); + + // compile for second time + let module = Module::new(&store, wasm).unwrap(); + let second = module.serialize().unwrap(); + + if first != second { + panic!("non-deterministic compilation from {}", name); + } +} + +fuzz_target!(|module: ConfiguredModule| { + let wasm_bytes = module.to_bytes(); + + let mut compiler = Cranelift::default(); + compiler.canonicalize_nans(true); + compiler.enable_verifier(); + compile_and_compare( + "universal-cranelift", + EngineBuilder::new(compiler.clone()).engine(), + &wasm_bytes, + ); + + let mut compiler = LLVM::default(); + compiler.canonicalize_nans(true); + compiler.enable_verifier(); + compile_and_compare( + "universal-llvm", + EngineBuilder::new(compiler.clone()).engine(), + &wasm_bytes, + ); + + let compiler = Singlepass::default(); + compile_and_compare( + "universal-singlepass", + EngineBuilder::new(compiler.clone()).engine(), + &wasm_bytes, + ); +}); diff --git a/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/equivalence_universal.rs b/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/equivalence_universal.rs new file mode 100644 index 0000000..4db992f --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/equivalence_universal.rs @@ -0,0 +1,206 @@ +#![no_main] +#![deny(unused_variables)] + +use anyhow::Result; +use libfuzzer_sys::{arbitrary, arbitrary::Arbitrary, fuzz_target}; +use wasm_smith::{Config, ConfiguredModule}; +use wasmer::{imports, CompilerConfig, EngineBuilder, Instance, Module, Store, Val}; +#[cfg(feature = "cranelift")] +use wasmer_compiler_cranelift::Cranelift; +#[cfg(feature = "llvm")] +use wasmer_compiler_llvm::LLVM; +#[cfg(feature = "singlepass")] +use wasmer_compiler_singlepass::Singlepass; + +#[derive(Arbitrary, Debug, Default, Copy, Clone)] +struct ExportedFunctionConfig; +impl Config for ExportedFunctionConfig { + fn max_imports(&self) -> usize { + 0 + } + fn max_memory_pages(&self) -> u32 { + // https://github.com/wasmerio/wasmer/issues/2187 + 65535 + } + fn min_funcs(&self) -> usize { + 1 + } + fn min_exports(&self) -> usize { + 1 + } +} + +struct WasmSmithModule(ConfiguredModule); +impl<'a> arbitrary::Arbitrary<'a> for WasmSmithModule { + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + let mut module = ConfiguredModule::::arbitrary(u)?; + module.ensure_termination(100000); + Ok(WasmSmithModule(module)) + } +} +impl std::fmt::Debug for WasmSmithModule { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&wasmprinter::print_bytes(self.0.to_bytes()).unwrap()) + } +} + +#[cfg(feature = "singlepass")] +fn maybe_instantiate_singlepass(wasm_bytes: &[u8]) -> Result> { + let compiler = Singlepass::default(); + let mut store = Store::new(compiler); + let module = Module::new(&store, &wasm_bytes); + let module = match module { + Ok(m) => m, + Err(e) => { + let error_message = format!("{}", e); + if error_message.contains("Validation error: invalid result arity: func type returns multiple values") || error_message.contains("Validation error: blocks, loops, and ifs may only produce a resulttype when multi-value is not enabled") || error_message.contains("multi-value returns not yet implemented") { + return Ok(None); + } + return Err(e.into()); + } + }; + let instance = Instance::new(&module, &imports! {})?; + Ok(Some(instance)) +} + +#[cfg(feature = "cranelift")] +fn maybe_instantiate_cranelift(wasm_bytes: &[u8]) -> Result> { + let mut compiler = Cranelift::default(); + compiler.canonicalize_nans(true); + compiler.enable_verifier(); + let mut store = Store::new(compiler); + let module = Module::new(&store, &wasm_bytes)?; + let instance = Instance::new(&module, &imports! {})?; + Ok(Some(instance)) +} + +#[cfg(feature = "llvm")] +fn maybe_instantiate_llvm(wasm_bytes: &[u8]) -> Result> { + let mut compiler = LLVM::default(); + compiler.canonicalize_nans(true); + compiler.enable_verifier(); + let mut store = Store::new(compiler); + let module = Module::new(&store, &wasm_bytes)?; + let instance = Instance::new(&module, &imports! {})?; + Ok(Some(instance)) +} + +#[derive(Debug)] +enum FunctionResult { + Error(String), + Values(Vec), +} + +#[derive(Debug, PartialEq, Eq)] +enum InstanceResult { + Error(String), + Functions(Vec), +} + +impl PartialEq for FunctionResult { + fn eq(&self, other: &Self) -> bool { + /* + match self { + FunctionResult::Error(self_message) => { + if let FunctionResult::Error(other_message) = other { + return self_message == other_message; + } + } + FunctionResult::Values(self_values) => { + if let FunctionResult::Values(other_values) = other { + return self_values == other_values; + } + } + } + false + */ + match (self, other) { + (FunctionResult::Values(self_values), FunctionResult::Values(other_values)) => { + self_values.len() == other_values.len() + && self_values + .iter() + .zip(other_values.iter()) + .all(|(x, y)| match (x, y) { + (Val::F32(x), Val::F32(y)) => x.to_bits() == y.to_bits(), + (Val::F64(x), Val::F64(y)) => x.to_bits() == y.to_bits(), + _ => x == y, + }) + } + _ => true, + } + } +} + +impl Eq for FunctionResult {} + +fn evaluate_instance(instance: Result) -> InstanceResult { + if let Err(_err) = instance { + /*let mut error_message = format!("{}", err); + // Remove the stack trace. + if error_message.starts_with("RuntimeError: unreachable\n") { + error_message = "RuntimeError: unreachable\n".into(); + } + InstanceResult::Error(error_message)*/ + InstanceResult::Error("".into()) + } else { + let instance = instance.unwrap(); + let mut results = vec![]; + for it in instance.exports.iter().functions() { + let (_, f) = it; + // TODO: support functions which take params. + if f.ty().params().is_empty() { + let result = f.call(&[]); + let result = if let Ok(values) = result { + FunctionResult::Values(values.into()) + } else { + /* + let err = result.unwrap_err(); + let error_message = err.message(); + FunctionResult::Error(error_message) + */ + FunctionResult::Error("".into()) + }; + results.push(result); + } + } + InstanceResult::Functions(results) + } +} + +fuzz_target!(|module: WasmSmithModule| { + let wasm_bytes = module.0.to_bytes(); + + if let Ok(path) = std::env::var("DUMP_TESTCASE") { + use std::fs::File; + use std::io::Write; + let mut file = File::create(path).unwrap(); + file.write_all(&wasm_bytes).unwrap(); + return; + } + + #[cfg(feature = "singlepass")] + let singlepass = maybe_instantiate_singlepass(&wasm_bytes) + .transpose() + .map(evaluate_instance); + #[cfg(feature = "cranelift")] + let cranelift = maybe_instantiate_cranelift(&wasm_bytes) + .transpose() + .map(evaluate_instance); + #[cfg(feature = "llvm")] + let llvm = maybe_instantiate_llvm(&wasm_bytes) + .transpose() + .map(evaluate_instance); + + #[cfg(all(feature = "singlepass", feature = "cranelift"))] + if singlepass.is_some() && cranelift.is_some() { + assert_eq!(singlepass.as_ref().unwrap(), cranelift.as_ref().unwrap()); + } + #[cfg(all(feature = "singlepass", feature = "llvm"))] + if singlepass.is_some() && llvm.is_some() { + assert_eq!(singlepass.as_ref().unwrap(), llvm.as_ref().unwrap()); + } + #[cfg(all(feature = "cranelift", feature = "llvm"))] + if cranelift.is_some() && llvm.is_some() { + assert_eq!(cranelift.as_ref().unwrap(), llvm.as_ref().unwrap()); + } +}); diff --git a/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/metering.rs b/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/metering.rs new file mode 100644 index 0000000..6c4f7f8 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/metering.rs @@ -0,0 +1,72 @@ +#![no_main] + +use libfuzzer_sys::{arbitrary, arbitrary::Arbitrary, fuzz_target}; +use std::sync::Arc; +use wasm_smith::{Config, ConfiguredModule}; +use wasmer::wasmparser::Operator; +use wasmer::{imports, CompilerConfig, Instance, Module, Store}; +use wasmer_compiler_cranelift::Cranelift; +use wasmer_middlewares::Metering; + +#[derive(Arbitrary, Debug, Default, Copy, Clone)] +struct NoImportsConfig; +impl Config for NoImportsConfig { + fn max_imports(&self) -> usize { + 0 + } + fn max_memory_pages(&self) -> u32 { + // https://github.com/wasmerio/wasmer/issues/2187 + 65535 + } + fn allow_start_export(&self) -> bool { + false + } +} + +#[derive(Arbitrary)] +struct WasmSmithModule(ConfiguredModule); +impl std::fmt::Debug for WasmSmithModule { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&wasmprinter::print_bytes(self.0.to_bytes()).unwrap()) + } +} + +fn cost(operator: &Operator) -> u64 { + match operator { + Operator::LocalGet { .. } | Operator::I32Const { .. } => 1, + Operator::I32Add { .. } => 2, + _ => 0, + } +} + +fuzz_target!(|module: WasmSmithModule| { + let wasm_bytes = module.0.to_bytes(); + + if let Ok(path) = std::env::var("DUMP_TESTCASE") { + use std::fs::File; + use std::io::Write; + let mut file = File::create(path).unwrap(); + file.write_all(&wasm_bytes).unwrap(); + return; + } + + let mut compiler = Cranelift::default(); + compiler.canonicalize_nans(true); + compiler.enable_verifier(); + let metering = Arc::new(Metering::new(10, cost)); + compiler.push_middleware(metering); + let mut store = Store::new(compiler); + let module = Module::new(&store, &wasm_bytes).unwrap(); + match Instance::new(&mut store, &module, &imports! {}) { + Ok(_) => {} + Err(e) => { + let error_message = format!("{}", e); + if error_message.starts_with("RuntimeError: ") + && error_message.contains("out of bounds") + { + return; + } + panic!("{}", e); + } + } +}); diff --git a/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/universal_cranelift.rs b/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/universal_cranelift.rs new file mode 100644 index 0000000..764947f --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/universal_cranelift.rs @@ -0,0 +1,58 @@ +#![no_main] + +use libfuzzer_sys::{arbitrary, arbitrary::Arbitrary, fuzz_target}; +use wasm_smith::{Config, ConfiguredModule}; +use wasmer::{imports, CompilerConfig, Instance, Module, Store}; +use wasmer_compiler_cranelift::Cranelift; + +#[derive(Arbitrary, Debug, Default, Copy, Clone)] +struct NoImportsConfig; +impl Config for NoImportsConfig { + fn max_imports(&self) -> usize { + 0 + } + fn max_memory_pages(&self) -> u32 { + // https://github.com/wasmerio/wasmer/issues/2187 + 65535 + } + fn allow_start_export(&self) -> bool { + false + } +} +#[derive(Arbitrary)] +struct WasmSmithModule(ConfiguredModule); +impl std::fmt::Debug for WasmSmithModule { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&wasmprinter::print_bytes(self.0.to_bytes()).unwrap()) + } +} + +fuzz_target!(|module: WasmSmithModule| { + let wasm_bytes = module.0.to_bytes(); + + if let Ok(path) = std::env::var("DUMP_TESTCASE") { + use std::fs::File; + use std::io::Write; + let mut file = File::create(path).unwrap(); + file.write_all(&wasm_bytes).unwrap(); + return; + } + + let mut compiler = Cranelift::default(); + compiler.canonicalize_nans(true); + compiler.enable_verifier(); + let mut store = Store::new(compiler); + let module = Module::new(&store, &wasm_bytes).unwrap(); + match Instance::new(&mut store, &module, &imports! {}) { + Ok(_) => {} + Err(e) => { + let error_message = format!("{}", e); + if error_message.starts_with("RuntimeError: ") + && error_message.contains("out of bounds") + { + return; + } + panic!("{}", e); + } + } +}); diff --git a/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/universal_llvm.rs b/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/universal_llvm.rs new file mode 100644 index 0000000..1f051f8 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/universal_llvm.rs @@ -0,0 +1,58 @@ +#![no_main] + +use libfuzzer_sys::{arbitrary, arbitrary::Arbitrary, fuzz_target}; +use wasm_smith::{Config, ConfiguredModule}; +use wasmer::{imports, CompilerConfig, Instance, Module, Store}; +use wasmer_compiler_llvm::LLVM; + +#[derive(Arbitrary, Debug, Default, Copy, Clone)] +struct NoImportsConfig; +impl Config for NoImportsConfig { + fn max_imports(&self) -> usize { + 0 + } + fn max_memory_pages(&self) -> u32 { + // https://github.com/wasmerio/wasmer/issues/2187 + 65535 + } + fn allow_start_export(&self) -> bool { + false + } +} +#[derive(Arbitrary)] +struct WasmSmithModule(ConfiguredModule); +impl std::fmt::Debug for WasmSmithModule { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&wasmprinter::print_bytes(self.0.to_bytes()).unwrap()) + } +} + +fuzz_target!(|module: WasmSmithModule| { + let wasm_bytes = module.0.to_bytes(); + + if let Ok(path) = std::env::var("DUMP_TESTCASE") { + use std::fs::File; + use std::io::Write; + let mut file = File::create(path).unwrap(); + file.write_all(&wasm_bytes).unwrap(); + return; + } + + let mut compiler = LLVM::default(); + compiler.canonicalize_nans(true); + compiler.enable_verifier(); + let mut store = Store::new(compiler); + let module = Module::new(&store, &wasm_bytes).unwrap(); + match Instance::new(&mut store, &module, &imports! {}) { + Ok(_) => {} + Err(e) => { + let error_message = format!("{}", e); + if error_message.starts_with("RuntimeError: ") + && error_message.contains("out of bounds") + { + return; + } + panic!("{}", e); + } + } +}); diff --git a/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/universal_singlepass.rs b/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/universal_singlepass.rs new file mode 100644 index 0000000..762f6ec --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/fuzz/fuzz_targets/universal_singlepass.rs @@ -0,0 +1,66 @@ +#![no_main] + +use libfuzzer_sys::{arbitrary, arbitrary::Arbitrary, fuzz_target}; +use wasm_smith::{Config, ConfiguredModule}; +use wasmer::{imports, Instance, Module, Store}; +use wasmer_compiler_singlepass::Singlepass; + +#[derive(Arbitrary, Debug, Default, Copy, Clone)] +struct NoImportsConfig; +impl Config for NoImportsConfig { + fn max_imports(&self) -> usize { + 0 + } + fn max_memory_pages(&self) -> u32 { + // https://github.com/wasmerio/wasmer/issues/2187 + 65535 + } + fn allow_start_export(&self) -> bool { + false + } +} +#[derive(Arbitrary)] +struct WasmSmithModule(ConfiguredModule); +impl std::fmt::Debug for WasmSmithModule { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&wasmprinter::print_bytes(self.0.to_bytes()).unwrap()) + } +} + +fuzz_target!(|module: WasmSmithModule| { + let wasm_bytes = module.0.to_bytes(); + + if let Ok(path) = std::env::var("DUMP_TESTCASE") { + use std::fs::File; + use std::io::Write; + let mut file = File::create(path).unwrap(); + file.write_all(&wasm_bytes).unwrap(); + return; + } + + let compiler = Singlepass::default(); + let mut store = Store::new(compiler); + let module = Module::new(&store, &wasm_bytes); + let module = match module { + Ok(m) => m, + Err(e) => { + let error_message = format!("{}", e); + if error_message.contains("Validation error: invalid result arity: func type returns multiple values") || error_message.contains("Validation error: blocks, loops, and ifs may only produce a resulttype when multi-value is not enabled") || error_message.contains("multi-value returns not yet implemented") { + return; + } + panic!("{}", e); + } + }; + match Instance::new(&mut store, &module, &imports! {}) { + Ok(_) => {} + Err(e) => { + let error_message = format!("{}", e); + if error_message.starts_with("RuntimeError: ") + && error_message.contains("out of bounds") + { + return; + } + panic!("{}", e); + } + } +}); diff --git a/arbitrator/tools/module_roots/wasmer/fuzzbuzz.yaml b/arbitrator/tools/module_roots/wasmer/fuzzbuzz.yaml new file mode 100644 index 0000000..cbf6d4a --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/fuzzbuzz.yaml @@ -0,0 +1,9 @@ +wasmer: + language: rust + features: + - universal + - cranelift + - singlepass + deps: + - run: apt update + - run: apt install -y zlib1g-dev libffi-dev build-essential diff --git a/arbitrator/tools/module_roots/wasmer/lib/README.md b/arbitrator/tools/module_roots/wasmer/lib/README.md new file mode 100644 index 0000000..1cff03e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/README.md @@ -0,0 +1,42 @@ +# The Wasmer runtime crates + +The philosophy of Wasmer is to be very modular by design. It's +composed of a set of crates. We can group them as follows: + +* `api` — The public Rust or JS API exposes everything a user needs to use Wasmer + programatically through the `wasmer` crate, +* `c-api` — The public C API exposes everything a C user needs to use + Wasmer programatically, +* `cache` — The traits and types to cache compiled WebAssembly + modules, +* `cli` — The Wasmer CLI itself, +* `compiler` — The base for the compiler implementations, it defines + the framework for the compilers and provides everything they need: + * `compiler-cranelift` — A WebAssembly compiler based on the + Cranelift compiler infrastructure, + * `compiler-llvm` — A WebAssembly compiler based on the LLVM + compiler infrastructure; recommended for runtime speed + performance, + * `compiler-singlepass` — A WebAssembly compiler based on our own + compilation infrastructure; recommended for compilation-time speed + performance. +* `derive` — A set of procedural macros used inside Wasmer, +* ABI: + * `emscripten` — Emscripten ABI implementation inside Wasmer, + * `wasi` — WASI ABI implementation inside Wasmer: + * `wasi-types` — All the WASI types, +* `engine` — The general abstraction for creating an engine, which is + responsible of leading the compiling and running flow. Using the + same compiler, the runtime performance will be approximately the + same, however the way it stores and loads the executable code will + differ: + * `engine-universal` — stores the code in a custom file format, and + loads it in memory, + * `object` — A library to cross-generate native objects for various + platforms. +* `middlewares` — A collection of middlewares, like `metering` that + tracks how many operators are executed in total and putting a limit + on the total number of operators executed, +* `types` — The basic structures to use WebAssembly, +* `vm` — The Wasmer VM runtime library, the low-level base of + everything. diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/Cargo.toml b/arbitrator/tools/module_roots/wasmer/lib/api/Cargo.toml new file mode 100644 index 0000000..34db9d4 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/Cargo.toml @@ -0,0 +1,153 @@ +[package] +name = "wasmer" +description = "High-performance WebAssembly runtime" +categories = ["wasm"] +keywords = ["wasm", "webassembly", "runtime", "vm"] +readme = "README.md" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +##### +# This crate comes in 2 major flavors: +# +# - `sys`, where `wasmer` will be compiled to a native executable +# which provides compilers, engines, a full VM etc. +# - `js`, where `wasmer` will be compiled to WebAssembly to run in a +# JavaScript host. +##### + +# Shared dependencies. +[dependencies] +# - Mandatory shared dependencies. +indexmap = { version = "1.6" } +cfg-if = "1.0" +thiserror = "1.0" +more-asserts = "0.2" +derivative = { version = "^2" } +bytes = "1" +tracing = { version = "0.1" } +# - Optional shared dependencies. +wat = { version = "=1.0.71", optional = true } +rustc-demangle = "0.1" +shared-buffer = { workspace = true } + +# Dependencies and Development Dependencies for `sys`. +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +# - Mandatory dependencies for `sys`. +wasmer-vm = { path = "../vm", version = "=4.2.8" } +wasmer-compiler = { path = "../compiler", version = "=4.2.8" } +wasmer-derive = { path = "../derive", version = "=4.2.8" } +wasmer-types = { path = "../types", version = "=4.2.8" } +target-lexicon = { version = "0.12.2", default-features = false } +# - Optional dependencies for `sys`. +wasmer-compiler-singlepass = { path = "../compiler-singlepass", version = "=4.2.8", optional = true } +wasmer-compiler-cranelift = { path = "../compiler-cranelift", version = "=4.2.8", optional = true } +wasmer-compiler-llvm = { path = "../compiler-llvm", version = "=4.2.8", optional = true } + +wasm-bindgen = { version = "0.2.74", optional = true } +js-sys = { version = "0.3.51", optional = true } +rusty_jsc = { version = "0.1.0", optional = true } +wasmparser = { workspace = true, default-features = false, optional = true } + +# - Mandatory dependencies for `sys` on Windows. +[target.'cfg(all(not(target_arch = "wasm32"), target_os = "windows"))'.dependencies] +winapi = "0.3" +# - Development Dependencies for `sys`. +[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] +wat = "1.0" +tempfile = "3.6.0" +anyhow = "1.0" +macro-wasmer-universal-test = { version = "4.2.8", path = "./macro-wasmer-universal-test" } + +# Dependencies and Develoment Dependencies for `js`. +[target.'cfg(target_arch = "wasm32")'.dependencies] +# - Mandatory dependencies for `js`. +wasmer-types = { path = "../types", version = "=4.2.8", default-features = false, features = ["std"] } +wasm-bindgen = "0.2.74" +js-sys = "0.3.51" +wasmer-derive = { path = "../derive", version = "=4.2.8" } +# - Optional dependencies for `js`. +wasmparser = { workspace = true, default-features = false, optional = true } +hashbrown = { version = "0.11", optional = true } +serde-wasm-bindgen = { version = "0.4.5" } +serde = { version = "1.0", features = ["derive"] } + +# - Development Dependencies for `js`. +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wat = "1.0" +anyhow = "1.0" +wasm-bindgen-test = "0.3.0" +macro-wasmer-universal-test = { version = "4.2.8", path = "./macro-wasmer-universal-test" } + +# Specific to `js`. +# +# `wasm-opt` is on by default in for the release profile, but it can be +# disabled by setting it to `false` +[package.metadata.wasm-pack.profile.release] +wasm-opt = false + +[badges] +maintenance = { status = "actively-developed" } + +[features] +default = ["sys-default"] +# default = ["js-default"] +std = [] +core = ["hashbrown"] + +# Features for `sys`. +sys = ["wasmer-compiler/translator", "wasmer-compiler/compiler", "std"] +sys-default = ["sys", "wat", "cranelift"] +# - Compilers. +compiler = ["sys"] +singlepass = ["compiler", "wasmer-compiler-singlepass"] +cranelift = ["compiler", "wasmer-compiler-cranelift"] +llvm = ["compiler", "wasmer-compiler-llvm"] +# - Engines. +engine = ["sys"] +# - Deprecated features. +jit = ["engine"] + +# Features for `js`. +js = ["wasm-bindgen", "js-sys"] +js-default = ["js", "std", "wasm-types-polyfill"] + +wasm-types-polyfill = ["wasmparser"] + +jsc = ["rusty_jsc", "wasm-types-polyfill", "wasmparser"] + +js-serializable-module = [] + +# Optional +enable-serde = [ + "wasmer-vm/enable-serde", + "wasmer-compiler/enable-serde", + "wasmer-types/enable-serde", +] + +wasmer-artifact-load = ["wasmer-compiler/wasmer-artifact-load"] +wasmer-artifact-create = ["wasmer-compiler/wasmer-artifact-create"] +static-artifact-load = ["wasmer-compiler/static-artifact-load"] +static-artifact-create = ["wasmer-compiler/static-artifact-create"] + +[package.metadata.docs.rs] +features = [ + "compiler", + "core", + "cranelift", + "engine", + "jit", + "singlepass", + "static-artifact-create", + "static-artifact-load", + "sys", + "sys-default", + "wasmer-artifact-create", + "wasmer-artifact-load", +] +rustc-args = ["--cfg", "docsrs"] diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/README.md b/arbitrator/tools/module_roots/wasmer/lib/api/README.md new file mode 100644 index 0000000..38b4f28 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/README.md @@ -0,0 +1,94 @@ +# `wasmer` [![Build Status](https://github.com/wasmerio/wasmer/workflows/build/badge.svg?style=flat-square)](https://github.com/wasmerio/wasmer/actions?query=workflow%3Abuild) [![Join Wasmer Slack](https://img.shields.io/static/v1?label=Slack&message=join%20chat&color=brighgreen&style=flat-square)](https://slack.wasmer.io) [![MIT License](https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square)](https://github.com/wasmerio/wasmer/blob/master/LICENSE) [![crates.io](https://img.shields.io/crates/v/wasmer.svg)](https://crates.io/crates/wasmer) + +[`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. + +It's designed to be safe and secure, and runnable in any kind of environment. + +## Usage + +Here is a small example of using Wasmer to run a WebAssembly module +written with its WAT format (textual format): + +```rust +use wasmer::{Store, Module, Instance, Value, imports}; + +fn main() -> anyhow::Result<()> { + let module_wat = r#" + (module + (type $t0 (func (param i32) (result i32))) + (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32) + get_local $p0 + i32.const 1 + i32.add)) + "#; + + let mut store = Store::default(); + let module = Module::new(&store, &module_wat)?; + // The module doesn't import anything, so we create an empty import object. + let import_object = imports! {}; + let instance = Instance::new(&mut store, &module, &import_object)?; + + let add_one = instance.exports.get_function("add_one")?; + let result = add_one.call(&mut store, &[Value::I32(42)])?; + assert_eq!(result[0], Value::I32(43)); + + Ok(()) +} +``` + +[Discover the full collection of examples](https://github.com/wasmerio/wasmer/tree/master/examples). + +## Features + +Wasmer is not only fast, but also designed to be *highly customizable*: + +* **Pluggable compilers** — A compiler is used by the engine to + transform WebAssembly into executable code: + * [`wasmer-compiler-singlepass`] provides a fast compilation-time + but an unoptimized runtime speed, + * [`wasmer-compiler-cranelift`] provides the right balance between + compilation-time and runtime performance, useful for development, + * [`wasmer-compiler-llvm`] provides a deeply optimized executable + code with the fastest runtime speed, ideal for production. + +* **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 + compiler, which makes it more portable and faster to load. It's + ideal for constrainted environments. + +* **Cross-compilation** — Most compilers support cross-compilation. It + means it possible to pre-compile a WebAssembly module targetting a + different architecture or platform and serialize it, to then run it + on the targetted architecture and platform later. + +* **Run Wasmer in a JavaScript environment** — With the `js` Cargo + feature, it is possible to compile a Rust program using Wasmer to + WebAssembly. In this context, the resulting WebAssembly module will + expect to run in a JavaScript environment, like a browser, Node.js, + Deno and so on. In this specific scenario, there is no engines or + compilers available, it's the one available in the JavaScript + environment that will be used. + +Wasmer ships by default with the Cranelift compiler as its great for +development purposes. However, we strongly encourage to use the LLVM +compiler in production as it performs about 50% faster, achieving +near-native speeds. + +Note: if one wants to use multiple compilers at the same time, it's +also possible! One will need to import them directly via each of the +compiler crates. + +Read [the documentation to learn +more](https://wasmerio.github.io/wasmer/crates/doc/wasmer/). + +--- + +Made with ❤ī¸ by the Wasmer team, for the community + +[`wasmer-compiler-singlepass`]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-singlepass +[`wasmer-compiler-cranelift`]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-cranelift +[`wasmer-compiler-llvm`]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-llvm diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/macro-wasmer-universal-test/Cargo.toml b/arbitrator/tools/module_roots/wasmer/lib/api/macro-wasmer-universal-test/Cargo.toml new file mode 100644 index 0000000..08e425f --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/macro-wasmer-universal-test/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "macro-wasmer-universal-test" +version = "4.2.8" +edition = "2021" +license = "MIT" +description = "Universal test macro for wasmer-test" + +[lib] +proc-macro = true + +[dependencies] +proc-quote = "0.4.0" +proc-macro2 = "1.0.43" +syn = { version = "1.0.99", features = ["full"] } +quote = "1.0.21" \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/macro-wasmer-universal-test/src/lib.rs b/arbitrator/tools/module_roots/wasmer/lib/api/macro-wasmer-universal-test/src/lib.rs new file mode 100644 index 0000000..9af4a8c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/macro-wasmer-universal-test/src/lib.rs @@ -0,0 +1,36 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; +use proc_macro2::{Ident, Span}; + +#[proc_macro_attribute] +pub fn universal_test(_attr: TokenStream, item: TokenStream) -> TokenStream { + let item_clone = item.clone(); + let mut iter = item_clone.into_iter(); + let _ = iter.next().unwrap(); // fn + let item_tree: proc_macro::TokenTree = iter.next().unwrap(); // fn ... + let n = match &item_tree { + proc_macro::TokenTree::Ident(i) => i.to_string(), + _ => panic!("expected fn ...() -> Result<(), String>"), + }; + + let function_name_normal = Ident::new(&n, Span::call_site()); + let function_name_js = Ident::new(&format!("{}_js", n), Span::call_site()); + let parsed = match syn::parse::(item) { + Ok(o) => o, + Err(e) => { + return proc_macro::TokenStream::from(e.to_compile_error()); + } + }; + + let tokens = quote::quote! { + #[cfg(feature = "js")] + #[cfg_attr(feature = "js", wasm_bindgen_test)] + fn #function_name_js() { #function_name_normal().unwrap(); } + + #[cfg_attr(any(feature = "sys", feature = "jsc"), test)] + #parsed + }; + + proc_macro::TokenStream::from(tokens) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/access.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/access.rs new file mode 100644 index 0000000..7d10348 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/access.rs @@ -0,0 +1,205 @@ +use std::mem::MaybeUninit; + +use crate::mem_access::{WasmRef, WasmSlice}; + +pub(super) enum SliceCow<'a, T> { + #[allow(dead_code)] + Borrowed(&'a mut [T]), + #[allow(dead_code)] + Owned(Vec, bool), +} + +impl<'a, T> AsRef<[T]> for SliceCow<'a, T> { + fn as_ref(&self) -> &[T] { + match self { + Self::Borrowed(buf) => buf, + Self::Owned(buf, _) => buf, + } + } +} + +impl<'a, T> AsMut<[T]> for SliceCow<'a, T> { + fn as_mut(&mut self) -> &mut [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 + match self { + Self::Borrowed(buf) => buf, + Self::Owned(buf, modified) => { + *modified = true; + buf.as_mut() + } + } + } +} + +/// Provides direct memory access to a piece of memory that +/// is owned by WASM +pub struct WasmSliceAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + pub(super) slice: WasmSlice<'a, T>, + pub(super) buf: SliceCow<'a, T>, +} + +impl<'a, T> AsRef<[T]> for WasmSliceAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + fn as_ref(&self) -> &[T] { + self.buf.as_ref() + } +} + +impl<'a, T> AsMut<[T]> for WasmSliceAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + fn as_mut(&mut self) -> &mut [T] { + self.buf.as_mut() + } +} + +impl<'a, T> WasmSliceAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + /// Returns an iterator of all the elements in the slice + pub fn iter(&'a self) -> std::slice::Iter<'a, T> { + self.as_ref().iter() + } + + /// Returns an iterator of all the elements in the slice + pub fn iter_mut(&'a mut self) -> std::slice::IterMut<'a, T> { + self.buf.as_mut().iter_mut() + } + + /// Number of elements in this slice + pub fn len(&self) -> usize { + self.buf.as_ref().len() + } + + /// If the slice is empty + pub fn is_empty(&self) -> bool { + self.buf.as_ref().is_empty() + } +} + +impl<'a> WasmSliceAccess<'a, u8> { + /// Writes to the address pointed to by this `WasmPtr` in a memory. + #[inline] + pub fn copy_from_slice(&mut self, src: &[u8]) { + let dst = self.buf.as_mut(); + dst.copy_from_slice(src); + } +} + +impl<'a, T> Drop for WasmSliceAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + fn drop(&mut self) { + if let SliceCow::Owned(buf, modified) = &self.buf { + if *modified { + self.slice.write_slice(buf.as_ref()).ok(); + } + } + } +} + +pub(super) enum RefCow<'a, T> { + #[allow(dead_code)] + Borrowed(&'a mut T), + #[allow(dead_code)] + Owned(T, bool), +} + +impl<'a, T> AsRef for RefCow<'a, T> { + fn as_ref(&self) -> &T { + match self { + Self::Borrowed(val) => val, + Self::Owned(val, _) => val, + } + } +} + +impl<'a, T> AsMut for RefCow<'a, T> { + fn as_mut(&mut self) -> &mut 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 + match self { + Self::Borrowed(val) => val, + Self::Owned(val, modified) => { + *modified = true; + val + } + } + } +} + +/// Provides direct memory access to a piece of memory that +/// is owned by WASM +pub struct WasmRefAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + pub(super) ptr: WasmRef<'a, T>, + pub(super) buf: RefCow<'a, T>, +} + +impl<'a, T> AsRef for WasmRefAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + fn as_ref(&self) -> &T { + self.buf.as_ref() + } +} + +impl<'a, T> AsMut for WasmRefAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + fn as_mut(&mut self) -> &mut T { + self.buf.as_mut() + } +} + +impl<'a, T> Drop for WasmRefAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + fn drop(&mut self) { + if let RefCow::Owned(val, modified) = &self.buf { + if *modified { + self.ptr.write(*val).ok(); + } + } + } +} + +impl<'a, T> WasmSliceAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + /// Returns a mutable slice that is not yet initialized + pub fn as_mut_uninit(&mut self) -> &mut [MaybeUninit] { + let ret: &mut [T] = self.buf.as_mut(); + let ret: &mut [MaybeUninit] = unsafe { std::mem::transmute(ret) }; + ret + } +} + +impl<'a, T> WasmRefAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + /// Returns a reference to an unitialized reference to this value + pub fn as_mut_uninit(&mut self) -> &mut MaybeUninit { + let ret: &mut T = self.buf.as_mut(); + let ret: &mut MaybeUninit = unsafe { std::mem::transmute(ret) }; + ret + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/engine.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/engine.rs new file mode 100644 index 0000000..820d4d9 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/engine.rs @@ -0,0 +1,170 @@ +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; +#[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; + +/// 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<'_>; +} + +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() + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/errors.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/errors.rs new file mode 100644 index 0000000..88c596d --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/errors.rs @@ -0,0 +1,287 @@ +#[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::ImportError; + +/// The WebAssembly.LinkError object indicates an error during +/// module instantiation (besides traps from the start function). +/// +/// This is based on the [link error][link-error] API. +/// +/// [link-error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/LinkError +#[derive(Debug, Clone)] +#[cfg_attr(feature = "std", derive(Error))] +#[cfg_attr(feature = "std", error("Link error: {0}"))] +pub enum LinkError { + /// An error occurred when checking the import types. + #[cfg_attr(feature = "std", error("Error while importing {0:?}.{1:?}: {2}"))] + Import(String, String, ImportError), + + /// A trap ocurred during linking. + #[cfg_attr(feature = "std", error("RuntimeError occurred during linking: {0}"))] + Trap(#[source] RuntimeError), + /// Insufficient resources available for linking. + #[cfg_attr(feature = "std", error("Insufficient resources: {0}"))] + Resource(String), +} + +/// An error while instantiating a module. +/// +/// This is not a common WebAssembly error, however +/// we need to differentiate from a `LinkError` (an error +/// that happens while linking, on instantiation), a +/// Trap that occurs when calling the WebAssembly module +/// start function, and an error when initializing the user's +/// host environments. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "std", derive(Error))] +pub enum InstantiationError { + /// A linking ocurred during instantiation. + #[cfg_attr(feature = "std", error(transparent))] + Link(LinkError), + + /// A runtime error occured while invoking the start function + #[cfg_attr(feature = "std", error(transparent))] + Start(RuntimeError), + + /// The module was compiled with a CPU feature that is not available on + /// the current host. + #[cfg_attr(feature = "std", error("missing required CPU features: {0:?}"))] + CpuFeature(String), + + /// Import from a different [`Store`][super::Store]. + /// This error occurs when an import from a different store is used. + #[cfg_attr(feature = "std", error("cannot mix imports from different stores"))] + DifferentStores, + + /// Import from a different Store. + /// This error occurs when an import from a different store is used. + #[cfg_attr(feature = "std", error("incorrect OS or architecture"))] + DifferentArchOS, +} + +/// A struct representing an aborted instruction execution, with a message +/// indicating the cause. +#[derive(Clone)] +pub struct RuntimeError { + pub(crate) inner: Arc, +} + +#[derive(Debug)] +struct RuntimeStringError { + details: String, +} + +impl RuntimeStringError { + fn new(msg: String) -> Self { + Self { details: msg } + } +} + +impl fmt::Display for RuntimeStringError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.details) + } +} + +impl std::error::Error for RuntimeStringError { + fn description(&self) -> &str { + &self.details + } +} + +pub(crate) struct RuntimeErrorInner { + /// The source error + pub(crate) source: Trap, + /// The trap code (if any) + trap_code: Option, + /// The reconstructed Wasm trace (from the native trace and the `GlobalFrameInfo`). + wasm_trace: Vec, +} + +impl RuntimeError { + /// Creates a new generic `RuntimeError` with the given `message`. + /// + /// # Example + /// ``` + /// let trap = wasmer::RuntimeError::new("unexpected error"); + /// assert_eq!("unexpected error", trap.message()); + /// ``` + pub fn new>(message: I) -> Self { + let msg = message.into(); + let source = RuntimeStringError::new(msg); + Self::user(Box::new(source)) + } + + /// Creates `RuntimeError` from an error and a WasmTrace + /// + /// # Example + /// ```ignore + /// let wasm_trace = vec![wasmer_types::FrameInfo::new( + /// "my_module".to_string(), + /// 0, + /// Some("my_function".to_string()), + /// 0.into(), + /// 2.into() + /// )]; + /// let trap = wasmer::RuntimeError::new_from_source(my_error, wasm_trace, None); + /// assert_eq!("unexpected error", trap.message()); + /// ``` + pub fn new_from_source( + source: Trap, + wasm_trace: Vec, + trap_code: Option, + ) -> Self { + Self { + inner: Arc::new(RuntimeErrorInner { + source, + wasm_trace, + trap_code, + }), + } + } + + /// Creates a custom user Error. + /// + /// This error object can be passed through Wasm frames and later retrieved + /// using the `downcast` method. + pub fn user(error: Box) -> Self { + match error.downcast::() { + Ok(err) => *err, + Err(error) => error.into(), + } + } + + /// Returns a reference the `message` stored in `Trap`. + pub fn message(&self) -> String { + if let Some(trap_code) = self.inner.trap_code { + trap_code.message().to_string() + } else { + self.inner.source.to_string() + } + } + + /// Returns a list of function frames in WebAssembly code that led to this + /// trap happening. + pub fn trace(&self) -> &[FrameInfo] { + &self.inner.wasm_trace + } + + /// Returns trap code, if it's a Trap + pub fn to_trap(self) -> Option { + self.inner.trap_code + } + + // /// Returns trap code, if it's a Trap + // pub fn to_source(self) -> &'static Trap { + // &self.inner.as_ref().source + // } + + /// Attempts to downcast the `RuntimeError` to a concrete type. + pub fn downcast(self) -> Result { + match Arc::try_unwrap(self.inner) { + Ok(inner) if inner.source.is::() => Ok(inner.source.downcast::().unwrap()), + Ok(inner) => Err(Self { + inner: Arc::new(inner), + }), + Err(inner) => Err(Self { inner }), + } + } + + /// Attempts to downcast the `RuntimeError` to a concrete type. + pub fn downcast_ref(&self) -> Option<&T> { + self.inner.as_ref().source.downcast_ref::() + } + + /// Returns true if the `RuntimeError` is the same as T + pub fn is(&self) -> bool { + self.inner.source.is::() + } +} + +impl fmt::Debug for RuntimeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RuntimeError") + .field("source", &self.inner.source) + .field("wasm_trace", &self.inner.wasm_trace) + .finish() + } +} + +impl fmt::Display for RuntimeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "RuntimeError: {}", self.message())?; + let trace = self.trace(); + if trace.is_empty() { + return Ok(()); + } + for frame in self.trace().iter() { + let name = frame.module_name(); + let func_index = frame.func_index(); + writeln!(f)?; + write!(f, " at ")?; + match frame.function_name() { + Some(name) => match rustc_demangle::try_demangle(name) { + Ok(name) => write!(f, "{}", name)?, + Err(_) => write!(f, "{}", name)?, + }, + None => write!(f, "")?, + } + write!( + f, + " ({}[{}]:0x{:x})", + name, + func_index, + frame.module_offset() + )?; + } + Ok(()) + } +} + +impl std::error::Error for RuntimeError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.inner.source.source() + } +} + +impl From> for RuntimeError { + fn from(error: Box) -> Self { + match error.downcast::() { + // The error is already a RuntimeError, we return it directly + Ok(runtime_error) => *runtime_error, + Err(error) => Trap::user(error).into(), + } + } +} + +/// Error that can occur during atomic operations. (notify/wait) +// Non-exhaustive to allow for future variants without breaking changes! +#[derive(PartialEq, Eq, Debug, Error)] +#[non_exhaustive] +pub enum AtomicsError { + /// Atomic operations are not supported by this memory. + Unimplemented, + /// To many waiter for address. + TooManyWaiters, + /// Atomic operations are disabled. + AtomicsDisabled, +} + +impl std::fmt::Display for AtomicsError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Unimplemented => write!(f, "Atomic operations are not supported"), + Self::TooManyWaiters => write!(f, "Too many waiters for address"), + Self::AtomicsDisabled => write!(f, "Atomic operations are disabled"), + } + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/exports.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/exports.rs new file mode 100644 index 0000000..d2f8211 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/exports.rs @@ -0,0 +1,306 @@ +use crate::store::AsStoreRef; +use crate::{Extern, Function, Global, Memory, Table, TypedFunction, WasmTypeList}; +use indexmap::IndexMap; +use std::fmt; +use std::iter::{ExactSizeIterator, FromIterator}; +use thiserror::Error; + +/// The `ExportError` can happen when trying to get a specific +/// export [`Extern`] from the [`Instance`] exports. +/// +/// [`Instance`]: crate::Instance +/// +/// # Examples +/// +/// ## Incompatible export type +/// +/// ```should_panic +/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value, ExportError}; +/// # let mut store = Store::default(); +/// # let wasm_bytes = wat2wasm(r#" +/// # (module +/// # (global $one (export "glob") f32 (f32.const 1))) +/// # "#.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(); +/// # +/// // This results with an error: `ExportError::IncompatibleType`. +/// let export = instance.exports.get_function("glob").unwrap(); +/// ``` +/// +/// ## Missing export +/// +/// ```should_panic +/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value, ExportError}; +/// # let mut store = Store::default(); +/// # let wasm_bytes = wat2wasm("(module)".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(); +/// # +/// // This results with an error: `ExportError::Missing`. +/// let export = instance.exports.get_function("unknown").unwrap(); +/// ``` +#[derive(Error, Debug, Clone)] +pub enum ExportError { + /// An error than occurs when the exported type and the expected type + /// are incompatible. + #[error("Incompatible Export Type")] + IncompatibleType, + /// This error arises when an export is missing + #[error("Missing export {0}")] + Missing(String), +} + +/// Exports is a special kind of map that allows easily unwrapping +/// the types of instances. +/// +/// TODO: add examples of using exports +#[derive(Clone, Default, PartialEq, Eq)] +pub struct Exports { + map: IndexMap, +} + +impl Exports { + /// Creates a new `Exports`. + pub fn new() -> Self { + Default::default() + } + + /// Creates a new `Exports` with capacity `n`. + pub fn with_capacity(n: usize) -> Self { + Self { + map: IndexMap::with_capacity(n), + } + } + + /// Return the number of exports in the `Exports` map. + pub fn len(&self) -> usize { + self.map.len() + } + + /// Return whether or not there are no exports + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Insert a new export into this `Exports` map. + pub fn insert(&mut self, name: S, value: E) + where + S: Into, + E: Into, + { + self.map.insert(name.into(), value.into()); + } + + /// Get an export given a `name`. + /// + /// The `get` method is specifically made for usage inside of + /// Rust APIs, as we can detect what's the desired type easily. + /// + /// If you want to get an export dynamically with type checking + /// please use the following functions: `get_func`, `get_memory`, + /// `get_table` or `get_global` instead. + /// + /// If you want to get an export dynamically handling manually + /// type checking manually, please use `get_extern`. + pub fn get<'a, T: Exportable<'a>>(&'a self, name: &str) -> Result<&'a T, ExportError> { + match self.map.get(name) { + None => Err(ExportError::Missing(name.to_string())), + Some(extern_) => T::get_self_from_extern(extern_), + } + } + + /// Get an export as a `Global`. + pub fn get_global(&self, name: &str) -> Result<&Global, ExportError> { + self.get(name) + } + + /// Get an export as a `Memory`. + pub fn get_memory(&self, name: &str) -> Result<&Memory, ExportError> { + self.get(name) + } + + /// Get an export as a `Table`. + pub fn get_table(&self, name: &str) -> Result<&Table, ExportError> { + self.get(name) + } + + /// Get an export as a `Func`. + pub fn get_function(&self, name: &str) -> Result<&Function, ExportError> { + self.get(name) + } + + /// Get an export as a `TypedFunction`. + pub fn get_typed_function( + &self, + store: &impl AsStoreRef, + name: &str, + ) -> Result, ExportError> + where + Args: WasmTypeList, + Rets: WasmTypeList, + { + self.get_function(name)? + .typed(store) + .map_err(|_| ExportError::IncompatibleType) + } + + /// Hack to get this working with nativefunc too + pub fn get_with_generics<'a, T, Args, Rets>(&'a self, name: &str) -> Result + where + Args: WasmTypeList, + Rets: WasmTypeList, + T: ExportableWithGenerics<'a, Args, Rets>, + { + match self.map.get(name) { + None => Err(ExportError::Missing(name.to_string())), + Some(extern_) => T::get_self_from_extern_with_generics(extern_), + } + } + + /// Get an export as an `Extern`. + pub fn get_extern(&self, name: &str) -> Option<&Extern> { + self.map.get(name) + } + + /// Returns true if the `Exports` contains the given export name. + pub fn contains(&self, name: S) -> bool + where + S: Into, + { + self.map.contains_key(&name.into()) + } + + /// Get an iterator over the exports. + pub fn iter(&self) -> ExportsIterator> { + ExportsIterator { + iter: self.map.iter(), + } + } +} + +impl fmt::Debug for Exports { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_set().entries(self.iter()).finish() + } +} + +/// An iterator over exports. +pub struct ExportsIterator<'a, I> +where + I: Iterator + Sized, +{ + iter: I, +} + +impl<'a, I> Iterator for ExportsIterator<'a, I> +where + I: Iterator + Sized, +{ + type Item = (&'a String, &'a Extern); + + fn next(&mut self) -> Option { + self.iter.next() + } +} + +impl<'a, I> ExactSizeIterator for ExportsIterator<'a, I> +where + I: Iterator + ExactSizeIterator + Sized, +{ + fn len(&self) -> usize { + self.iter.len() + } +} + +impl<'a, I> ExportsIterator<'a, I> +where + I: Iterator + Sized, +{ + /// Get only the functions. + pub fn functions(self) -> impl Iterator + Sized { + self.iter.filter_map(|(name, export)| match export { + Extern::Function(function) => Some((name, function)), + _ => None, + }) + } + + /// Get only the memories. + pub fn memories(self) -> impl Iterator + Sized { + self.iter.filter_map(|(name, export)| match export { + Extern::Memory(memory) => Some((name, memory)), + _ => None, + }) + } + + /// Get only the globals. + pub fn globals(self) -> impl Iterator + Sized { + self.iter.filter_map(|(name, export)| match export { + Extern::Global(global) => Some((name, global)), + _ => None, + }) + } + + /// Get only the tables. + pub fn tables(self) -> impl Iterator + Sized { + self.iter.filter_map(|(name, export)| match export { + Extern::Table(table) => Some((name, table)), + _ => None, + }) + } +} + +impl FromIterator<(String, Extern)> for Exports { + fn from_iter>(iter: I) -> Self { + Self { + map: IndexMap::from_iter(iter), + } + } +} + +impl IntoIterator for Exports { + type IntoIter = indexmap::map::IntoIter; + type Item = (String, Extern); + + fn into_iter(self) -> Self::IntoIter { + self.map.into_iter() + } +} + +impl<'a> IntoIterator for &'a Exports { + type IntoIter = indexmap::map::Iter<'a, String, Extern>; + type Item = (&'a String, &'a Extern); + + fn into_iter(self) -> Self::IntoIter { + self.map.iter() + } +} + +/// This trait is used to mark types as gettable from an [`Instance`]. +/// +/// [`Instance`]: crate::Instance +pub trait Exportable<'a>: Sized { + /// Implementation of how to get the export corresponding to the implementing type + /// from an [`Instance`] by name. + /// + /// [`Instance`]: crate::Instance + fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError>; +} + +/// A trait for accessing exports (like [`Exportable`]) but it takes generic +/// `Args` and `Rets` parameters so that `TypedFunction` can be accessed directly +/// as well. +pub trait ExportableWithGenerics<'a, Args: WasmTypeList, Rets: WasmTypeList>: Sized { + /// Get an export with the given generics. + fn get_self_from_extern_with_generics(_extern: &'a Extern) -> Result; +} + +/// We implement it for all concrete [`Exportable`] types (that are `Clone`) +/// with empty `Args` and `Rets`. +impl<'a, T: Exportable<'a> + Clone + 'static> ExportableWithGenerics<'a, (), ()> for T { + fn get_self_from_extern_with_generics(_extern: &'a Extern) -> Result { + T::get_self_from_extern(_extern).map(|i| i.clone()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/extern_ref.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/extern_ref.rs new file mode 100644 index 0000000..038c7e4 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/extern_ref.rs @@ -0,0 +1,59 @@ +use std::any::Any; + +use crate::store::{AsStoreMut, AsStoreRef}; + +#[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::vm::VMExternRef; + +#[derive(Debug, Clone)] +#[repr(transparent)] +/// An opaque reference to some data. This reference can be passed through Wasm. +pub struct ExternRef(pub(crate) extern_ref_imp::ExternRef); + +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(extern_ref_imp::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, + { + self.0.downcast(store) + } + + pub(crate) fn vm_externref(&self) -> VMExternRef { + self.0.vm_externref() + } + + 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, + )) + } + + /// 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.0.is_from_store(store) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/function.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/function.rs new file mode 100644 index 0000000..70d18c3 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/function.rs @@ -0,0 +1,468 @@ +#[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; + +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; + +/// 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; + + /// Get the pointer to the function call trampoline. + fn call_trampoline_address() -> VMTrampoline { + // This is not implemented in JS + unimplemented!(); + } +} + +/// 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 {} +} + +/// 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) +#[derive(Debug, Clone, PartialEq)] +pub struct Function(pub(crate) function_impl::Function); + +impl Function { + /// 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, + { + Self(function_impl::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, + { + Self(function_impl::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, + { + Self(function_impl::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 { + self.0.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).results().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> { + self.0.call(store, params) + } + + #[doc(hidden)] + #[allow(missing_docs)] + pub fn call_raw( + &self, + store: &mut impl AsStoreMut, + params: Vec, + ) -> Result, RuntimeError> { + self.0.call_raw(store, params) + } + + pub(crate) fn vm_funcref(&self, store: &impl AsStoreRef) -> VMFuncRef { + self.0.vm_funcref(store) + } + + pub(crate) unsafe fn from_vm_funcref(store: &mut impl AsStoreMut, funcref: VMFuncRef) -> Self { + Self(function_impl::Function::from_vm_funcref(store, funcref)) + } + + /// 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, self.clone())) + } + + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternFunction) -> Self { + Self(function_impl::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 { + self.0.is_from_store(store) + } + + pub(crate) fn to_vm_extern(&self) -> VMExtern { + self.0.to_vm_extern() + } +} + +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 { + Extern::Function(func) => Ok(func), + _ => Err(ExportError::IncompatibleType), + } + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/global.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/global.rs new file mode 100644 index 0000000..3420562 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/global.rs @@ -0,0 +1,175 @@ +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; + +#[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; + +/// 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: +#[derive(Debug, Clone, PartialEq)] +pub struct Global(pub(crate) global_impl::Global); + +impl Global { + /// Create a new `Global` with the initial value [`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 [`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 [`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, + )?)) + } + + /// 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 { + self.0.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 { + self.0.get(store) + } + + /// Sets a custom value [`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> { + self.0.set(store, val) + } + + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternGlobal) -> Self { + Self(global_impl::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 { + self.0.is_from_store(store) + } + + 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 { + Extern::Global(global) => Ok(global), + _ => Err(ExportError::IncompatibleType), + } + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/memory.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/memory.rs new file mode 100644 index 0000000..3e2b07e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/memory.rs @@ -0,0 +1,363 @@ +#[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)] +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/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/memory_view.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/memory_view.rs new file mode 100644 index 0000000..1fbda5c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/memory_view.rs @@ -0,0 +1,183 @@ +use super::memory::{Memory, MemoryBuffer}; +use crate::store::AsStoreRef; +use crate::MemoryAccessError; +use std::mem::MaybeUninit; +use std::ops::Range; +use wasmer_types::Pages; + +#[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; + +/// 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)] +pub struct MemoryView<'a>(pub(crate) memory_view_impl::MemoryView<'a>); + +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)) + } + + /// Returns the pointer to the raw bytes of the `Memory`. + // + // This used by wasmer-emscripten and 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> { + MemoryBuffer(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.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/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/mod.rs new file mode 100644 index 0000000..c5b46e8 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/mod.rs @@ -0,0 +1,128 @@ +pub(crate) mod function; +mod global; +pub(crate) mod memory; +mod memory_view; +mod table; + +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 = "js")] +use crate::js::vm::VMExtern; +#[cfg(feature = "jsc")] +use crate::jsc::vm::VMExtern; +#[cfg(feature = "sys")] +use wasmer_vm::VMExtern; + +use crate::store::{AsStoreMut, AsStoreRef}; + +/// An `Extern` is the runtime representation of an entity that +/// can be imported or exported. +/// +/// Spec: +#[derive(Clone, PartialEq, Eq)] +pub enum Extern { + /// A external [`Function`]. + Function(Function), + /// A external [`Global`]. + Global(Global), + /// A external [`Table`]. + Table(Table), + /// A external [`Memory`]. + Memory(Memory), +} + +impl Extern { + /// Return the underlying type of the inner `Extern`. + pub fn ty(&self, store: &impl AsStoreRef) -> ExternType { + match self { + Self::Function(ft) => ExternType::Function(ft.ty(store)), + Self::Memory(ft) => ExternType::Memory(ft.ty(store)), + Self::Table(tt) => ExternType::Table(tt.ty(store)), + Self::Global(gt) => ExternType::Global(gt.ty(store)), + } + } + + /// Create an `Extern` from an `wasmer_engine::Export`. + pub fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExtern) -> Self { + match vm_extern { + VMExtern::Function(f) => Self::Function(Function::from_vm_extern(store, f)), + VMExtern::Memory(m) => Self::Memory(Memory::from_vm_extern(store, m)), + VMExtern::Global(g) => Self::Global(Global::from_vm_extern(store, g)), + VMExtern::Table(t) => Self::Table(Table::from_vm_extern(store, t)), + } + } + + /// Checks whether this `Extern` can be used with the given context. + pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + match self { + Self::Function(f) => f.is_from_store(store), + Self::Global(g) => g.is_from_store(store), + Self::Memory(m) => m.is_from_store(store), + Self::Table(t) => t.is_from_store(store), + } + } + + /// To `VMExtern`. + pub fn to_vm_extern(&self) -> VMExtern { + match self { + Self::Function(f) => f.to_vm_extern(), + Self::Global(g) => g.to_vm_extern(), + Self::Memory(m) => m.to_vm_extern(), + Self::Table(t) => t.to_vm_extern(), + } + } +} + +impl<'a> Exportable<'a> for Extern { + fn get_self_from_extern(_extern: &'a Self) -> Result<&'a Self, ExportError> { + // Since this is already an extern, we can just return it. + Ok(_extern) + } +} + +impl fmt::Debug for Extern { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::Function(_) => "Function(...)", + Self::Global(_) => "Global(...)", + Self::Memory(_) => "Memory(...)", + Self::Table(_) => "Table(...)", + } + ) + } +} + +impl From for Extern { + fn from(r: Function) -> Self { + Self::Function(r) + } +} + +impl From for Extern { + fn from(r: Global) -> Self { + Self::Global(r) + } +} + +impl From for Extern { + fn from(r: Memory) -> Self { + Self::Memory(r) + } +} + +impl From

for Extern { + fn from(r: Table) -> Self { + Self::Table(r) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/table.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/table.rs new file mode 100644 index 0000000..49eb389 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/externals/table.rs @@ -0,0 +1,145 @@ +#[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; + +/// 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: +#[derive(Debug, Clone, PartialEq)] +pub struct Table(pub(crate) table_impl::Table); + +impl Table { + /// 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 { + Ok(Self(table_impl::Table::new(store, ty, init)?)) + } + + /// Returns the [`TableType`] of the `Table`. + pub fn ty(&self, store: &impl AsStoreRef) -> TableType { + self.0.ty(store) + } + + /// Retrieves an element of the table at the provided `index`. + pub fn get(&self, store: &mut impl AsStoreMut, index: u32) -> Option { + self.0.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> { + self.0.set(store, index, val) + } + + /// Retrieves the size of the `Table` (in elements) + pub fn size(&self, store: &impl AsStoreRef) -> u32 { + self.0.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 { + self.0.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> { + table_impl::Table::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_)) + } + + /// Checks whether this `Table` can be used with the given context. + pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + self.0.is_from_store(store) + } + + pub(crate) fn to_vm_extern(&self) -> VMExtern { + self.0.to_vm_extern() + } +} + +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 { + Extern::Table(table) => Ok(table), + _ => Err(ExportError::IncompatibleType), + } + } +} + +/// Check the example from . +#[test] +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(); +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/function_env.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/function_env.rs new file mode 100644 index 0000000..89c1de7 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/function_env.rs @@ -0,0 +1,168 @@ +use std::{any::Any, fmt::Debug, marker::PhantomData}; + +use crate::vm::VMFunctionEnvironment; + +use crate::store::{AsStoreMut, AsStoreRef, StoreHandle, StoreMut, StoreObjects, StoreRef}; + +#[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(), + 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_ref() + .downcast_ref::() + .unwrap() + } + + #[allow(dead_code)] // This function is only used in js + 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_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, + } + } + #[inline] + fn objects_mut(&mut self) -> &mut StoreObjects { + &mut self.store_mut.inner.objects + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/imports.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/imports.rs new file mode 100644 index 0000000..344bc24 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/imports.rs @@ -0,0 +1,476 @@ +//! 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 std::collections::HashMap; +use std::fmt; +use wasmer_types::ImportError; + +/// All of the import data used when instantiating. +/// +/// It's suggested that you use the [`imports!`] macro +/// instead of creating an `Imports` by hand. +/// +/// [`imports!`]: macro.imports.html +/// +/// # Usage: +/// ```no_run +/// use wasmer::{Store, Exports, Module, Instance, imports, Imports, Function, FunctionEnvMut}; +/// # fn foo_test(mut store: &mut Store, module: Module) { +/// +/// let host_fn = Function::new_typed(&mut store, foo); +/// let import_object: Imports = imports! { +/// "env" => { +/// "foo" => host_fn, +/// }, +/// }; +/// +/// let instance = Instance::new(&mut store, &module, &import_object).expect("Could not instantiate module."); +/// +/// fn foo(n: i32) -> i32 { +/// n +/// } +/// +/// # } +/// ``` +#[derive(Clone, Default)] +pub struct Imports { + pub(crate) map: HashMap<(String, String), Extern>, +} + +impl Imports { + /// Create a new `Imports`. + pub fn new() -> Self { + Default::default() + } + + /// Gets an export given a module and a name + /// + /// # Usage + /// ```no_run + /// # use wasmer::Imports; + /// let mut import_object = Imports::new(); + /// import_object.get_export("module", "name"); + /// ``` + pub fn get_export(&self, module: &str, name: &str) -> Option { + if self.exists(module, name) { + let ext = &self.map[&(module.to_string(), name.to_string())]; + return Some(ext.clone()); + } + None + } + + /// Returns if an export exist for a given module and name. + /// + /// # Usage + /// ```no_run + /// # use wasmer::Imports; + /// let mut import_object = Imports::new(); + /// import_object.exists("module", "name"); + /// ``` + pub fn exists(&self, module: &str, name: &str) -> bool { + self.map + .contains_key(&(module.to_string(), name.to_string())) + } + + /// Returns true if the Imports contains namespace with the provided name. + pub fn contains_namespace(&self, name: &str) -> bool { + self.map.keys().any(|(k, _)| (k == name)) + } + + /// Register a list of externs into a namespace. + /// + /// # Usage: + /// ```no_run + /// # use wasmer::{Imports, Exports, Memory}; + /// # fn foo_test(memory: Memory) { + /// let mut exports = Exports::new(); + /// exports.insert("memory", memory); + /// + /// let mut import_object = Imports::new(); + /// import_object.register_namespace("env", exports); + /// // ... + /// # } + /// ``` + pub fn register_namespace( + &mut self, + ns: &str, + contents: impl IntoIterator, + ) { + for (name, extern_) in contents.into_iter() { + self.map.insert((ns.to_string(), name.clone()), extern_); + } + } + + /// Add a single import with a namespace `ns` and name `name`. + /// + /// # Usage + /// ```no_run + /// # use wasmer::{FunctionEnv, Store}; + /// # let mut store: Store = Default::default(); + /// use wasmer::{StoreMut, Imports, Function, FunctionEnvMut}; + /// fn foo(n: i32) -> i32 { + /// n + /// } + /// let mut import_object = Imports::new(); + /// import_object.define("env", "foo", Function::new_typed(&mut store, foo)); + /// ``` + pub fn define(&mut self, ns: &str, name: &str, val: impl Into) { + self.map + .insert((ns.to_string(), name.to_string()), val.into()); + } + + /// Returns the contents of a namespace as an `Exports`. + /// + /// Returns `None` if the namespace doesn't exist. + pub fn get_namespace_exports(&self, name: &str) -> Option { + let ret: Exports = self + .map + .iter() + .filter(|((ns, _), _)| ns == name) + .map(|((_, name), e)| (name.clone(), e.clone())) + .collect(); + if ret.is_empty() { + None + } else { + Some(ret) + } + } + + /// Resolve and return a vector of imports in the order they are defined in the `module`'s source code. + /// + /// This means the returned `Vec` might be a subset of the imports contained in `self`. + #[allow(clippy::result_large_err)] + pub fn imports_for_module(&self, module: &Module) -> Result, LinkError> { + let mut ret = vec![]; + for import in module.imports() { + if let Some(imp) = self + .map + .get(&(import.module().to_string(), import.name().to_string())) + { + ret.push(imp.clone()); + } else { + return Err(LinkError::Import( + import.module().to_string(), + import.name().to_string(), + ImportError::UnknownImport(import.ty().clone()), + )); + } + } + Ok(ret) + } + + /// Iterates through all the imports in this structure + pub fn iter(&self) -> ImportsIterator<'_> { + ImportsIterator::new(self) + } +} + +pub struct ImportsIterator<'a> { + iter: std::collections::hash_map::Iter<'a, (String, String), Extern>, +} + +impl<'a> ImportsIterator<'a> { + fn new(imports: &'a Imports) -> Self { + let iter = imports.map.iter(); + Self { iter } + } +} + +impl<'a> Iterator for ImportsIterator<'a> { + type Item = (&'a str, &'a str, &'a Extern); + + fn next(&mut self) -> Option { + self.iter + .next() + .map(|(k, v)| (k.0.as_str(), k.1.as_str(), v)) + } +} + +impl IntoIterator for &Imports { + type IntoIter = std::collections::hash_map::IntoIter<(String, String), Extern>; + type Item = ((String, String), Extern); + + fn into_iter(self) -> Self::IntoIter { + self.map.clone().into_iter() + } +} + +impl Extend<((String, String), Extern)> for Imports { + fn extend>(&mut self, iter: T) { + for ((ns, name), ext) in iter.into_iter() { + self.define(&ns, &name, ext); + } + } +} + +impl fmt::Debug for Imports { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + enum SecretMap { + Empty, + Some(usize), + } + + impl SecretMap { + fn new(len: usize) -> Self { + if len == 0 { + Self::Empty + } else { + Self::Some(len) + } + } + } + + impl fmt::Debug for SecretMap { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Empty => write!(f, "(empty)"), + Self::Some(len) => write!(f, "(... {} item(s) ...)", len), + } + } + } + + f.debug_struct("Imports") + .field("map", &SecretMap::new(self.map.len())) + .finish() + } +} + +// The import! macro for Imports + +/// Generate an [`Imports`] easily with the `imports!` macro. +/// +/// [`Imports`]: struct.Imports.html +/// +/// # Usage +/// +/// ``` +/// # use wasmer::{StoreMut, Function, FunctionEnvMut, Store}; +/// # let mut store = Store::default(); +/// use wasmer::imports; +/// +/// let import_object = imports! { +/// "env" => { +/// "foo" => Function::new_typed(&mut store, foo) +/// }, +/// }; +/// +/// fn foo(n: i32) -> i32 { +/// n +/// } +/// ``` +#[macro_export] +macro_rules! imports { + ( $( $ns_name:expr => $ns:tt ),* $(,)? ) => { + { + #[allow(unused_mut)] + let mut import_object = $crate::Imports::new(); + + $({ + let namespace = $crate::import_namespace!($ns); + + import_object.register_namespace($ns_name, namespace); + })* + + import_object + } + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! namespace { + ($( $import_name:expr => $import_item:expr ),* $(,)? ) => { + $crate::import_namespace!( { $( $import_name => $import_item, )* } ) + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! import_namespace { + ( { $( $import_name:expr => $import_item:expr ),* $(,)? } ) => {{ + let mut namespace = $crate::Exports::new(); + + $( + namespace.insert($import_name, $import_item); + )* + + namespace + }}; + + ( $namespace:ident ) => { + $namespace + }; +} + +#[cfg(test)] +mod test { + use crate::store::Store; + use crate::value::Value; + use crate::Extern; + use crate::Global; + use wasmer_types::Type; + + #[test] + fn namespace() { + let mut store = Store::default(); + let g1 = Global::new(&mut store, Value::I32(0)); + let namespace = namespace! { + "happy" => g1 + }; + let imports1 = imports! { + "dog" => namespace + }; + + let happy_dog_entry = imports1.get_export("dog", "happy").unwrap(); + + assert!(if let Extern::Global(happy_dog_global) = happy_dog_entry { + happy_dog_global.get(&mut store).ty() == Type::I32 + } else { + false + }); + } + + #[test] + fn imports_macro_allows_trailing_comma_and_none() { + use crate::Function; + + let mut store: Store = Default::default(); + + fn func(arg: i32) -> i32 { + arg + 1 + } + + let _ = imports! { + "env" => { + "func" => Function::new_typed(&mut store, func), + }, + }; + let _ = imports! { + "env" => { + "func" => Function::new_typed(&mut store, func), + } + }; + let _ = imports! { + "env" => { + "func" => Function::new_typed(&mut store, func), + }, + "abc" => { + "def" => Function::new_typed(&mut store, func), + } + }; + let _ = imports! { + "env" => { + "func" => Function::new_typed(&mut store, func) + }, + }; + let _ = imports! { + "env" => { + "func" => Function::new_typed(&mut store, func) + } + }; + let _ = imports! { + "env" => { + "func1" => Function::new_typed(&mut store, func), + "func2" => Function::new_typed(&mut store, func) + } + }; + let _ = imports! { + "env" => { + "func1" => Function::new_typed(&mut store, func), + "func2" => Function::new_typed(&mut store, func), + } + }; + } + + #[test] + fn chaining_works() { + let mut store = Store::default(); + + let g = Global::new(&mut store, Value::I32(0)); + + let mut imports1 = imports! { + "dog" => { + "happy" => g.clone() + } + }; + + let imports2 = imports! { + "dog" => { + "small" => g.clone() + }, + "cat" => { + "small" => g + } + }; + + imports1.extend(&imports2); + + let small_cat_export = imports1.get_export("cat", "small"); + assert!(small_cat_export.is_some()); + + let happy = imports1.get_export("dog", "happy"); + let small = imports1.get_export("dog", "small"); + assert!(happy.is_some()); + assert!(small.is_some()); + } + + #[test] + fn extending_conflict_overwrites() { + let mut store = Store::default(); + let g1 = Global::new(&mut store, Value::I32(0)); + let g2 = Global::new(&mut store, Value::I64(0)); + + let mut imports1 = imports! { + "dog" => { + "happy" => g1, + }, + }; + + let imports2 = imports! { + "dog" => { + "happy" => g2, + }, + }; + + imports1.extend(&imports2); + let _happy_dog_entry = imports1.get_export("dog", "happy").unwrap(); + /* + assert!( + if let Exports::Global(happy_dog_global) = happy_dog_entry.to_vm_extern() { + happy_dog_global.from.ty().ty == Type::I64 + } else { + false + } + ); + */ + // now test it in reverse + let mut store = Store::default(); + let g1 = Global::new(&mut store, Value::I32(0)); + let g2 = Global::new(&mut store, Value::I64(0)); + + let imports1 = imports! { + "dog" => { + "happy" => g1, + }, + }; + + let mut imports2 = imports! { + "dog" => { + "happy" => g2, + }, + }; + + imports2.extend(&imports1); + let _happy_dog_entry = imports2.get_export("dog", "happy").unwrap(); + /* + assert!( + if let Exports::Global(happy_dog_global) = happy_dog_entry.to_vm_extern() { + happy_dog_global.from.ty().ty == Type::I32 + } else { + false + } + ); + */ + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/instance.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/instance.rs new file mode 100644 index 0000000..ff31210 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/instance.rs @@ -0,0 +1,113 @@ +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 = "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/arbitrator/tools/module_roots/wasmer/lib/api/src/into_bytes.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/into_bytes.rs new file mode 100644 index 0000000..2016036 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/into_bytes.rs @@ -0,0 +1,50 @@ +use bytes::Bytes; +use std::borrow::Cow; + +/// Convert binary data into [`bytes::Bytes`]. +pub trait IntoBytes { + /// Convert binary data into [`bytes::Bytes`]. + fn into_bytes(self) -> Bytes; +} + +impl IntoBytes for Bytes { + fn into_bytes(self) -> Bytes { + self + } +} + +impl IntoBytes for Vec { + fn into_bytes(self) -> Bytes { + Bytes::from(self) + } +} + +impl IntoBytes for &Vec { + fn into_bytes(self) -> Bytes { + Bytes::from(self.clone()) + } +} + +impl IntoBytes for &[u8] { + fn into_bytes(self) -> Bytes { + Bytes::from(self.to_vec()) + } +} + +impl IntoBytes for &[u8; N] { + fn into_bytes(self) -> Bytes { + Bytes::from(self.to_vec()) + } +} + +impl IntoBytes for &str { + fn into_bytes(self) -> Bytes { + Bytes::from(self.as_bytes().to_vec()) + } +} + +impl IntoBytes for Cow<'_, [u8]> { + fn into_bytes(self) -> Bytes { + Bytes::from(self.to_vec()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/js/as_js.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/as_js.rs new file mode 100644 index 0000000..5068c8b --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/as_js.rs @@ -0,0 +1,334 @@ +//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 wasmer_types::ExternType; + +/// Convert the given type to a [`JsValue`]. +pub trait AsJs: 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; + /// Convert the given type to a [`JsValue`]. + fn from_jsvalue( + store: &mut impl AsStoreMut, + type_: &Self::DefinitionType, + value: &JsValue, + ) -> Result; +} + +#[inline] +pub fn param_from_js(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() { + js_val.clone().try_into().unwrap() + } else { + js_val.as_f64().unwrap() as _ + }), + Type::F32 => Value::F32(js_val.as_f64().unwrap() as _), + Type::F64 => Value::F64(js_val.as_f64().unwrap()), + Type::V128 => { + let big_num: u128 = js_sys::BigInt::from(js_val.clone()).try_into().unwrap(); + Value::V128(big_num) + } + Type::ExternRef | Type::FuncRef => unimplemented!( + "The type `{:?}` is not yet supported in the JS Function API", + ty + ), + } +} + +impl AsJs for Value { + type DefinitionType = Type; + + fn as_jsvalue(&self, _store: &impl AsStoreRef) -> JsValue { + match self { + Self::I32(i) => JsValue::from(*i), + Self::I64(i) => JsValue::from(*i), + 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(None) => JsValue::null(), + Self::ExternRef(_) => unimplemented!(), + } + } + + fn from_jsvalue( + _store: &mut impl AsStoreMut, + type_: &Self::DefinitionType, + value: &JsValue, + ) -> Result { + Ok(param_from_js(type_, value)) + } +} + +impl AsJs for Imports { + type DefinitionType = crate::module::Module; + + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + fn as_jsvalue(&self, store: &impl AsStoreRef) -> wasm_bindgen::JsValue { + let imports_object = js_sys::Object::new(); + for (namespace, name, extern_) in self.iter() { + let val = unsafe { js_sys::Reflect::get(&imports_object, &namespace.into()).unwrap() }; + if !val.is_undefined() { + // If the namespace is already set + + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + unsafe { + js_sys::Reflect::set( + &val, + &name.into(), + &extern_.as_jsvalue(&store.as_store_ref()), + ) + .unwrap(); + } + } else { + // If the namespace doesn't exist + let import_namespace = js_sys::Object::new(); + #[allow(unused_unsafe)] + unsafe { + js_sys::Reflect::set( + &import_namespace, + &name.into(), + &extern_.as_jsvalue(&store.as_store_ref()), + ) + .unwrap(); + js_sys::Reflect::set( + &imports_object, + &namespace.into(), + &import_namespace.into(), + ) + .unwrap(); + } + } + } + imports_object.into() + } + + fn from_jsvalue( + store: &mut impl AsStoreMut, + module: &Self::DefinitionType, + value: &JsValue, + ) -> Result { + let module_imports: HashMap<(String, String), ExternType> = module + .imports() + .map(|import| { + ( + (import.module().to_string(), import.name().to_string()), + import.ty().clone(), + ) + }) + .collect::>(); + + let mut map: HashMap<(String, String), Extern> = HashMap::new(); + let object: js_sys::Object = value.clone().into(); + for module_entry in js_sys::Object::entries(&object).iter() { + let module_entry: js_sys::Array = module_entry.into(); + let module_name = module_entry.get(0).as_string().unwrap().to_string(); + let module_import_object: js_sys::Object = module_entry.get(1).into(); + for import_entry in js_sys::Object::entries(&module_import_object).iter() { + let import_entry: js_sys::Array = import_entry.into(); + let import_name = import_entry.get(0).as_string().unwrap().to_string(); + let import_js: wasm_bindgen::JsValue = import_entry.get(1); + let key = (module_name.clone(), import_name); + let extern_type = module_imports.get(&key).unwrap(); + let extern_ = Extern::from_jsvalue(store, extern_type, &import_js)?; + map.insert(key, extern_); + } + } + + Ok(Self { map }) + } +} + +impl AsJs for Extern { + type DefinitionType = ExternType; + + 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(), + } + } + + fn from_jsvalue( + store: &mut impl AsStoreMut, + extern_type: &Self::DefinitionType, + val: &JsValue, + ) -> Result { + // Note: this function do a soft check over the type + // We only check the "kind" of Extern, but nothing else + match extern_type { + ExternType::Memory(memory_type) => { + Ok(Self::Memory(Memory::from_jsvalue(store, memory_type, val)?)) + } + ExternType::Global(global_type) => { + Ok(Self::Global(Global::from_jsvalue(store, global_type, val)?)) + } + ExternType::Function(function_type) => Ok(Self::Function(Function::from_jsvalue( + store, + function_type, + val, + )?)), + ExternType::Table(table_type) => { + Ok(Self::Table(Table::from_jsvalue(store, table_type, val)?)) + } + } + } +} + +impl AsJs for Instance { + type DefinitionType = crate::module::Module; + fn as_jsvalue(&self, _store: &impl AsStoreRef) -> wasm_bindgen::JsValue { + self._inner._handle.clone().into() + } + + fn from_jsvalue( + store: &mut impl AsStoreMut, + module: &Self::DefinitionType, + value: &JsValue, + ) -> Result { + let js_instance: js_sys::WebAssembly::Instance = value.clone().into(); + 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, + module: module.clone(), + exports, + }) + } +} + +impl AsJs for Memory { + type DefinitionType = crate::MemoryType; + + fn as_jsvalue(&self, _store: &impl AsStoreRef) -> wasm_bindgen::JsValue { + self.0.handle.memory.clone().into() + } + + fn from_jsvalue( + store: &mut impl AsStoreMut, + memory_type: &Self::DefinitionType, + value: &JsValue, + ) -> Result { + if let Some(memory) = value.dyn_ref::() { + Ok(Memory::from_vm_extern( + store, + VMMemory::new(memory.clone(), memory_type.clone()), + )) + } else { + Err(JsError::new(&format!( + "Extern expect to be of type Memory, but received {:?}", + value + ))) + } + } +} + +impl AsJs for Function { + type DefinitionType = crate::FunctionType; + + fn as_jsvalue(&self, _store: &impl AsStoreRef) -> wasm_bindgen::JsValue { + self.0.handle.function.clone().into() + } + + fn from_jsvalue( + store: &mut impl AsStoreMut, + function_type: &Self::DefinitionType, + value: &JsValue, + ) -> Result { + if value.is_instance_of::() { + Ok(Function::from_vm_extern( + store, + VMFunction::new( + value.clone().unchecked_into::(), + function_type.clone(), + ), + )) + } else { + Err(JsError::new(&format!( + "Extern expect to be of type Function, but received {:?}", + value + ))) + } + } +} + +impl AsJs for Global { + type DefinitionType = crate::GlobalType; + + fn as_jsvalue(&self, _store: &impl AsStoreRef) -> wasm_bindgen::JsValue { + self.0.handle.global.clone().into() + } + + fn from_jsvalue( + store: &mut impl AsStoreMut, + global_type: &Self::DefinitionType, + value: &JsValue, + ) -> Result { + if value.is_instance_of::() { + Ok(Global::from_vm_extern( + store, + VMGlobal::new( + value.clone().unchecked_into::(), + global_type.clone(), + ), + )) + } else { + Err(JsError::new(&format!( + "Extern expect to be of type Global, but received {:?}", + value + ))) + } + } +} + +impl AsJs for Table { + type DefinitionType = crate::TableType; + + fn as_jsvalue(&self, _store: &impl AsStoreRef) -> wasm_bindgen::JsValue { + self.0.handle.table.clone().into() + } + + fn from_jsvalue( + store: &mut impl AsStoreMut, + table_type: &Self::DefinitionType, + value: &JsValue, + ) -> Result { + if value.is_instance_of::() { + Ok(Table::from_vm_extern( + store, + VMTable::new( + value.clone().unchecked_into::(), + table_type.clone(), + ), + )) + } else { + Err(JsError::new(&format!( + "Extern expect to be of type Table, but received {:?}", + value + ))) + } + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/js/engine.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/engine.rs new file mode 100644 index 0000000..b56d104 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/engine.rs @@ -0,0 +1,21 @@ +/// 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/arbitrator/tools/module_roots/wasmer/lib/api/src/js/errors.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/errors.rs new file mode 100644 index 0000000..b4f6e02 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/errors.rs @@ -0,0 +1,28 @@ +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/arbitrator/tools/module_roots/wasmer/lib/api/src/js/extern_ref.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/extern_ref.rs new file mode 100644 index 0000000..fc0771e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/extern_ref.rs @@ -0,0 +1,39 @@ +use std::any::Any; + +use crate::js::vm::VMExternRef; +use crate::store::{AsStoreMut, AsStoreRef}; + +#[derive(Debug, Clone)] +#[repr(transparent)] +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 in Javascript"); + } + + 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 Javascript"); + } + + pub(crate) fn vm_externref(&self) -> VMExternRef { + unimplemented!("ExternRef is not yet supported in Javascript"); + } + + pub(crate) unsafe fn from_vm_externref( + _store: &mut impl AsStoreMut, + _vm_externref: VMExternRef, + ) -> Self { + unimplemented!("ExternRef is not yet supported in Javascript"); + } + + pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/function.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/function.rs new file mode 100644 index 0000000..1d588ff --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/function.rs @@ -0,0 +1,487 @@ +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; +use std::marker::PhantomData; +use std::panic::{self, AssertUnwindSafe}; + +use wasmer_types::{FunctionType, NativeWasmType, RawValue}; + +use js_sys::{Array, Function as JSFunction}; +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; + +#[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 + ), + } +} + +#[inline] +fn results_to_js_array(values: &[Value]) -> Array { + Array::from_iter(values.iter().map(result_to_js)) +} + +#[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 mut store = store.as_store_mut(); + let function_type = ty.into(); + let func_ty = function_type.clone(); + let raw_store = store.as_raw() as *mut u8; + let raw_env = env.clone(); + let wrapped_func: JsValue = match function_type.results().len() { + 0 => Closure::wrap(Box::new(move |args: &Array| { + let mut store: StoreMut = unsafe { StoreMut::from_raw(raw_store as _) }; + let env: FunctionEnvMut = raw_env.clone().into_mut(&mut store); + let wasm_arguments = function_type + .params() + .iter() + .enumerate() + .map(|(i, param)| param_from_js(param, &args.get(i as u32))) + .collect::>(); + let _results = func(env, &wasm_arguments)?; + Ok(()) + }) + as Box Result<(), JsValue>>) + .into_js_value(), + 1 => Closure::wrap(Box::new(move |args: &Array| { + let mut store: StoreMut = unsafe { StoreMut::from_raw(raw_store as _) }; + let env: FunctionEnvMut = raw_env.clone().into_mut(&mut store); + let wasm_arguments = function_type + .params() + .iter() + .enumerate() + .map(|(i, param)| param_from_js(param, &args.get(i as u32))) + .collect::>(); + let results = func(env, &wasm_arguments)?; + return Ok(result_to_js(&results[0])); + }) + as Box Result>) + .into_js_value(), + _n => Closure::wrap(Box::new(move |args: &Array| { + let mut store: StoreMut = unsafe { StoreMut::from_raw(raw_store as _) }; + let env: FunctionEnvMut = raw_env.clone().into_mut(&mut store); + let wasm_arguments = function_type + .params() + .iter() + .enumerate() + .map(|(i, param)| param_from_js(param, &args.get(i as u32))) + .collect::>(); + let results = func(env, &wasm_arguments)?; + return Ok(results_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))"); + 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) + } + + /// 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(); + if std::mem::size_of::() != 0 { + Self::closures_unsupported_panic(); + } + let function = WasmFunction::::new(func); + let address = function.address() as usize as u32; + + let ft = wasm_bindgen::function_table(); + let as_table = ft.unchecked_ref::(); + let func = as_table.get(address).unwrap(); + + let binded_func = func.bind1( + &JsValue::UNDEFINED, + &JsValue::from_f64(store.as_raw() as *mut u8 as usize as f64), + ); + let ty = function.ty(); + let vm_function = VMFunction::new(binded_func, 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(); + if std::mem::size_of::() != 0 { + Self::closures_unsupported_panic(); + } + let function = WasmFunction::::new(func); + let address = function.address() as usize as u32; + + let ft = wasm_bindgen::function_table(); + let as_table = ft.unchecked_ref::(); + let func = as_table.get(address).unwrap(); + + 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), + ); + let ty = function.ty(); + let vm_function = VMFunction::new(binded_func, 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 JS, so we just + // simply rely the call + // self.call(store, params) + unimplemented!(); + } + + pub fn call( + &self, + store: &mut impl AsStoreMut, + params: &[Value], + ) -> Result, RuntimeError> { + // Annotation is here to prevent spurious IDE warnings. + let arr = js_sys::Array::new_with_length(params.len() as u32); + + // let raw_env = env.as_raw() as *mut u8; + // let mut env = unsafe { FunctionEnvMut::from_raw(raw_env as *mut StoreInner<()>) }; + + for (i, param) in params.iter().enumerate() { + let js_value = param.as_jsvalue(&store.as_store_ref()); + arr.set(i as u32, js_value); + } + + 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 { + r = js_sys::Reflect::apply( + &self.handle.function, + &wasm_bindgen::JsValue::NULL, + &arr, + ); + let store_mut = store.as_store_mut(); + 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(); + match result_types.len() { + 0 => Ok(Box::new([])), + 1 => { + let value = param_from_js(&result_types[0], &result); + Ok(vec![value].into_boxed_slice()) + } + _n => { + let result_array: Array = result.into(); + Ok(result_array + .iter() + .enumerate() + .map(|(i, js_val)| param_from_js(&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!(); + } + + #[track_caller] + fn closures_unsupported_panic() -> ! { + unimplemented!("Closures (functions with captured environments) are currently unsupported with native functions. See: https://github.com/wasmerio/wasmer/issues/1840") + } + + /// 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 { + address: VMFunctionCallback, + _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 { + address: 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 address(&self) -> VMFunctionCallback { + self.address + } +} + +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. + 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 + } + } + + // 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. + 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 + } + } + }; + } + +// 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/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/global.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/global.rs new file mode 100644 index 0000000..d9be955 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/global.rs @@ -0,0 +1,139 @@ +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}; + +#[derive(Debug, Clone, PartialEq)] +pub struct Global { + pub(crate) handle: VMGlobal, +} + +// 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 { + VMExtern::Global(self.handle.clone()) + } + + /// 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 { + if !val.is_from_store(store) { + return Err(RuntimeError::new( + "cross-`WasmerEnv` values are not supported", + )); + } + let global_ty = GlobalType { + mutability, + ty: val.ty(), + }; + let descriptor = js_sys::Object::new(); + let (type_str, value) = match val { + Value::I32(i) => ("i32", JsValue::from_f64(i as _)), + Value::I64(i) => ("i64", JsValue::from_f64(i as _)), + Value::F32(f) => ("f32", JsValue::from_f64(f as _)), + Value::F64(f) => ("f64", JsValue::from_f64(f)), + _ => unimplemented!("The type is not yet supported in the JS Global API"), + }; + // This is the value type as string, even though is incorrectly called "value" + // in the JS API. + js_sys::Reflect::set(&descriptor, &"value".into(), &type_str.into())?; + js_sys::Reflect::set( + &descriptor, + &"mutable".into(), + &mutability.is_mutable().into(), + )?; + + 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)) + } + + pub fn ty(&self, _store: &impl AsStoreRef) -> GlobalType { + self.handle.ty + } + + pub fn get(&self, store: &mut impl AsStoreMut) -> Value { + unsafe { + let value = self.handle.global.value(); + let ty = self.handle.ty; + let raw = match ty.ty { + Type::I32 => RawValue { + i32: value.as_f64().unwrap_or_default() as _, + }, + Type::I64 => RawValue { + i64: value.as_f64().unwrap_or_default() as _, + }, + Type::F32 => RawValue { + f32: value.as_f64().unwrap_or_default() as _, + }, + Type::F64 => RawValue { + f64: value.as_f64().unwrap_or_default(), + }, + Type::V128 => RawValue { + u128: value.as_f64().unwrap_or_default() as _, + }, + Type::FuncRef => { + unimplemented!(); + // Self::FuncRef(VMFuncRef::from_raw(raw).map(|f| Function::from_vm_funcref(store, f))) + } + Type::ExternRef => { + unimplemented!(); + // Self::ExternRef( + // VMExternRef::from_raw(raw).map(|e| ExternRef::from_vm_externref(store, e)), + // ) + } + }; + Value::from_raw(store, ty.ty, raw) + } + } + + pub fn set(&self, store: &mut impl AsStoreMut, val: Value) -> Result<(), RuntimeError> { + if !val.is_from_store(store) { + return Err(RuntimeError::new( + "cross-`WasmerEnv` values are not supported", + )); + } + let global_ty = self.ty(&store); + if global_ty.mutability == Mutability::Const { + return Err(RuntimeError::new("The global is immutable".to_owned())); + } + if val.ty() != global_ty.ty { + return Err(RuntimeError::new("The types don't match".to_owned())); + } + let new_value = 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), + _ => { + return Err(RuntimeError::new( + "The type is not yet supported in the JS Global API".to_owned(), + )) + } + }; + self.handle.global.set_value(&new_value); + Ok(()) + } + + pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_global: VMGlobal) -> Self { + use crate::js::store::StoreObject; + VMGlobal::list_mut(store.objects_mut()).push(vm_global.clone()); + Self { handle: vm_global } + } + + pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/memory.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/memory.rs new file mode 100644 index 0000000..ebd8b3e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/memory.rs @@ -0,0 +1,281 @@ +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; + +use tracing::warn; + +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; +use wasmer_types::Pages; + +use super::memory_view::MemoryView; + +pub use wasmer_types::MemoryError; + +#[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; +} + +#[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 vm_memory = VMMemory::new(Self::js_memory_from_type(&ty)?, ty); + Ok(Self::from_vm_extern(store, vm_memory)) + } + + pub(crate) fn js_memory_from_type( + ty: &MemoryType, + ) -> Result { + let descriptor = js_sys::Object::new(); + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + unsafe { + js_sys::Reflect::set(&descriptor, &"initial".into(), &ty.minimum.0.into()).unwrap(); + if let Some(max) = ty.maximum { + js_sys::Reflect::set(&descriptor, &"maximum".into(), &max.0.into()).unwrap(); + } + js_sys::Reflect::set(&descriptor, &"shared".into(), &ty.shared.into()).unwrap(); + } + + let js_memory = js_sys::WebAssembly::Memory::new(&descriptor).map_err(|e| { + let error_message = if let Some(s) = e.as_string() { + s + } else if let Some(obj) = e.dyn_ref::() { + obj.to_string().into() + } else { + "Error while creating the memory".to_string() + }; + MemoryError::Generic(error_message) + })?; + + Ok(js_memory) + } + + pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { + Self::from_vm_extern(new_store, memory) + } + + pub(crate) fn to_vm_extern(&self) -> VMExtern { + VMExtern::Memory(self.handle.clone()) + } + + pub fn ty(&self, _store: &impl AsStoreRef) -> MemoryType { + self.handle.ty + } + + /// Creates a view into the memory that then allows for + /// read and write + 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 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( + &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(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, internal: VMMemory) -> Self { + Self { handle: internal } + } + + /// 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() + } + + pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } + + 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 From for crate::Memory { + fn from(value: Memory) -> Self { + crate::Memory(value) + } +} + +impl From for Memory { + fn from(value: crate::Memory) -> Self { + value.0 + } +} + +/// 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/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/memory_view.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/memory_view.rs new file mode 100644 index 0000000..f4830ad --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/memory_view.rs @@ -0,0 +1,285 @@ +use std::{convert::TryInto, 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, +}; + +/// 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> { + view: js_sys::Uint8Array, + size: u64, + marker: PhantomData<&'a Memory>, +} + +impl<'a> MemoryView<'a> { + pub(crate) fn new(memory: &Memory, _store: &'a (impl AsStoreRef + ?Sized)) -> Self { + Self::new_raw(&memory.handle.memory) + } + + pub(crate) fn new_raw(memory: &js_sys::WebAssembly::Memory) -> Self { + let buffer = memory.buffer(); + + // This also works for SharedArrayBuffer. + let size = buffer + .unchecked_ref::() + .byte_length() + .into(); + + let view = js_sys::Uint8Array::new(&buffer); + + Self { + view, + size, + marker: PhantomData, + } + } + + /// Returns the pointer to the raw bytes of the `Memory`. + #[doc(hidden)] + pub fn data_ptr(&self) -> *mut u8 { + unimplemented!("direct data pointer access is not possible in JavaScript"); + } + + /// Returns the size (in bytes) of the `Memory`. + pub fn data_size(&self) -> u64 { + self.size + } + + // TODO: do we want a proper implementation here instead? + /// 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] { + unimplemented!("direct data pointer access is not possible in JavaScript"); + } + + // TODO: do we want a proper implementation here instead? + /// 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] { + unimplemented!("direct data pointer access is not possible in JavaScript"); + } + + /// 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(&store, MemoryType::new(1, None, false)).unwrap(); + /// + /// assert_eq!(m.size(), Pages(1)); + /// ``` + pub fn size(&self) -> Pages { + Bytes(self.size as usize).try_into().unwrap() + } + + #[inline] + pub(crate) fn buffer(&self) -> MemoryBuffer<'a> { + MemoryBuffer { + base: &self.view as *const _ as *mut _, + marker: PhantomData, + } + } + + /// 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, data: &mut [u8]) -> Result<(), MemoryAccessError> { + let view = &self.view; + let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?; + let len: u32 = data + .len() + .try_into() + .map_err(|_| MemoryAccessError::Overflow)?; + let end = offset.checked_add(len).ok_or(MemoryAccessError::Overflow)?; + if end > view.length() { + tracing::warn!( + "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", + len, + end, + view.length() + ); + Err(MemoryAccessError::HeapOutOfBounds)?; + } + view.subarray(offset, end).copy_to(data); + Ok(()) + } + + /// 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 view = &self.view; + let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?; + if offset >= view.length() { + tracing::warn!( + "attempted to read beyond the bounds of the memory view ({} >= {})", + offset, + view.length() + ); + Err(MemoryAccessError::HeapOutOfBounds)?; + } + Ok(view.get_index(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> { + let view = &self.view; + let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?; + let len: u32 = buf + .len() + .try_into() + .map_err(|_| MemoryAccessError::Overflow)?; + let end = offset.checked_add(len).ok_or(MemoryAccessError::Overflow)?; + if end > view.length() { + tracing::warn!( + "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", + len, + end, + view.length() + ); + Err(MemoryAccessError::HeapOutOfBounds)?; + } + + // Zero-initialize the buffer to avoid undefined behavior with + // uninitialized data. + for elem in buf.iter_mut() { + *elem = MaybeUninit::new(0); + } + let buf = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, buf.len()) }; + + view.subarray(offset, end).copy_to(buf); + Ok(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> { + let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?; + let len: u32 = data + .len() + .try_into() + .map_err(|_| MemoryAccessError::Overflow)?; + let view = &self.view; + let end = offset.checked_add(len).ok_or(MemoryAccessError::Overflow)?; + if end > view.length() { + tracing::warn!( + "attempted to write ({} bytes) beyond the bounds of the memory view ({} > {})", + len, + end, + view.length() + ); + Err(MemoryAccessError::HeapOutOfBounds)?; + } + view.subarray(offset, end).copy_from(data); + Ok(()) + } + + /// 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 view = &self.view; + let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?; + if offset >= view.length() { + tracing::warn!( + "attempted to write beyond the bounds of the memory view ({} >= {})", + offset, + view.length() + ); + Err(MemoryAccessError::HeapOutOfBounds)?; + } + view.set_index(offset, val); + Ok(()) + } + + /// Copies the memory and returns it as a vector of bytes + #[allow(unused)] + 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 + #[allow(unused)] + 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/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/mod.rs new file mode 100644 index 0000000..3575fe2 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/mod.rs @@ -0,0 +1,5 @@ +pub(crate) mod function; +pub(crate) mod global; +pub(crate) mod memory; +pub(crate) mod memory_view; +pub(crate) mod table; diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/table.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/table.rs new file mode 100644 index 0000000..f7c2cdd --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/externals/table.rs @@ -0,0 +1,119 @@ +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 js_sys::Function; + +#[derive(Debug, Clone, PartialEq)] +pub struct Table { + pub(crate) handle: VMTable, +} + +// Table 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 Table {} + +fn set_table_item(table: &VMTable, item_index: u32, item: &Function) -> Result<(), RuntimeError> { + table.table.set(item_index, item).map_err(|e| e.into()) +} + +fn get_function(store: &mut impl AsStoreMut, val: Value) -> Result { + 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_inner()), + // Only funcrefs is supported by the spec atm + _ => unimplemented!("The {val:?} is not yet supported"), + } +} + +impl Table { + pub fn new( + store: &mut impl AsStoreMut, + ty: TableType, + init: Value, + ) -> Result { + let mut store = store; + let descriptor = js_sys::Object::new(); + js_sys::Reflect::set(&descriptor, &"initial".into(), &ty.minimum.into())?; + if let Some(max) = ty.maximum { + js_sys::Reflect::set(&descriptor, &"maximum".into(), &max.into())?; + } + js_sys::Reflect::set(&descriptor, &"element".into(), &"anyfunc".into())?; + + let js_table = js_sys::WebAssembly::Table::new(&descriptor)?; + let table = VMTable::new(js_table, ty); + + let num_elements = table.table.length(); + let func = get_function(&mut store, init)?; + for i in 0..num_elements { + set_table_item(&table, i, &func)?; + } + + Ok(Self { handle: table }) + } + + pub fn to_vm_extern(&self) -> VMExtern { + VMExtern::Table(self.handle.clone()) + } + + pub fn ty(&self, _store: &impl AsStoreRef) -> TableType { + self.handle.ty + } + + pub fn get(&self, store: &mut impl AsStoreMut, index: u32) -> Option { + 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( + &self, + store: &mut impl AsStoreMut, + index: u32, + val: Value, + ) -> Result<(), RuntimeError> { + let item = get_function(store, val)?; + set_table_item(&self.handle, index, &item) + } + + pub fn size(&self, _store: &impl AsStoreRef) -> u32 { + self.handle.table.length() + } + + pub fn grow( + &self, + _store: &mut impl AsStoreMut, + _delta: u32, + _init: Value, + ) -> Result { + unimplemented!(); + } + + pub fn copy( + _store: &mut impl AsStoreMut, + _dst_table: &Self, + _dst_index: u32, + _src_table: &Self, + _src_index: u32, + _len: u32, + ) -> Result<(), RuntimeError> { + 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 fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/js/instance.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/instance.rs new file mode 100644 index 0000000..4972641 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/instance.rs @@ -0,0 +1,76 @@ +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; + +#[derive(Clone, PartialEq, Eq)] +pub struct Instance { + pub(crate) _handle: JsHandle, +} + +// Instance 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 Instance {} + +impl Instance { + pub(crate) fn new( + mut store: &mut impl AsStoreMut, + module: &Module, + imports: &Imports, + ) -> Result<(Self, Exports), InstantiationError> { + let instance = module + .0 + .instantiate(&mut store, imports) + .map_err(|e| InstantiationError::Start(e))?; + + Self::from_module_and_instance(store, &module, instance) + } + + pub(crate) fn new_by_index( + store: &mut impl AsStoreMut, + module: &Module, + externs: &[Extern], + ) -> Result<(Self, Exports), InstantiationError> { + let mut imports = Imports::new(); + for (import_ty, extern_ty) in module.imports().zip(externs.iter()) { + imports.define(import_ty.module(), import_ty.name(), extern_ty.clone()); + } + Self::new(store, module, &imports) + } + + /// Creates a Wasmer `Instance` from a Wasmer `Module` and a WebAssembly Instance + pub(crate) fn from_module_and_instance( + mut store: &mut impl AsStoreMut, + module: &Module, + instance: WebAssembly::Instance, + ) -> Result<(Self, Exports), InstantiationError> { + let instance_exports = instance.exports(); + + let exports = module + .exports() + .map(|export_type| { + let name = export_type.name(); + let extern_type = export_type.ty(); + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + let js_export = + unsafe { js_sys::Reflect::get(&instance_exports, &name.into()).unwrap() }; + let extern_ = Extern::from_jsvalue(&mut store, extern_type, &js_export) + .map_err(|e| wasm_bindgen::JsValue::from(e)) + .unwrap(); + Ok((name.to_string(), extern_)) + }) + .collect::>()?; + + let instance = Instance { + _handle: JsHandle::new(instance), + }; + + Ok((instance, exports)) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/js/js_handle.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/js_handle.rs new file mode 100644 index 0000000..043cc1e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/js_handle.rs @@ -0,0 +1,221 @@ +use std::{ + ops::{Deref, DerefMut}, + sync::atomic::{AtomicU32, Ordering}, +}; + +use js_sys::Symbol; +use wasm_bindgen::JsValue; + +use self::integrity_check::IntegrityCheck; + +/// A handle that lets you detect thread-safety issues when passing a +/// [`JsValue`] (or derived type) around. +#[derive(Debug, Clone)] +pub(crate) struct JsHandle { + value: T, + integrity: IntegrityCheck, +} + +impl JsHandle { + #[track_caller] + pub fn new(value: T) -> Self { + JsHandle { + value, + integrity: IntegrityCheck::new(std::any::type_name::()), + } + } + + #[track_caller] + pub fn into_inner(self) -> T { + self.integrity.check(); + self.value + } +} + +impl PartialEq for JsHandle { + fn eq(&self, other: &Self) -> bool { + let JsHandle { + value, + integrity: _, + } = self; + + *value == other.value + } +} + +impl Eq for JsHandle {} + +impl From for JsHandle { + #[track_caller] + fn from(value: T) -> Self { + JsHandle::new(value) + } +} + +impl> From> for JsValue { + fn from(value: JsHandle) -> Self { + value.into_inner().into() + } +} + +impl AsRef for JsHandle +where + T: AsRef, +{ + #[track_caller] + fn as_ref(&self) -> &A { + self.integrity.check(); + self.value.as_ref() + } +} + +impl Deref for JsHandle { + type Target = T; + + #[track_caller] + fn deref(&self) -> &Self::Target { + self.integrity.check(); + &self.value + } +} + +impl DerefMut for JsHandle { + #[track_caller] + fn deref_mut(&mut self) -> &mut Self::Target { + self.integrity.check(); + &mut self.value + } +} + +#[cfg(not(debug_assertions))] +mod integrity_check { + + #[derive(Debug, Clone, PartialEq)] + pub(crate) struct IntegrityCheck; + + impl IntegrityCheck { + #[track_caller] + pub(crate) fn new(_type_name: &'static str) -> Self { + IntegrityCheck + } + + pub(crate) fn check(&self) {} + } +} + +#[cfg(debug_assertions)] +mod integrity_check { + use std::{fmt::Write as _, panic::Location}; + + use js_sys::JsString; + + #[derive(Debug, Clone, PartialEq)] + pub(crate) struct IntegrityCheck { + original_thread: u32, + created: &'static Location<'static>, + type_name: &'static str, + backtrace: Option, + } + + impl IntegrityCheck { + #[track_caller] + pub(crate) fn new(type_name: &'static str) -> Self { + IntegrityCheck { + original_thread: super::current_thread_id(), + created: Location::caller(), + type_name, + backtrace: record_backtrace(), + } + } + + #[track_caller] + pub(crate) fn check(&self) { + let current_thread = super::current_thread_id(); + + if current_thread != self.original_thread { + let IntegrityCheck { + original_thread, + created, + type_name, + backtrace, + } = self; + let mut error_message = String::new(); + + writeln!( + error_message, + "Thread-safety integrity check for {type_name} failed." + ) + .unwrap(); + + writeln!( + error_message, + "Created at {created} on thread #{original_thread}" + ) + .unwrap(); + + if let Some(bt) = backtrace { + writeln!(error_message, "{bt}").unwrap(); + writeln!(error_message).unwrap(); + } + + let caller = Location::caller(); + + writeln!( + error_message, + "Accessed from {caller} on thread #{current_thread}" + ) + .unwrap(); + + if let Some(bt) = record_backtrace() { + writeln!(error_message, "{bt}").unwrap(); + writeln!(error_message).unwrap(); + } + + panic!("{error_message}"); + } + } + } + + fn record_backtrace() -> Option { + let err = js_sys::Error::new(""); + let stack = JsString::from(wasm_bindgen::intern("stack")); + + js_sys::Reflect::get(&err, &stack) + .ok() + .and_then(|v| v.as_string()) + } +} + +/// A browser polyfill for [`std::thread::ThreadId`] [`std::thread::current()`]. +/// +/// This works by creating a `$WASMER_THREAD_ID` symbol and setting it on +/// the global object. As long as they use the same `SharedArrayBuffer` for +/// their linear memory, each thread (i.e. web worker or the UI thread) is +/// guaranteed to get a unique ID. +/// +/// This is mainly intended for use in `wasmer-wasix` and `wasmer-js`, and may +/// go away in the future. +#[doc(hidden)] +pub fn current_thread_id() -> u32 { + static NEXT_ID: AtomicU32 = AtomicU32::new(0); + + let global = js_sys::global(); + let thread_id_symbol = Symbol::for_("$WASMER_THREAD_ID"); + + if let Some(v) = js_sys::Reflect::get(&global, &thread_id_symbol) + .ok() + .and_then(|v| v.as_f64()) + { + // Note: we use a symbol so we know for sure that nobody else created + // this field. + return v as u32; + } + + // Looks like we haven't set the thread ID yet. + let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); + + js_sys::Reflect::set(&global, &thread_id_symbol, &JsValue::from(id)) + .expect("Setting a field on the global object should never fail"); + + id +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/js/mem_access.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/mem_access.rs new file mode 100644 index 0000000..f8a822d --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/mem_access.rs @@ -0,0 +1,65 @@ +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/arbitrator/tools/module_roots/wasmer/lib/api/src/js/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/mod.rs new file mode 100644 index 0000000..0584da2 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/mod.rs @@ -0,0 +1,41 @@ +#[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/arbitrator/tools/module_roots/wasmer/lib/api/src/js/module.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/module.rs new file mode 100644 index 0000000..61866ab --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/module.rs @@ -0,0 +1,479 @@ +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 bytes::Bytes; +use js_sys::{Reflect, Uint8Array, WebAssembly}; +use std::path::Path; +use tracing::{debug, warn}; +use wasm_bindgen::JsValue; +use wasmer_types::{ + CompileError, DeserializeError, ExportsIterator, ExternType, FunctionType, GlobalType, + ImportsIterator, MemoryType, ModuleInfo, Mutability, Pages, SerializeError, TableType, Type, +}; + +/// WebAssembly in the browser doesn't yet output the descriptor/types +/// corresponding to each extern (import and export). +/// +/// This should be fixed once the JS-Types Wasm proposal is adopted +/// by the browsers: +/// +/// +/// Until that happens, we annotate the module with the expected +/// types so we can built on top of them at runtime. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ModuleTypeHints { + /// The type hints for the imported types + pub imports: Vec, + /// The type hints for the exported types + pub exports: Vec, +} + +#[derive(Clone, PartialEq, Eq)] +pub struct Module { + module: JsHandle, + name: Option, + // WebAssembly type hints + type_hints: Option, + #[cfg(feature = "js-serializable-module")] + raw_bytes: Option, +} + +// Module implements `structuredClone` in js, so it's safe it to make it Send. +// https://developer.mozilla.org/en-US/docs/Web/API/structuredClone +// ```js +// const module = new WebAssembly.Module(new Uint8Array([ +// 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00 +// ])); +// structuredClone(module) +// ``` +unsafe impl Send for Module {} +unsafe impl Sync for Module {} + +impl From for JsValue { + fn from(val: Module) -> Self { + Self::from(val.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 js_bytes = Uint8Array::view(binary); + let module = WebAssembly::Module::new(&js_bytes.into()) + .map_err(|e| CompileError::Validate(format!("{}", e.as_string().unwrap())))?; + Ok(Self::from_js_module(module, binary)) + } + + /// Creates a new WebAssembly module skipping any kind of validation from a javascript module + pub(crate) unsafe fn from_js_module( + module: WebAssembly::Module, + binary: impl IntoBytes, + ) -> Self { + let binary = binary.into_bytes(); + + // 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(); + + ( + Some(ModuleTypeHints { + imports: info + .info + .imports() + .map(|import| import.ty().clone()) + .collect::>(), + exports: info + .info + .exports() + .map(|export| export.ty().clone()) + .collect::>(), + }), + info.info.name, + ) + }; + #[cfg(not(feature = "wasm-types-polyfill"))] + let (type_hints, name) = (None, None); + + Self { + module: JsHandle::new(module), + type_hints, + name, + #[cfg(feature = "js-serializable-module")] + raw_bytes: Some(binary), + } + } + + pub fn validate(_engine: &impl AsEngineRef, binary: &[u8]) -> Result<(), CompileError> { + let js_bytes = unsafe { Uint8Array::view(binary) }; + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + unsafe { + match WebAssembly::validate(&js_bytes.into()) { + Ok(true) => Ok(()), + _ => Err(CompileError::Validate("Invalid Wasm file".to_owned())), + } + } + } + + pub(crate) fn instantiate( + &self, + store: &mut impl AsStoreMut, + imports: &Imports, + ) -> Result { + // Ensure all imports come from the same store. + if imports + .into_iter() + .any(|(_, import)| !import.is_from_store(store)) + { + return Err(RuntimeError::user(Box::new( + InstantiationError::DifferentStores, + ))); + } + + let imports_object = js_sys::Object::new(); + let mut import_externs: Vec = vec![]; + for import_type in self.imports() { + let resolved_import = imports.get_export(import_type.module(), import_type.name()); + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_variables)] + if let wasmer_types::ExternType::Memory(mem_ty) = import_type.ty() { + if resolved_import.is_some() { + debug!("imported shared memory {:?}", &mem_ty); + } else { + warn!( + "Error while importing {0:?}.{1:?}: memory. Expected {2:?}", + import_type.module(), + import_type.name(), + import_type.ty(), + ); + } + } + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + unsafe { + if let Some(import) = resolved_import { + let val = js_sys::Reflect::get(&imports_object, &import_type.module().into())?; + if !val.is_undefined() { + // If the namespace is already set + js_sys::Reflect::set( + &val, + &import_type.name().into(), + &import.as_jsvalue(&store.as_store_ref()), + )?; + } else { + // If the namespace doesn't exist + let import_namespace = js_sys::Object::new(); + js_sys::Reflect::set( + &import_namespace, + &import_type.name().into(), + &import.as_jsvalue(&store.as_store_ref()), + )?; + js_sys::Reflect::set( + &imports_object, + &import_type.module().into(), + &import_namespace.into(), + )?; + } + import_externs.push(import); + } else { + warn!( + "import not found {}:{}", + import_type.module(), + import_type.name() + ); + } + } + // in case the import is not found, the JS Wasm VM will handle + // the error for us, so we don't need to handle it + } + Ok(WebAssembly::Instance::new(&self.module, &imports_object) + .map_err(|e: JsValue| -> RuntimeError { e.into() })?) + } + + pub fn name(&self) -> Option<&str> { + self.name.as_ref().map(|s| s.as_ref()) + } + + pub fn serialize(&self) -> Result { + #[cfg(feature = "js-serializable-module")] + return self.raw_bytes.clone().ok_or(SerializeError::Generic( + "Not able to serialize module".to_string(), + )); + + #[cfg(not(feature = "js-serializable-module"))] + return Err(SerializeError::Generic( + "You need to enable the `js-serializable-module` feature flag to serialize a `Module`" + .to_string(), + )); + } + + #[tracing::instrument(level = "debug", skip_all)] + pub unsafe fn deserialize_unchecked( + _engine: &impl AsEngineRef, + _bytes: impl IntoBytes, + ) -> Result { + #[cfg(feature = "js-serializable-module")] + return Self::from_binary(_engine, &_bytes.into_bytes()) + .map_err(|e| DeserializeError::Compiler(e)); + + #[cfg(not(feature = "js-serializable-module"))] + return Err(DeserializeError::Generic("You need to enable the `js-serializable-module` feature flag to deserialize a `Module`".to_string())); + } + + #[tracing::instrument(level = "debug", skip_all)] + pub unsafe fn deserialize( + _engine: &impl AsEngineRef, + _bytes: impl IntoBytes, + ) -> Result { + #[cfg(feature = "js-serializable-module")] + return Self::from_binary(_engine, &_bytes.into_bytes()) + .map_err(|e| DeserializeError::Compiler(e)); + + #[cfg(not(feature = "js-serializable-module"))] + return Err(DeserializeError::Generic("You need to enable the `js-serializable-module` feature flag to deserialize a `Module`".to_string())); + } + + pub unsafe fn deserialize_from_file_unchecked( + engine: &impl AsEngineRef, + path: impl AsRef, + ) -> Result { + let bytes = std::fs::read(path.as_ref())?; + Self::deserialize(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 + // match Reflect::set(self.module.as_ref(), &"wasmer_name".into(), &name.into()) { + // Ok(_) => true, + // _ => false + // } + // Arc::get_mut(&mut self.artifact) + // .and_then(|artifact| artifact.module_mut()) + // .map(|mut module_info| { + // module_info.info.name = Some(name.to_string()); + // true + // }) + // .unwrap_or(false) + } + + pub fn imports<'a>(&'a self) -> ImportsIterator + 'a> { + let imports = WebAssembly::Module::imports(&self.module); + let iter = imports + .iter() + .enumerate() + .map(move |(i, val)| { + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + unsafe { + let module = Reflect::get(val.as_ref(), &"module".into()) + .unwrap() + .as_string() + .unwrap(); + let field = Reflect::get(val.as_ref(), &"name".into()) + .unwrap() + .as_string() + .unwrap(); + let kind = Reflect::get(val.as_ref(), &"kind".into()) + .unwrap() + .as_string() + .unwrap(); + let type_hint = self + .type_hints + .as_ref() + .map(|hints| hints.imports.get(i).unwrap().clone()); + let extern_type = if let Some(hint) = type_hint { + hint + } else { + match kind.as_str() { + "function" => { + let func_type = FunctionType::new(vec![], vec![]); + ExternType::Function(func_type) + } + "global" => { + let global_type = GlobalType::new(Type::I32, Mutability::Const); + ExternType::Global(global_type) + } + "memory" => { + // The javascript API does not yet expose these properties so without + // the type_hints we don't know what memory to import. + let memory_type = MemoryType::new(Pages(1), None, false); + ExternType::Memory(memory_type) + } + "table" => { + let table_type = TableType::new(Type::FuncRef, 1, None); + ExternType::Table(table_type) + } + _ => unimplemented!(), + } + }; + ImportType::new(&module, &field, extern_type) + } + }) + .collect::>() + .into_iter(); + ImportsIterator::new(iter, imports.length() as usize) + } + + /// Set the type hints for this module. + /// + /// Returns an error if the hints doesn't match the shape of + /// import or export types of the module. + #[allow(unused)] + pub fn set_type_hints(&mut self, type_hints: ModuleTypeHints) -> Result<(), String> { + let exports = WebAssembly::Module::exports(&self.module); + // Check exports + if exports.length() as usize != type_hints.exports.len() { + return Err("The exports length must match the type hints lenght".to_owned()); + } + for (i, val) in exports.iter().enumerate() { + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + let kind = unsafe { + Reflect::get(val.as_ref(), &"kind".into()) + .unwrap() + .as_string() + .unwrap() + }; + // It is safe to unwrap as we have already checked for the exports length + let type_hint = type_hints.exports.get(i).unwrap(); + let expected_kind = match type_hint { + ExternType::Function(_) => "function", + ExternType::Global(_) => "global", + ExternType::Memory(_) => "memory", + ExternType::Table(_) => "table", + }; + if expected_kind != kind.as_str() { + return Err(format!("The provided type hint for the export {} is {} which doesn't match the expected kind: {}", i, kind.as_str(), expected_kind)); + } + } + self.type_hints = Some(type_hints); + Ok(()) + } + + pub fn exports<'a>(&'a self) -> ExportsIterator + 'a> { + let exports = WebAssembly::Module::exports(&self.module); + let iter = exports + .iter() + .enumerate() + .map(move |(i, val)| { + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + let field = unsafe { + Reflect::get(val.as_ref(), &"name".into()) + .unwrap() + .as_string() + .unwrap() + }; + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + let kind = unsafe { + Reflect::get(val.as_ref(), &"kind".into()) + .unwrap() + .as_string() + .unwrap() + }; + let type_hint = self + .type_hints + .as_ref() + .map(|hints| hints.exports.get(i).unwrap().clone()); + let extern_type = if let Some(hint) = type_hint { + hint + } else { + // The default types + match kind.as_str() { + "function" => { + let func_type = FunctionType::new(vec![], vec![]); + ExternType::Function(func_type) + } + "global" => { + let global_type = GlobalType::new(Type::I32, Mutability::Const); + ExternType::Global(global_type) + } + "memory" => { + let memory_type = MemoryType::new(Pages(1), None, false); + ExternType::Memory(memory_type) + } + "table" => { + let table_type = TableType::new(Type::FuncRef, 1, None); + ExternType::Table(table_type) + } + _ => unimplemented!(), + } + }; + ExportType::new(&field, extern_type) + }) + .collect::>() + .into_iter(); + ExportsIterator::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(crate) fn info(&self) -> &ModuleInfo { + unimplemented!() + } +} + +impl From for Module { + #[track_caller] + fn from(module: WebAssembly::Module) -> Module { + Module { + module: JsHandle::new(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 From for WebAssembly::Module { + fn from(value: crate::module::Module) -> Self { + value.0.module.into_inner() + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/js/store.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/store.rs new file mode 100644 index 0000000..03f229a --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/store.rs @@ -0,0 +1,266 @@ +pub(crate) use objects::{InternalStoreHandle, StoreObject}; +pub use objects::{StoreHandle, StoreObjects}; + +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/arbitrator/tools/module_roots/wasmer/lib/api/src/js/trap.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/trap.rs new file mode 100644 index 0000000..23a002e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/trap.rs @@ -0,0 +1,179 @@ +use std::{ + error::Error, + fmt::{self, Display}, +}; + +use js_sys::Reflect; +use wasm_bindgen::{prelude::*, JsValue}; + +use crate::RuntimeError; + +#[derive(Debug)] +enum InnerTrap { + User(Box), + Js(JsTrap), +} + +/// A struct representing a Trap +#[wasm_bindgen(skip_typescript)] +#[derive(Debug)] +pub struct Trap { + inner: InnerTrap, +} + +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, + } + } +} + +#[wasm_bindgen] +impl Trap { + /// A marker method to indicate that an object is an instance of the `Trap` + /// class. + pub fn __wbg_wasmer_trap() {} +} + +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 fmt::Display for Trap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.inner { + InnerTrap::User(e) => write!(f, "user: {e}"), + InnerTrap::Js(value) => write!(f, "js: {value}"), + } + } +} + +impl From for RuntimeError { + fn from(value: JsValue) -> Self { + // We try to downcast the error and see if it's an instance of Trap + // instead, so we don't need to re-wrap it. + if let Some(obj) = value.dyn_ref() { + if let Some(trap) = downcast_from_ptr(obj) { + return trap.into(); + } + } + + RuntimeError::from(Trap { + inner: InnerTrap::Js(value.into()), + }) + } +} + +/// This whole mechanism works because the JavaScript wrapper class has a static +/// `__wbg_wasmer_trap()` method which marks that it is a [`Trap`]. +/// +/// If that method exists, we assume the pointer is valid and safe to cast back +/// to our type. +fn downcast_from_ptr(value: &JsValue) -> Option { + if !value.is_object() { + return None; + } + + let prototype = &Reflect::get_prototype_of(value).ok()?; + let class = prototype.constructor(); + let key = JsValue::from_str("__wbg_wasmer_trap"); + + let marker_func: Option = Reflect::get(&class, &key) + .and_then(|v: JsValue| v.dyn_into()) + .ok(); + + if marker_func.is_none() { + // We couldn't find the marker, so it's something else. + return None; + } + + // Safety: The marker function exists, therefore it's safe to convert back + // to a Trap. + unsafe { + // Note: this assumes the wrapper class generated by #[wasm_bindgen] will + // always have a `__destroy_into_raw()` method which consumes the `Trap` + // wrapper and returns a pointer. + // + // This is valid as of wasm-bindgen version 0.2.87 + let key = JsValue::from_str("__destroy_into_raw"); + let ptr = Reflect::get(value, &key) + .ok() + .and_then(|v| v.dyn_into::().ok()) + .and_then(|destroy_into_raw| destroy_into_raw.call0(value).ok()) + .and_then(|ret| ret.as_f64())?; + + Some(::from_abi( + ptr as u32, + )) + } +} + +/// A `Send+Sync` version of a JavaScript error. +#[derive(Debug)] +enum JsTrap { + /// An error message. + Message(String), + /// Unable to determine the underlying error. + Unknown, +} + +impl From for JsTrap { + fn from(value: JsValue) -> Self { + // Let's try some easy special cases first + if let Some(error) = value.dyn_ref::() { + return JsTrap::Message(error.message().into()); + } + + if let Some(s) = value.as_string() { + return JsTrap::Message(s); + } + + // Otherwise, we'll try to stringify the error and hope for the best + if let Some(obj) = value.dyn_ref::() { + return JsTrap::Message(obj.to_string().into()); + } + + JsTrap::Unknown + } +} + +impl Display for JsTrap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + JsTrap::Message(m) => write!(f, "{m}"), + JsTrap::Unknown => write!(f, "unknown"), + } + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/js/typed_function.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/typed_function.rs new file mode 100644 index 0000000..22cd2bf --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/typed_function.rs @@ -0,0 +1,116 @@ +//! 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 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 js_sys::Array; +use std::iter::FromIterator; +use wasm_bindgen::JsValue; +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(&self, mut store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result where + $( $x: FromToNativeWasmType + NativeWasmTypeInto, )* + { + #[allow(unused_unsafe)] + let params_list: Vec<_> = unsafe { + vec![ $( ($x::WASM_TYPE, $x.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( + &JsValue::UNDEFINED, + unsafe { + &Array::from_iter(params_list.clone() + .into_iter() + .map(|(b, a)| Value::from_raw(store, b, a).as_jsvalue(store))) + } + ); + let store_mut = store.as_store_mut(); + 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 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 => unsafe { + let ty = Rets::wasm_types()[0]; + let val = param_from_js(&ty, &results); + *mut_rets = val.as_raw(&mut store); + } + _n => { + let results: Array = results.into(); + 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 slot = mut_rets.add(i); + *slot = val.as_raw(&mut store); + } + } + } + } + Ok(unsafe { Rets::from_array(store, rets_list_array) }) + } + } + }; +} + +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/arbitrator/tools/module_roots/wasmer/lib/api/src/js/vm.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/vm.rs new file mode 100644 index 0000000..3d22705 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/vm.rs @@ -0,0 +1,291 @@ +/// 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 std::{any::Any, fmt}; + +use js_sys::{ + Function as JsFunction, + WebAssembly::{self, Memory as JsMemory, Table as JsTable}, +}; +use serde::{Deserialize, Serialize}; +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 + #[deprecated = "use `copy` instead"] + pub fn duplicate(&mut self) -> Result { + self.copy() + } + + /// 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), +} + +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/arbitrator/tools/module_roots/wasmer/lib/api/src/js/wasm_bindgen_polyfill.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/wasm_bindgen_polyfill.rs new file mode 100644 index 0000000..80f89a6 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/js/wasm_bindgen_polyfill.rs @@ -0,0 +1,33 @@ +use js_sys::Object; +use wasm_bindgen::prelude::*; + +// WebAssembly.Global +#[wasm_bindgen] +extern "C" { + /// The `WebAssembly.Global()` constructor creates a new `Global` object + /// of the given type and value. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Global) + #[wasm_bindgen(js_namespace = WebAssembly, extends = Object, typescript_type = "WebAssembly.Global")] + #[derive(Clone, Debug, PartialEq, Eq)] + pub type Global; + + /// The `WebAssembly.Global()` constructor creates a new `Global` object + /// of the given type and value. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Global) + #[allow(unused_doc_comments)] + #[wasm_bindgen(constructor, js_namespace = WebAssembly, catch)] + pub fn new(global_descriptor: &Object, value: &JsValue) -> Result; + + /// The value prototype property of the `WebAssembly.Global` object + /// returns the value of the global. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Global) + #[allow(unused_doc_comments)] + #[wasm_bindgen(method, getter, structural, js_namespace = WebAssembly)] + pub fn value(this: &Global) -> JsValue; + + #[wasm_bindgen(method, setter = value, structural, js_namespace = WebAssembly)] + pub fn set_value(this: &Global, value: &JsValue); +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/as_js.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/as_js.rs new file mode 100644 index 0000000..77175fc --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/as_js.rs @@ -0,0 +1,154 @@ +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; + +/// Convert the given type to a [`JsValue`]. +pub trait AsJs: 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; + /// Convert the given type to a [`JsValue`]. + fn from_jsvalue( + store: &mut impl AsStoreMut, + type_: &Self::DefinitionType, + value: &JSValue, + ) -> Result; +} + +#[inline] +pub fn param_from_js(context: &JSContext, ty: &Type, js_val: &JSValue) -> Value { + match ty { + Type::I32 => Value::I32(js_val.to_number(&context).unwrap() as _), + Type::I64 => { + let number = if js_val.is_number(&context) { + js_val.to_number(&context).unwrap() as _ + } else { + js_val + .to_string(&context) + .unwrap() + .to_string() + .parse() + .unwrap() + }; + Value::I64(number) + } + Type::F32 => Value::F32(js_val.to_number(&context).unwrap() as _), + Type::F64 => Value::F64(js_val.to_number(&context).unwrap()), + Type::V128 => { + let number = if js_val.is_number(&context) { + js_val.to_number(&context).unwrap() as _ + } else { + js_val + .to_string(&context) + .unwrap() + .to_string() + .parse() + .unwrap() + }; + Value::V128(number) + } + Type::ExternRef | Type::FuncRef => unimplemented!( + "The type `{:?}` is not yet supported in the JS Function API", + ty + ), + } +} + +impl AsJs for Value { + type DefinitionType = Type; + + fn as_jsvalue(&self, store: &impl AsStoreRef) -> JSValue { + let engine = store.as_store_ref(); + let context = engine.jsc().context(); + match self { + Self::I32(i) => JSValue::number(&context, *i as _), + // JavascriptCore will fail with: + // new WebAssembly.Global({value: "i64", mutable: false}, 3); + // But will succeed with + // new WebAssembly.Global({value: "i64", mutable: false}, "3"); + Self::I64(i) => JSValue::string(&context, (*i).to_string()), + 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(None) => JSValue::null(&context), + Self::ExternRef(_) => unimplemented!(), + } + } + + fn from_jsvalue( + 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)) + } +} + +impl AsJs for Extern { + type DefinitionType = ExternType; + + fn as_jsvalue(&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(), + } + } + + fn from_jsvalue( + store: &mut impl AsStoreMut, + extern_type: &Self::DefinitionType, + val: &JSValue, + ) -> Result { + // Note: this function do a soft check over the type + // We only check the "kind" of Extern, but nothing else + // unimplemented!(); + let engine = store.as_store_mut(); + let context = engine.jsc().context(); + match extern_type { + ExternType::Function(function_type) => { + let obj_val = val.to_object(&context).unwrap(); + Ok(Self::Function(Function::from_vm_extern( + store, + VMFunction::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( + store, + VMGlobal::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( + store, + VMMemory::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( + store, + VMTable::new(obj_val, table_type.clone()), + ))) + } + } + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/engine.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/engine.rs new file mode 100644 index 0000000..adbf7a3 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/engine.rs @@ -0,0 +1,220 @@ +use rusty_jsc::{JSContext, JSObject}; +use std::sync::Arc; + +#[derive(Debug)] +pub(crate) struct JSCEngine { + context: JSContext, + global_wasm: JSObject, + wasm_validate_type: JSObject, + wasm_module_type: JSObject, + wasm_instance_type: JSObject, + wasm_global_type: JSObject, + wasm_table_type: JSObject, + wasm_memory_type: JSObject, +} + +impl Default for JSCEngine { + fn default() -> Self { + let context = JSContext::default(); + let mut global = context.get_global_object(); + let mut global_wasm = global + .get_property(&context, "WebAssembly".to_string()) + .to_object(&context) + .expect("WebAssembly is not available in JavascriptCore"); + + let mut wasm_validate_type = global_wasm + .get_property(&context, "validate".to_string()) + .to_object(&context) + .unwrap(); + + let mut wasm_module_type = global_wasm + .get_property(&context, "Module".to_string()) + .to_object(&context) + .unwrap(); + + let mut wasm_instance_type = global_wasm + .get_property(&context, "Instance".to_string()) + .to_object(&context) + .unwrap(); + + let mut wasm_global_type = global_wasm + .get_property(&context, "Global".to_string()) + .to_object(&context) + .unwrap(); + + let mut wasm_table_type = global_wasm + .get_property(&context, "Table".to_string()) + .to_object(&context) + .unwrap(); + + let mut wasm_memory_type = global_wasm + .get_property(&context, "Memory".to_string()) + .to_object(&context) + .unwrap(); + + Self { + context, + global_wasm, + wasm_validate_type, + wasm_module_type, + wasm_instance_type, + wasm_global_type, + wasm_table_type, + wasm_memory_type, + } + } +} + +/// A WebAssembly `Universal` Engine. +#[derive(Clone, Debug, Default)] +pub struct Engine { + inner: Arc, +} + +unsafe impl Send for Engine {} +unsafe impl Sync for Engine {} + +impl From<&crate::engine::Engine> for Engine { + fn from(engine: &crate::engine::Engine) -> Self { + engine.0.clone() + } +} + +pub(crate) trait JSC { + fn jsc(&self) -> &JSCEngine; +} + +impl JSC for crate::Engine { + #[inline] + fn jsc(&self) -> &JSCEngine { + &self.0.inner + } +} + +impl JSC for crate::engine::EngineRef<'_> { + #[inline] + fn jsc(&self) -> &JSCEngine { + &self.engine().0.inner + } +} + +impl JSC for crate::store::StoreRef<'_> { + #[inline] + fn jsc(&self) -> &JSCEngine { + &self.engine().jsc() + } +} + +impl JSC for crate::store::StoreMut<'_> { + #[inline] + fn jsc(&self) -> &JSCEngine { + &self.engine().jsc() + } +} + +impl JSC for crate::Store { + #[inline] + fn jsc(&self) -> &JSCEngine { + &self.engine().jsc() + } +} + +impl JSCEngine { + #[inline] + pub(crate) fn context(&self) -> &JSContext { + &self.context + } + + #[inline] + pub(crate) fn global_wasm(&self) -> &JSObject { + &self.global_wasm + } + + #[inline] + pub(crate) fn wasm_module_type(&self) -> &JSObject { + &self.wasm_module_type + } + + #[inline] + pub(crate) fn wasm_validate_type(&self) -> &JSObject { + &self.wasm_validate_type + } + + #[inline] + pub(crate) fn wasm_instance_type(&self) -> &JSObject { + &self.wasm_instance_type + } + + #[inline] + pub(crate) fn wasm_global_type(&self) -> &JSObject { + &self.wasm_global_type + } + + #[inline] + pub(crate) fn wasm_table_type(&self) -> &JSObject { + &self.wasm_table_type + } + + #[inline] + pub(crate) fn wasm_memory_type(&self) -> &JSObject { + &self.wasm_memory_type + } +} + +impl Engine { + pub(crate) fn deterministic_id(&self) -> &str { + // All js engines have the same id + "javascriptcore" + } + + #[inline] + pub(crate) fn context(&self) -> &JSContext { + &self.inner.context + } + + #[inline] + pub(crate) fn global_wasm(&self) -> &JSObject { + &self.inner.global_wasm + } + + #[inline] + pub(crate) fn wasm_module_type(&self) -> &JSObject { + &self.inner.wasm_module_type + } + + #[inline] + pub(crate) fn wasm_validate_type(&self) -> &JSObject { + &self.inner.wasm_validate_type + } + + #[inline] + pub(crate) fn wasm_instance_type(&self) -> &JSObject { + &self.inner.wasm_instance_type + } + + #[inline] + pub(crate) fn wasm_global_type(&self) -> &JSObject { + &self.inner.wasm_global_type + } + + #[inline] + pub(crate) fn wasm_table_type(&self) -> &JSObject { + &self.inner.wasm_table_type + } + + #[inline] + pub(crate) fn wasm_memory_type(&self) -> &JSObject { + &self.inner.wasm_memory_type + } +} + +// 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/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/errors.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/errors.rs new file mode 100644 index 0000000..88f20ba --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/errors.rs @@ -0,0 +1,30 @@ +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!(); + } +} + +pub(crate) fn raise(error: Box) -> ! { + unimplemented!() + // let error = Trap::user(error); + // let js_error: JsValue = error.into(); + // wasm_bindgen::throw_val(js_error) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/extern_ref.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/extern_ref.rs new file mode 100644 index 0000000..410a35b --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/extern_ref.rs @@ -0,0 +1,39 @@ +use std::any::Any; + +use crate::jsc::vm::VMExternRef; +use crate::store::{AsStoreMut, AsStoreRef}; + +#[derive(Debug, Clone)] +#[repr(transparent)] +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 in Javascript"); + } + + 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 Javascript"); + } + + pub(crate) fn vm_externref(&self) -> VMExternRef { + unimplemented!("ExternRef is not yet supported in Javascript"); + } + + pub(crate) unsafe fn from_vm_externref( + _store: &mut impl AsStoreMut, + _vm_externref: VMExternRef, + ) -> Self { + unimplemented!("ExternRef is not yet supported in Javascript"); + } + + pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/function.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/function.rs new file mode 100644 index 0000000..bfe64f5 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/function.rs @@ -0,0 +1,582 @@ +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/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/global.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/global.rs new file mode 100644 index 0000000..ca83f52 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/global.rs @@ -0,0 +1,112 @@ +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}; + +#[derive(Debug, Clone, PartialEq)] +pub struct Global { + pub(crate) handle: VMGlobal, +} + +// 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 { + VMExtern::Global(self.handle.clone()) + } + + /// 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 { + if !val.is_from_store(store) { + return Err(RuntimeError::new("cross-`Store` values are not supported")); + } + let global_ty = GlobalType { + mutability, + ty: val.ty(), + }; + let store_mut = store.as_store_mut(); + let engine = store_mut.engine(); + let context = engine.0.context(); + + let mut descriptor = JSObject::new(&context); + let type_str = match val.ty() { + Type::I32 => "i32", + Type::I64 => "i64", + Type::F32 => "f32", + Type::F64 => "f64", + ty => unimplemented!( + "The type: {:?} is not yet supported in the JS Global API", + ty + ), + }; + // This is the value type as string, even though is incorrectly called "value" + // in the JS API. + descriptor.set_property( + &context, + "value".to_string(), + JSValue::string(&context, type_str.to_string()), + ); + descriptor.set_property( + &context, + "mutable".to_string(), + JSValue::boolean(&context, mutability.is_mutable()), + ); + + let value: JSValue = val.as_jsvalue(&store_mut); + let js_global = engine + .0 + .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)) + } + + pub fn ty(&self, _store: &impl AsStoreRef) -> GlobalType { + self.handle.ty + } + + 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 value = self + .handle + .global + .get_property(&context, "value".to_string()); + param_from_js(&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 engine = store_mut.engine(); + let context = engine.0.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 fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/memory.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/memory.rs new file mode 100644 index 0000000..bcb9361 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/memory.rs @@ -0,0 +1,359 @@ +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; + +use tracing::warn; + +use wasmer_types::{Pages, WASM_PAGE_SIZE}; + +use super::memory_view::MemoryView; + +pub use wasmer_types::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 vm_memory = VMMemory::new(Self::js_memory_from_type(store, &ty)?, ty); + Ok(Self::from_vm_extern(store, vm_memory)) + } + + 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), + ); + + engine + .0 + .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::from_vm_extern(new_store, memory) + } + + pub(crate) fn to_vm_extern(&self) -> VMExtern { + 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.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; + Err(MemoryError::CouldNotGrow { + current: old_pages, + attempted_delta: pages, + }) + } + } + // 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( + &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: VMMemory) -> Self { + Self { handle: internal } + } + + /// 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.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)?; + 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 { 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(mem::size_of::()); + *dst = dst.add(mem::size_of::()); + *len -= 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(mem::size_of::()); + *dst = dst.add(mem::size_of::()); + *len -= 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/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/memory_view.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/memory_view.rs new file mode 100644 index 0000000..bb89129 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/memory_view.rs @@ -0,0 +1,217 @@ +use crate::store::AsStoreRef; +use crate::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. +/// +/// 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: Pages, +} + +impl<'a> MemoryView<'a> { + pub(crate) fn new(memory: &Memory, store: &'a (impl AsStoreRef + ?Sized)) -> Self { + Self::new_raw(&memory.handle.memory, store) + } + + 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 buffer = memory + .get_property(&context, "buffer".to_string()) + .to_object(&context) + .unwrap(); + let typed_buffer = JSObject::create_typed_array_from_buffer(&context, buffer).unwrap(); + + let mut buffer_data = typed_buffer.get_typed_array_buffer(&context).unwrap(); + // println!("BUFFER DATA {}", buffer_data.to_string(&context)); + + // let definition = memory.handle.get(store.as_store_ref().objects()).vmmemory(); + // let def = unsafe { definition.as_ref() }; + Self { + buffer: MemoryBuffer { + base: buffer_data.as_mut_ptr(), + len: buffer_data.len(), + marker: PhantomData, + }, + // size, + } + } + + /// Returns the pointer to the raw bytes of the `Memory`. + // + // This used by wasmer-emscripten and 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] { + 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 { + Bytes(self.buffer.len).try_into().unwrap() + } + + #[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/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/mod.rs new file mode 100644 index 0000000..3575fe2 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/mod.rs @@ -0,0 +1,5 @@ +pub(crate) mod function; +pub(crate) mod global; +pub(crate) mod memory; +pub(crate) mod memory_view; +pub(crate) mod table; diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/table.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/table.rs new file mode 100644 index 0000000..2415977 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/externals/table.rs @@ -0,0 +1,158 @@ +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}; + +#[derive(Debug, Clone, PartialEq)] +pub struct Table { + pub(crate) handle: VMTable, +} + +// Table 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 Table {} + +fn set_table_item(table: &VMTable, item_index: u32, item: &JSObject) -> Result<(), RuntimeError> { + unimplemented!(); + // table.table.set(item_index, item).map_err(|e| e.into()) +} + +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 { + 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 context = engine.0.context(); + + let mut descriptor = JSObject::new(&context); + descriptor.set_property( + &context, + "initial".to_string(), + JSValue::number(&context, ty.minimum.into()), + ); + if let Some(max) = ty.maximum { + descriptor.set_property( + &context, + "maximum".to_string(), + JSValue::number(&context, max.into()), + ); + } + descriptor.set_property( + &context, + "element".to_string(), + JSValue::string(&context, "anyfunc".to_string()), + ); + + let js_table = engine + .0 + .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)) + } + + pub fn to_vm_extern(&self) -> VMExtern { + VMExtern::Table(self.handle.clone()) + } + + pub fn ty(&self, _store: &impl AsStoreRef) -> TableType { + self.handle.ty + } + + 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( + &self, + store: &mut impl AsStoreMut, + index: u32, + val: Value, + ) -> Result<(), RuntimeError> { + unimplemented!(); + // let item = get_function(store, val)?; + // set_table_item(&self.handle, index, &item) + } + + 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(); + self.handle + .table + .get_property(&context, "length".to_string()) + .to_number(&context) + .unwrap() as _ + } + + pub fn grow( + &self, + store: &mut impl AsStoreMut, + delta: u32, + _init: Value, + ) -> Result { + let store_mut = store.as_store_mut(); + let engine = store_mut.engine(); + let context = engine.0.context(); + let func = self + .handle + .table + .get_property(&context, "grow".to_string()) + .to_object(&context) + .unwrap(); + match func.call( + &context, + Some(&self.handle.table), + &[JSValue::number(&context, delta as _)], + ) { + Ok(val) => Ok(val.to_number(&context).unwrap() as _), + Err(e) => Err(>::into(e)), + } + } + + pub fn copy( + _store: &mut impl AsStoreMut, + _dst_table: &Self, + _dst_index: u32, + _src_table: &Self, + _src_index: u32, + _len: u32, + ) -> Result<(), RuntimeError> { + 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 fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/instance.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/instance.rs new file mode 100644 index 0000000..614eb76 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/instance.rs @@ -0,0 +1,77 @@ +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; + +#[derive(Clone, PartialEq, Eq)] +pub struct Instance { + pub(crate) _handle: VMInstance, +} + +// Instance 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 Instance {} + +impl Instance { + pub(crate) fn new( + mut store: &mut impl AsStoreMut, + module: &Module, + imports: &Imports, + ) -> Result<(Self, Exports), InstantiationError> { + let instance = module + .0 + .instantiate(&mut store, imports) + .map_err(|e| InstantiationError::Start(e))?; + + Self::from_module_and_instance(store, &module, instance) + } + + pub(crate) fn new_by_index( + store: &mut impl AsStoreMut, + module: &Module, + externs: &[Extern], + ) -> Result<(Self, Exports), InstantiationError> { + let mut imports = Imports::new(); + for (import_ty, extern_ty) in module.imports().zip(externs.iter()) { + imports.define(import_ty.module(), import_ty.name(), extern_ty.clone()); + } + Self::new(store, module, &imports) + } + + pub(crate) fn from_module_and_instance( + mut store: &mut impl AsStoreMut, + module: &Module, + instance: VMInstance, + ) -> Result<(Self, Exports), InstantiationError> { + let engine = store.as_store_mut(); + let context = engine.jsc().context(); + + let mut instance_exports = instance + .get_property(&context, "exports".to_string()) + .to_object(&context) + .unwrap(); + + let exports_ty = module.exports().collect::>(); + + let exports = exports_ty + .iter() + .map(|export_type| { + let name = export_type.name(); + let mut store = store.as_store_mut(); + let context = store.jsc().context(); + 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(); + Ok((name.to_string(), extern_)) + }) + .collect::>()?; + + Ok((Self { _handle: instance }, exports)) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/mem_access.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/mem_access.rs new file mode 100644 index 0000000..f878ed6 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/mem_access.rs @@ -0,0 +1,92 @@ +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/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/mod.rs new file mode 100644 index 0000000..3a195e3 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/mod.rs @@ -0,0 +1,39 @@ +//! 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/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/module.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/module.rs new file mode 100644 index 0000000..1eda38c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/module.rs @@ -0,0 +1,259 @@ +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 wasmer_types::{ + CompileError, DeserializeError, ExportsIterator, ExternType, FunctionType, GlobalType, + ImportsIterator, MemoryType, ModuleInfo, Mutability, Pages, SerializeError, TableType, Type, +}; + +#[derive(Clone, PartialEq, Eq)] +pub struct Module { + module: JSObject, + name: Option, + raw_bytes: Option, + info: ModuleInfo, +} + +// Module implements `structuredClone` in js, so it's safe it to make it Send. +// https://developer.mozilla.org/en-US/docs/Web/API/structuredClone +// ```js +// const module = new WebAssembly.Module(new Uint8Array([ +// 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00 +// ])); +// structuredClone(module) +// ``` +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 engine = engine.as_engine_ref(); + let jsc = engine.jsc(); + let context = jsc.context(); + let bytes = JSObject::create_typed_array_with_bytes(&context, &mut binary).unwrap(); + let module_type = jsc.wasm_module_type(); + let global_wasm = jsc.global_wasm(); + let module = module_type + .construct(&context, &[bytes.to_jsvalue()]) + .map_err(|e| CompileError::Validate(format!("{}", e.to_string(&context).unwrap())))?; + + Ok(Self::from_js_module(module, binary)) + } + + /// Creates a new WebAssembly module skipping any kind of validation from a javascript module + /// + pub(crate) unsafe fn from_js_module(module: JSObject, binary: impl IntoBytes) -> Self { + 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[..]) + .unwrap() + .info; + + Self { + 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(); + let jsc = engine.jsc(); + let context = jsc.context(); + let mut binary = binary.to_vec(); + let bytes = JSObject::create_typed_array_with_bytes(&context, &mut binary).unwrap(); + + let global_wasm = jsc.global_wasm(); + let validate_type = jsc.wasm_validate_type(); + + match validate_type.call(&context, Some(&global_wasm), &[bytes.to_jsvalue()]) { + Ok(val) => { + if val.to_bool(&context) { + Ok(()) + } else { + Err(CompileError::Validate(format!("Not a valid wasm binary"))) + } + } + Err(e) => Err(CompileError::Validate(format!( + "Error while validating: {}", + e.to_string(&context).unwrap() + ))), + } + } + + pub(crate) fn instantiate( + &self, + store: &mut impl AsStoreMut, + imports: &Imports, + ) -> Result { + // Ensure all imports come from the same store. + if imports + .into_iter() + .any(|(_, import)| !import.is_from_store(store)) + { + return Err(RuntimeError::user(Box::new( + InstantiationError::DifferentStores, + ))); + } + + let store = store.as_store_mut(); + let context = store.jsc().context(); + + let mut imports_object = JSObject::new(&context); + for import_type in self.imports() { + let resolved_import = imports.get_export(import_type.module(), import_type.name()); + if let Some(import) = resolved_import { + let val = imports_object.get_property(&context, import_type.module().to_string()); + if !val.is_undefined(&context) { + // If the namespace is already set + let mut obj_val = val.to_object(&context).unwrap(); + obj_val.set_property( + &context, + import_type.name().to_string(), + import.as_jsvalue(&store.as_store_ref()), + ); + } else { + // If the namespace doesn't exist + let mut import_namespace = JSObject::new(&context); + import_namespace.set_property( + &context, + import_type.name().to_string(), + import.as_jsvalue(&store.as_store_ref()), + ); + imports_object + .set_property( + &context, + import_type.module().to_string(), + import_namespace.to_jsvalue(), + ) + .unwrap(); + } + } else { + warn!( + "import not found {}:{}", + import_type.module(), + import_type.name() + ); + } + // in case the import is not found, the JS Wasm VM will handle + // the error for us, so we don't need to handle it + } + + let instance_type = store.jsc().wasm_instance_type(); + let instance = instance_type.construct( + &context, + &[self.module.to_jsvalue(), imports_object.to_jsvalue()], + ); + Ok(instance.map_err(|e: JSValue| -> RuntimeError { e.into() })?) + } + + 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) -> impl Iterator> + 'a { + self.info().custom_sections(name) + } + + pub(crate) fn info(&self) -> &ModuleInfo { + &self.info + } +} + +// 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()) +// } +// } diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/store.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/store.rs new file mode 100644 index 0000000..b6e28dc --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/store.rs @@ -0,0 +1,264 @@ +pub(crate) use objects::{InternalStoreHandle, StoreObject}; +pub use objects::{StoreHandle, StoreObjects}; + +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/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/trap.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/trap.rs new file mode 100644 index 0000000..fa606fc --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/trap.rs @@ -0,0 +1,117 @@ +use rusty_jsc::{JSContext, JSObject, JSValue}; + +use crate::RuntimeError; +use std::error::Error; +use std::fmt; + +#[derive(Debug)] +enum InnerTrap { + User(Box), + JSC(JSValue), +} + +/// A struct representing a Trap +#[derive(Debug)] +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(crate) fn into_jsvalue(self, ctx: &JSContext) -> JSValue { + match self.inner { + InnerTrap::User(err) => { + let obj = JSObject::new(ctx); + let err_ptr = Box::leak(Box::new(err)); + let wasmer_error_ptr = JSValue::number(&ctx, err_ptr as *mut _ as usize as _); + obj.set_property(&ctx, "wasmer_error_ptr".to_string(), wasmer_error_ptr) + .unwrap(); + obj.to_jsvalue() + } + InnerTrap::JSC(value) => value, + } + } + + pub(crate) fn from_jsvalue(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) { + let err_ptr = wasmer_error_ptr.to_number(ctx).unwrap() as usize + as *mut Box; + let err = unsafe { Box::from_raw(err_ptr) }; + return Self::user(*err); + } + Self { + inner: InnerTrap::JSC(val), + } + } +} + +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 fmt::Display for Trap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.inner { + InnerTrap::User(e) => write!(f, "user: {}", e), + InnerTrap::JSC(value) => write!(f, "jsc: obscure"), + } + } +} + +impl From for RuntimeError { + fn from(original: JSValue) -> Self { + let trap = Trap { + inner: InnerTrap::JSC(original), + }; + trap.into() + // unimplemented!("TODO: implement Trap::from(JSValue) for RuntimeError"); + // // We try to downcast the error and see if it's + // // an instance of RuntimeError instead, so we don't need + // // to re-wrap it. + // let trap = Trap::downcast_js(original).unwrap_or_else(|o| Trap { + // inner: InnerTrap::JSC(o), + // }); + // trap.into() + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/typed_function.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/typed_function.rs new file mode 100644 index 0000000..ff3cf39 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/typed_function.rs @@ -0,0 +1,145 @@ +//! 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 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 std::iter::FromIterator; +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(&self, mut store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result where + $( $x: FromToNativeWasmType + NativeWasmTypeInto, )* + { + // 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 store_mut = store.as_store_mut(); + let context = store_mut.jsc().context(); + + let 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 _)).unwrap(); + + 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 { + + let store_mut = store.as_store_mut(); + let context = store_mut.jsc().context(); + r = self.func.0.handle.function + .call( + &context, + None, + ¶ms_list, + ); + + // let store_mut = store.as_store_mut(); + 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)) => { let err = Err(RuntimeError::user(trap)); + return err; + }, + Err(trap) => { + return Err(RuntimeError::user(trap)) + }, + } + } + break; + } + let store_mut = store.as_store_mut(); + let context = store_mut.jsc().context(); + r.map_err(|e| Trap::from_jsvalue(context, e))? + }; + let mut rets_list_array = Rets::empty_array(); + let mut_rets = rets_list_array.as_mut() as *mut [RawValue] as *mut RawValue; + let store_mut = store.as_store_mut(); + let context = store_mut.jsc().context(); + match Rets::size() { + 0 => {}, + 1 => unsafe { + let ty = Rets::wasm_types()[0]; + let val = param_from_js(&context, &ty, &results); + *mut_rets = val.as_raw(&mut store); + } + _n => { + if !results.is_array(&context) { + panic!("Expected results to be an array.") + } + let results = results.to_object(&context).unwrap(); + for (i, ret_type) in Rets::wasm_types().iter().enumerate() { + let store_mut = store.as_store_mut(); + 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 slot = mut_rets.add(i); + *slot = val.as_raw(&mut store); + } + } + } + } + Ok(unsafe { Rets::from_array(store, rets_list_array) }) + } + } + }; +} + +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/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/vm.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/vm.rs new file mode 100644 index 0000000..23bcf68 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/jsc/vm.rs @@ -0,0 +1,270 @@ +/// 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 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 + #[deprecated = "use `copy` instead"] + pub fn duplicate( + &mut self, + store: &impl AsStoreRef, + ) -> Result { + self.copy(store) + } + + /// 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), +} + +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/arbitrator/tools/module_roots/wasmer/lib/api/src/lib.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/lib.rs new file mode 100644 index 0000000..0f2ed2d --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/lib.rs @@ -0,0 +1,530 @@ +#![doc( + html_logo_url = "https://github.com/wasmerio.png?size=200", + html_favicon_url = "https://wasmer.io/images/icons/favicon-32x32.png" +)] +#![deny( + missing_docs, + trivial_numeric_casts, + unused_extern_crates, + rustdoc::broken_intra_doc_links +)] +#![warn(unused_import_braces)] +#![allow(clippy::new_without_default, clippy::vtable_address_comparisons)] +#![warn( + clippy::float_arithmetic, + clippy::mut_mut, + clippy::nonminimal_bool, + clippy::map_unwrap_or, + clippy::print_stdout, + clippy::unicode_not_nfc, + clippy::use_self +)] +#![allow(deprecated_cfg_attr_crate_type_name)] +#![cfg_attr(feature = "js", crate_type = "cdylib")] + +//! [`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. +//! +//! It's designed to be safe and secure, and runnable in any kind of environment. +//! +//! # Usage +//! +//! Here is a small example of using Wasmer to run a WebAssembly module +//! written with its WAT format (textual format): +//! +//! ```rust +//! use wasmer::{Store, Module, Instance, Value, imports}; +//! +//! fn main() -> anyhow::Result<()> { +//! let module_wat = r#" +//! (module +//! (type $t0 (func (param i32) (result i32))) +//! (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32) +//! get_local $p0 +//! i32.const 1 +//! i32.add)) +//! "#; +//! +//! let mut store = Store::default(); +//! let module = Module::new(&store, &module_wat)?; +//! // The module doesn't import anything, so we create an empty import object. +//! let import_object = imports! {}; +//! let instance = Instance::new(&mut store, &module, &import_object)?; +//! +//! let add_one = instance.exports.get_function("add_one")?; +//! let result = add_one.call(&mut store, &[Value::I32(42)])?; +//! assert_eq!(result[0], Value::I32(43)); +//! +//! Ok(()) +//! } +//! ``` +//! +//! [Discover the full collection of examples](https://github.com/wasmerio/wasmer/tree/master/examples). +//! +//! # Overview of the Features +//! +//! Wasmer is not only fast, but also designed to be *highly customizable*: +//! +//! * **Pluggable compilers** — A compiler is used by the engine to +//! transform WebAssembly into executable code: +//! * [`wasmer-compiler-singlepass`] provides a fast compilation-time +//! but an unoptimized runtime speed, +//! * [`wasmer-compiler-cranelift`] provides the right balance between +//! compilation-time and runtime performance, useful for development, +//! * [`wasmer-compiler-llvm`] provides a deeply optimized executable +//! code with the fastest runtime speed, ideal for production. +//! +//! * **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 +//! compiler, which makes it more portable and faster to load. It's +//! ideal for constrainted environments. +//! +//! * **Cross-compilation** — Most compilers support cross-compilation. It +//! means it possible to pre-compile a WebAssembly module targetting a +//! different architecture or platform and serialize it, to then run it +//! on the targetted architecture and platform later. +//! +//! * **Run Wasmer in a JavaScript environment** — With the `js` Cargo +//! feature, it is possible to compile a Rust program using Wasmer to +//! WebAssembly. In this context, the resulting WebAssembly module will +//! expect to run in a JavaScript environment, like a browser, Node.js, +//! Deno and so on. In this specific scenario, there is no engines or +//! compilers available, it's the one available in the JavaScript +//! environment that will be used. +//! +//! Wasmer ships by default with the Cranelift compiler as its great for +//! development purposes. However, we strongly encourage to use the LLVM +//! compiler in production as it performs about 50% faster, achieving +//! near-native speeds. +//! +//! Note: if one wants to use multiple compilers at the same time, it's +//! also possible! One will need to import them directly via each of the +//! compiler crates. +//! +//! # Table of Contents +//! +//! - [WebAssembly Primitives](#webassembly-primitives) +//! - [Externs](#externs) +//! - [Functions](#functions) +//! - [Memories](#memories) +//! - [Globals](#globals) +//! - [Tables](#tables) +//! - [Project Layout](#project-layout) +//! - [Engines](#engines) +//! - [Compilers](#compilers) +//! - [Cargo Features](#cargo-features) +//! - [Using Wasmer in a JavaScript environment](#using-wasmer-in-a-javascript-environment) +//! +//! +//! # WebAssembly Primitives +//! +//! In order to make use of the power of the `wasmer` API, it's important +//! to understand the primitives around which the API is built. +//! +//! Wasm only deals with a small number of core data types, these data +//! types can be found in the [`Value`] type. +//! +//! In addition to the core Wasm types, the core types of the API are +//! referred to as "externs". +//! +//! ## Externs +//! +//! An [`Extern`] is a type that can be imported or exported from a Wasm +//! module. +//! +//! To import an extern, simply give it a namespace and a name with the +//! [`imports!`] macro: +//! +//! ``` +//! # use wasmer::{imports, Function, FunctionEnv, FunctionEnvMut, Memory, MemoryType, Store, Imports}; +//! # fn imports_example(mut store: &mut Store) -> Imports { +//! let memory = Memory::new(&mut store, MemoryType::new(1, None, false)).unwrap(); +//! imports! { +//! "env" => { +//! "my_function" => Function::new_typed(&mut store, || println!("Hello")), +//! "memory" => memory, +//! } +//! } +//! # } +//! ``` +//! +//! And to access an exported extern, see the [`Exports`] API, accessible +//! from any instance via `instance.exports`: +//! +//! ``` +//! # use wasmer::{imports, Instance, FunctionEnv, Memory, TypedFunction, Store}; +//! # fn exports_example(mut env: FunctionEnv<()>, mut store: &mut Store, instance: &Instance) -> anyhow::Result<()> { +//! let memory = instance.exports.get_memory("memory")?; +//! let memory: &Memory = instance.exports.get("some_other_memory")?; +//! let add: TypedFunction<(i32, i32), i32> = instance.exports.get_typed_function(&mut store, "add")?; +//! let result = add.call(&mut store, 5, 37)?; +//! assert_eq!(result, 42); +//! # Ok(()) +//! # } +//! ``` +//! +//! These are the primary types that the `wasmer` API uses. +//! +//! ### Functions +//! +//! There are 2 types of functions in `wasmer`: +//! 1. Wasm functions, +//! 2. Host functions. +//! +//! A Wasm function is a function defined in a WebAssembly module that can +//! only perform computation without side effects and call other functions. +//! +//! Wasm functions take 0 or more arguments and return 0 or more results. +//! Wasm functions can only deal with the primitive types defined in +//! [`Value`]. +//! +//! A Host function is any function implemented on the host, in this case in +//! Rust. +//! +//! Thus WebAssembly modules by themselves cannot do anything but computation +//! on the core types in [`Value`]. In order to make them more useful we +//! give them access to the outside world with [`imports!`]. +//! +//! If you're looking for a sandboxed, POSIX-like environment to execute Wasm +//! in, check out the [`wasmer-wasix`] crate for our implementation of WASI, +//! the WebAssembly System Interface, and WASIX, the Extended version of WASI. +//! +//! In the `wasmer` API we support functions which take their arguments and +//! return their results dynamically, [`Function`], and functions which +//! take their arguments and return their results statically, [`TypedFunction`]. +//! +//! ### Memories +//! +//! Memories store data. +//! +//! In most Wasm programs, nearly all data will live in a [`Memory`]. +//! +//! This data can be shared between the host and guest to allow for more +//! interesting programs. +//! +//! ### Globals +//! +//! A [`Global`] is a type that may be either mutable or immutable, and +//! contains one of the core Wasm types defined in [`Value`]. +//! +//! ### Tables +//! +//! A [`Table`] is an indexed list of items. +//! +//! # Project Layout +//! +//! The Wasmer project is divided into a number of crates, below is a dependency +//! graph with transitive dependencies removed. +//! +//!
+//! +//!
+//! +//! While this crate is the top level API, we also publish crates built +//! on top of this API that you may be interested in using, including: +//! +//! - [`wasmer-cache`] for caching compiled Wasm modules, +//! - [`wasmer-emscripten`] for running Wasm modules compiled to the +//! Emscripten ABI, +//! - [`wasmer-wasix`] for running Wasm modules compiled to the WASI ABI. +//! +//! The Wasmer project has two major abstractions: +//! 1. [Engine][wasmer-compiler], +//! 2. [Compilers][wasmer-compiler]. +//! +//! These two abstractions have multiple options that can be enabled +//! with features. +//! +//! ## Engine +//! +//! The engine is a system that uses a compiler to make a WebAssembly +//! module executable. +//! +//! ## Compilers +//! +//! 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. +//! +//! # 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` +#![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)" +)] +#![cfg_attr( + not(feature = "sys"), + doc = "## Features for the `sys` feature group (disabled)" +)] +//! +//! The default features can be enabled with the `sys-default` feature. +//! +//! The features for the `sys` feature group can be broken down into 2 +//! kinds: features that enable new functionality and features that +//! set defaults. +//! +//! The features that enable new functionality are: +//! - `cranelift` +#![cfg_attr(feature = "cranelift", doc = "(enabled),")] +#![cfg_attr(not(feature = "cranelift"), doc = "(disabled),")] +//! enables Wasmer's [Cranelift compiler][wasmer-compiler-cranelift], +//! - `llvm` +#![cfg_attr(feature = "llvm", doc = "(enabled),")] +#![cfg_attr(not(feature = "llvm"), doc = "(disabled),")] +//! enables Wasmer's [LLVM compiler][wasmer-compiler-lvm], +//! - `singlepass` +#![cfg_attr(feature = "singlepass", doc = "(enabled),")] +#![cfg_attr(not(feature = "singlepass"), doc = "(disabled),")] +//! enables Wasmer's [Singlepass compiler][wasmer-compiler-singlepass], +//! - `wat` +#![cfg_attr(feature = "wat", doc = "(enabled),")] +#![cfg_attr(not(feature = "wat"), doc = "(disabled),")] +//! enables `wasmer` to parse the WebAssembly text format, +//! - `compilation` +#![cfg_attr(feature = "compiler", doc = "(enabled),")] +#![cfg_attr(not(feature = "compiler"), doc = "(disabled),")] +//! enables compilation with the wasmer engine. +//! +#![cfg_attr( + feature = "js", + doc = "## Features for the `js` feature group (enabled)" +)] +#![cfg_attr( + not(feature = "js"), + doc = "## Features for the `js` feature group (disabled)" +)] +//! +//! The default features can be enabled with the `js-default` feature. +//! +//! Here are the detailed list of features: +//! +//! - `wasm-types-polyfill` +#![cfg_attr(feature = "wasm-types-polyfill", doc = "(enabled),")] +#![cfg_attr(not(feature = "wasm-types-polyfill"), doc = "(disabled),")] +//! parses the Wasm file, allowing to do type reflection of the +//! inner Wasm types. It adds 100kb to the Wasm bundle (28kb +//! gzipped). It is possible to disable it and to use +//! `Module::set_type_hints` manually instead for a lightweight +//! alternative. This is needed until the [Wasm JS introspection API +//! proposal](https://github.com/WebAssembly/js-types/blob/master/proposals/js-types/Overview.md) +//! is adopted by browsers, +//! - `wat` +#![cfg_attr(feature = "wat", doc = "(enabled),")] +#![cfg_attr(not(feature = "wat"), doc = "(disabled),")] +//! allows to read a Wasm file in its text format. This feature is +//! normally used only in development environments. It will add +//! around 650kb to the Wasm bundle (120Kb gzipped). +//! +//! # Using Wasmer in a JavaScript environment +//! +//! Imagine a Rust program that uses this `wasmer` crate to execute a +//! WebAssembly module. It is possible to compile this Rust progam to +//! WebAssembly by turning on the `js` Cargo feature of this `wasmer` +//! crate. +//! +//! Here is a small example illustrating such a Rust program, and how +//! to compile it with [`wasm-pack`] and [`wasm-bindgen`]: +//! +//! ```ignore +//! use wasm_bindgen::prelude::*; +//! use wasmer::{imports, Instance, Module, Store, Value}; +//! +//! #[wasm_bindgen] +//! pub extern fn do_add_one_in_wasmer() -> i32 { +//! let module_wat = r#" +//! (module +//! (type $t0 (func (param i32) (result i32))) +//! (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32) +//! get_local $p0 +//! i32.const 1 +//! i32.add)) +//! "#; +//! let mut store = Store::default(); +//! let module = Module::new(&store, &module_wat).unwrap(); +//! // The module doesn't import anything, so we create an empty import object. +//! let import_object = imports! {}; +//! let instance = Instance::new(&mut store, &module, &import_object).unwrap(); +//! +//! let add_one = instance.exports.get_function("add_one").unwrap(); +//! let result = add_one.call(&mut store, &[Value::I32(42)]).unwrap(); +//! assert_eq!(result[0], Value::I32(43)); +//! +//! result[0].unwrap_i32() +//! } +//! ``` +//! +//! Note that it's the same code as above with the former example. The +//! API is the same! +//! +//! Then, compile with `wasm-pack build`. Take care of using the `js` +//! or `js-default` Cargo features. +//! +//! [wasm]: https://webassembly.org/ +//! [wasmer-examples]: https://github.com/wasmerio/wasmer/tree/master/examples +//! [`wasmer-cache`]: https://docs.rs/wasmer-cache/ +//! [wasmer-compiler]: https://docs.rs/wasmer-compiler/ +//! [`wasmer-emscripten`]: https://docs.rs/wasmer-emscripten/ +//! [`wasmer-compiler-singlepass`]: https://docs.rs/wasmer-compiler-singlepass/ +//! [`wasmer-compiler-llvm`]: https://docs.rs/wasmer-compiler-llvm/ +//! [`wasmer-compiler-cranelift`]: https://docs.rs/wasmer-compiler-cranelift/ +//! [`wasmer-wasix`]: https://docs.rs/wasmer-wasix/ +//! [`wasm-pack`]: https://github.com/rustwasm/wasm-pack/ +//! [`wasm-bindgen`]: https://github.com/rustwasm/wasm-bindgen + +#[cfg(all(not(feature = "sys"), not(feature = "js"), not(feature = "jsc")))] +compile_error!("One of: `sys`, `js` or `jsc` 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 = "sys", feature = "jsc"))] +compile_error!( + "Cannot have both `sys` and `jsc` 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")))] +compile_error!( + "The `js` feature must be enabled only for the `wasm32` target (either `wasm32-unknown-unknown` or `wasm32-wasi`)." +); + +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; + +#[cfg(feature = "sys")] +/// sys +pub mod sys; + +#[cfg(feature = "sys")] +pub use sys::*; + +#[cfg(feature = "sys")] +#[deprecated(note = "wasmer::Artifact is deprecated, use wasmer::sys::Artifact instead")] +/// A compiled wasm module, ready to be instantiated. +pub type Artifact = sys::Artifact; +#[cfg(feature = "sys")] +#[deprecated(note = "wasmer::EngineBuilder is deprecated, use wasmer::sys::EngineBuilder instead")] +/// The Builder contents of `Engine` +pub type EngineBuilder = sys::EngineBuilder; +#[cfg(feature = "sys")] +#[deprecated(note = "wasmer::Features is deprecated, use wasmer::sys::Features instead")] +/// Controls which experimental features will be enabled. +pub type Features = sys::Features; +#[cfg(feature = "sys")] +#[deprecated(note = "wasmer::BaseTunables is deprecated, use wasmer::sys::BaseTunables instead")] +/// Tunable parameters for WebAssembly compilation. +/// This is the reference implementation of the `Tunables` trait, +/// used by default. +pub type BaseTunables = sys::BaseTunables; +#[cfg(feature = "sys")] +#[deprecated(note = "wasmer::VMConfig is deprecated, use wasmer::sys::VMConfig instead")] +/// Configuration for the the runtime VM +/// Currently only the stack size is configurable +pub type VMConfig = sys::VMConfig; + +#[cfg(feature = "js")] +mod js; + +#[cfg(feature = "js")] +pub use js::*; + +#[cfg(feature = "jsc")] +mod jsc; + +#[cfg(feature = "jsc")] +pub use jsc::*; + +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, StoreRef}; +#[cfg(feature = "sys")] +pub use store::{TrapHandlerFn, Tunables}; +#[cfg(any(feature = "sys", feature = "jsc"))] +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; +// 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, CpuFeature, DeserializeError, ExportIndex, ExportType, + ExternType, FrameInfo, FunctionType, GlobalInit, GlobalType, ImportType, LocalFunctionIndex, + MemoryError, MemoryType, MiddlewareError, Mutability, OnCalledAction, Pages, + ParseCpuFeatureError, SerializeError, TableType, Target, 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"); diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/mem_access.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/mem_access.rs new file mode 100644 index 0000000..7c0b031 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/mem_access.rs @@ -0,0 +1,404 @@ +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; +use thiserror::Error; +use wasmer_types::ValueType; + +/// Error for invalid [`Memory`][super::Memory] access. +#[derive(Clone, Copy, Debug, Error)] +#[non_exhaustive] +pub enum MemoryAccessError { + /// Memory access is outside heap bounds. + #[error("memory access out of bounds")] + HeapOutOfBounds, + /// Address calculation overflow. + #[error("address calculation overflow")] + Overflow, + /// String is not valid UTF-8. + #[error("string is not valid utf-8")] + NonUtf8String, +} + +impl From for RuntimeError { + fn from(err: MemoryAccessError) -> Self { + Self::new(err.to_string()) + } +} +impl From for MemoryAccessError { + fn from(_err: FromUtf8Error) -> Self { + Self::NonUtf8String + } +} + +/// Reference to a value in Wasm memory. +/// +/// The type of the value must satisfy the requirements of the `ValueType` +/// trait which guarantees that reading and writing such a value to untrusted +/// memory is safe. +/// +/// The address is required to be aligned: unaligned accesses cause undefined behavior. +/// +/// This wrapper safely handles concurrent modifications of the data by another +/// thread. +#[derive(Clone, Copy)] +pub struct WasmRef<'a, T: ValueType> { + #[allow(unused)] + pub(crate) buffer: MemoryBuffer<'a>, + pub(crate) offset: u64, + marker: PhantomData<*mut T>, +} + +impl<'a, T: ValueType> WasmRef<'a, T> { + /// Creates a new `WasmRef` at the given offset in a memory. + #[inline] + pub fn new(view: &'a MemoryView, offset: u64) -> Self { + Self { + buffer: view.buffer(), + offset, + marker: PhantomData, + } + } + + /// Get the offset into Wasm linear memory for this `WasmRef`. + #[inline] + pub fn offset(self) -> u64 { + self.offset + } + + /// Get a `WasmPtr` for this `WasmRef`. + #[inline] + pub fn as_ptr32(self) -> WasmPtr { + WasmPtr::new(self.offset as u32) + } + + /// Get a 64-bit `WasmPtr` for this `WasmRef`. + #[inline] + pub fn as_ptr64(self) -> WasmPtr { + WasmPtr::new(self.offset) + } + + /// Get a `WasmPtr` for this `WasmRef`. + #[inline] + pub fn as_ptr(self) -> WasmPtr { + let offset: M::Offset = self + .offset + .try_into() + .map_err(|_| "invalid offset into memory") + .unwrap(); + WasmPtr::::new(offset) + } + + /// Reads the location pointed to by this `WasmRef`. + #[inline] + pub fn read(self) -> Result { + let mut out = MaybeUninit::uninit(); + let buf = + unsafe { slice::from_raw_parts_mut(out.as_mut_ptr() as *mut u8, mem::size_of::()) }; + self.buffer.read(self.offset, buf)?; + Ok(unsafe { out.assume_init() }) + // Ok(self.access()?.read()) + } + + /// Writes to the location pointed to by this `WasmRef`. + #[inline] + pub fn write(self, val: T) -> Result<(), MemoryAccessError> { + self.access()?.write(val); + Ok(()) + } + + /// Gains direct access to the memory of this slice + #[inline] + pub fn access(self) -> Result, MemoryAccessError> { + WasmRefAccess::new(self) + } +} + +impl<'a, T: ValueType> fmt::Debug for WasmRef<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "WasmRef(offset: {}, pointer: {:#x})", + self.offset, self.offset + ) + } +} + +/// Reference to an array of values in Wasm memory. +/// +/// The type of the value must satisfy the requirements of the `ValueType` +/// trait which guarantees that reading and writing such a value to untrusted +/// memory is safe. +/// +/// The address is not required to be aligned: unaligned accesses are fully +/// supported. +/// +/// This wrapper safely handles concurrent modifications of the data by another +/// thread. +#[derive(Clone, Copy)] +pub struct WasmSlice<'a, T: ValueType> { + pub(crate) buffer: MemoryBuffer<'a>, + pub(crate) offset: u64, + pub(crate) len: u64, + marker: PhantomData<*mut T>, +} + +impl<'a, T: ValueType> WasmSlice<'a, T> { + /// Creates a new `WasmSlice` starting at the given offset in memory and + /// with the given number of elements. + /// + /// Returns a `MemoryAccessError` if the slice length overflows. + #[inline] + pub fn new(view: &'a MemoryView, offset: u64, len: u64) -> Result { + let total_len = len + .checked_mul(mem::size_of::() as u64) + .ok_or(MemoryAccessError::Overflow)?; + offset + .checked_add(total_len) + .ok_or(MemoryAccessError::Overflow)?; + Ok(Self { + buffer: view.buffer(), + offset, + len, + marker: PhantomData, + }) + } + + /// Get the offset into Wasm linear memory for this `WasmSlice`. + #[inline] + pub fn offset(self) -> u64 { + self.offset + } + + /// Get a 32-bit `WasmPtr` for this `WasmRef`. + #[inline] + pub fn as_ptr32(self) -> WasmPtr { + WasmPtr::new(self.offset as u32) + } + + /// Get a 64-bit `WasmPtr` for this `WasmRef`. + #[inline] + pub fn as_ptr64(self) -> WasmPtr { + WasmPtr::new(self.offset) + } + + /// Get the number of elements in this slice. + #[inline] + pub fn len(self) -> u64 { + self.len + } + + /// Returns `true` if the number of elements is 0. + #[inline] + pub fn is_empty(self) -> bool { + self.len == 0 + } + + /// Get a `WasmRef` to an element in the slice. + #[inline] + pub fn index(self, idx: u64) -> WasmRef<'a, T> { + if idx >= self.len { + panic!("WasmSlice out of bounds"); + } + let offset = self.offset + idx * mem::size_of::() as u64; + WasmRef { + buffer: self.buffer, + offset, + marker: PhantomData, + } + } + + /// Get a `WasmSlice` for a subslice of this slice. + #[inline] + pub fn subslice(self, range: Range) -> WasmSlice<'a, T> { + if range.start > range.end || range.end > self.len { + panic!("WasmSlice out of bounds"); + } + let offset = self.offset + range.start * mem::size_of::() as u64; + Self { + buffer: self.buffer, + offset, + len: range.end - range.start, + marker: PhantomData, + } + } + + /// Get an iterator over the elements in this slice. + #[inline] + pub fn iter(self) -> WasmSliceIter<'a, T> { + WasmSliceIter { slice: self } + } + + /// Gains direct access to the memory of this slice + #[inline] + pub fn access(self) -> Result, MemoryAccessError> { + WasmSliceAccess::new(self) + } + + /// Reads an element of this slice. + #[inline] + pub fn read(self, idx: u64) -> Result { + self.index(idx).read() + } + + /// Writes to an element of this slice. + #[inline] + pub fn write(self, idx: u64, val: T) -> Result<(), MemoryAccessError> { + self.index(idx).write(val) + } + + /// Reads the entire slice into the given buffer. + /// + /// The length of the buffer must match the length of the slice. + #[inline] + pub fn read_slice(self, buf: &mut [T]) -> Result<(), MemoryAccessError> { + assert_eq!( + buf.len() as u64, + self.len, + "slice length doesn't match WasmSlice length" + ); + let size = std::mem::size_of_val(buf); + let bytes = + unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut MaybeUninit, size) }; + self.buffer.read_uninit(self.offset, bytes)?; + Ok(()) + } + + /// Reads the entire slice into the given uninitialized buffer. + /// + /// The length of the buffer must match the length of the slice. + /// + /// This method returns an initialized view of the buffer. + #[inline] + pub fn read_slice_uninit( + self, + buf: &mut [MaybeUninit], + ) -> Result<&mut [T], MemoryAccessError> { + assert_eq!( + buf.len() as u64, + self.len, + "slice length doesn't match WasmSlice length" + ); + let bytes = unsafe { + slice::from_raw_parts_mut( + buf.as_mut_ptr() as *mut MaybeUninit, + buf.len() * mem::size_of::(), + ) + }; + self.buffer.read_uninit(self.offset, bytes)?; + Ok(unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut T, buf.len()) }) + } + + /// Write the given slice into this `WasmSlice`. + /// + /// The length of the slice must match the length of the `WasmSlice`. + #[inline] + pub fn write_slice(self, data: &[T]) -> Result<(), MemoryAccessError> { + assert_eq!( + data.len() as u64, + self.len, + "slice length doesn't match WasmSlice length" + ); + let size = std::mem::size_of_val(data); + let bytes = unsafe { slice::from_raw_parts(data.as_ptr() as *const u8, size) }; + self.buffer.write(self.offset, bytes) + } + + /// Reads this `WasmSlice` into a `slice`. + #[inline] + pub fn read_to_slice(self, buf: &mut [MaybeUninit]) -> Result { + let len = self.len.try_into().expect("WasmSlice length overflow"); + self.buffer.read_uninit(self.offset, buf)?; + Ok(len) + } + + /// Reads this `WasmSlice` into a `Vec`. + #[inline] + pub fn read_to_vec(self) -> Result, MemoryAccessError> { + let len = self.len.try_into().expect("WasmSlice length overflow"); + let mut vec = Vec::with_capacity(len); + let bytes = unsafe { + slice::from_raw_parts_mut( + vec.as_mut_ptr() as *mut MaybeUninit, + len * mem::size_of::(), + ) + }; + self.buffer.read_uninit(self.offset, bytes)?; + unsafe { + vec.set_len(len); + } + Ok(vec) + } + + /// Reads this `WasmSlice` into a `BytesMut` + #[inline] + pub fn read_to_bytes(self) -> Result { + let len = self.len.try_into().expect("WasmSlice length overflow"); + let mut ret = bytes::BytesMut::with_capacity(len); + let bytes = unsafe { + slice::from_raw_parts_mut( + ret.as_mut_ptr() as *mut MaybeUninit, + len * mem::size_of::(), + ) + }; + self.buffer.read_uninit(self.offset, bytes)?; + unsafe { + ret.set_len(len); + } + Ok(ret) + } +} + +impl<'a, T: ValueType> fmt::Debug for WasmSlice<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "WasmSlice(offset: {}, len: {}, pointer: {:#x})", + self.offset, self.len, self.offset + ) + } +} + +/// Iterator over the elements of a `WasmSlice`. +pub struct WasmSliceIter<'a, T: ValueType> { + slice: WasmSlice<'a, T>, +} + +impl<'a, T: ValueType> Iterator for WasmSliceIter<'a, T> { + type Item = WasmRef<'a, T>; + + fn next(&mut self) -> Option { + if !self.slice.is_empty() { + let elem = self.slice.index(0); + self.slice = self.slice.subslice(1..self.slice.len()); + Some(elem) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + (0..self.slice.len()).size_hint() + } +} + +impl<'a, T: ValueType> DoubleEndedIterator for WasmSliceIter<'a, T> { + fn next_back(&mut self) -> Option { + if !self.slice.is_empty() { + let elem = self.slice.index(self.slice.len() - 1); + self.slice = self.slice.subslice(0..self.slice.len() - 1); + Some(elem) + } else { + None + } + } +} + +impl<'a, T: ValueType> ExactSizeIterator for WasmSliceIter<'a, T> {} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/module.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/module.rs new file mode 100644 index 0000000..52bf98a --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/module.rs @@ -0,0 +1,484 @@ +use bytes::Bytes; +use std::fmt; +use std::fs; +use std::io; +use std::path::Path; + +use crate::engine::AsEngineRef; +use thiserror::Error; +#[cfg(feature = "wat")] +use wasmer_types::WasmError; +use wasmer_types::{ + CompileError, DeserializeError, ExportsIterator, ImportsIterator, ModuleInfo, SerializeError, +}; +use wasmer_types::{ExportType, ImportType}; + +use crate::into_bytes::IntoBytes; + +#[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 +#[derive(Error, Debug)] +pub enum IoCompileError { + /// An IO error + #[error(transparent)] + Io(#[from] 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. +#[derive(Clone, PartialEq, Eq)] +pub struct Module(pub(crate) module_imp::Module); + +impl Module { + /// Creates a new WebAssembly Module given the configuration + /// in the store. + /// + /// If the provided bytes are not WebAssembly-like (start with `b"\0asm"`), + /// and the "wat" feature is enabled for this crate, this function will try to + /// to convert the bytes assuming they correspond to the WebAssembly text + /// format. + /// + /// ## Security + /// + /// Before the code is compiled, it will be validated using the store + /// features. + /// + /// ## Errors + /// + /// Creating a WebAssembly module from bytecode can result in a + /// [`CompileError`] since this operation requires to transorm the Wasm + /// bytecode into code the machine can easily execute. + /// + /// ## Example + /// + /// Reading from a WAT file. + /// + /// ``` + /// use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// let wat = "(module)"; + /// let module = Module::new(&store, wat)?; + /// # Ok(()) + /// # } + /// ``` + /// + /// Reading from bytes: + /// + /// ``` + /// use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// // The following is the same as: + /// // (module + /// // (type $t0 (func (param i32) (result i32))) + /// // (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32) + /// // get_local $p0 + /// // i32.const 1 + /// // i32.add) + /// // ) + /// let bytes: Vec = vec![ + /// 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60, + /// 0x01, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x0b, 0x01, 0x07, + /// 0x61, 0x64, 0x64, 0x5f, 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x0a, 0x09, 0x01, + /// 0x07, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x0b, 0x00, 0x1a, 0x04, 0x6e, + /// 0x61, 0x6d, 0x65, 0x01, 0x0a, 0x01, 0x00, 0x07, 0x61, 0x64, 0x64, 0x5f, + /// 0x6f, 0x6e, 0x65, 0x02, 0x07, 0x01, 0x00, 0x01, 0x00, 0x02, 0x70, 0x30, + /// ]; + /// let module = Module::new(&store, bytes)?; + /// # Ok(()) + /// # } + /// ``` + /// # Example of loading a module using just an `Engine` and no `Store` + /// + /// ``` + /// # use wasmer::*; + /// # + /// # let engine: Engine = Cranelift::default().into(); + /// + /// let module = Module::from_file(&engine, "path/to/foo.wasm"); + /// ``` + 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 { + Ok(Self(module_imp::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 { + Ok(Self(module_imp::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> { + module_imp::Module::validate(engine, binary) + } + + /// 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 { + self.0.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.0.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 { + Ok(Self(module_imp::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 { + Ok(Self(module_imp::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 { + Ok(Self(module_imp::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 { + Ok(Self(module_imp::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> { + self.0.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 { + self.0.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 + '_> { + self.0.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 + '_> { + self.0.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 { + self.0.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 { + self.0.info() + } +} + +impl fmt::Debug for Module { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Module") + .field("name", &self.name()) + .finish() + } +} + +#[cfg(feature = "js")] +impl From for wasm_bindgen::JsValue { + fn from(value: Module) -> Self { + wasm_bindgen::JsValue::from(value.0) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/module_info_polyfill.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/module_info_polyfill.rs new file mode 100644 index 0000000..43c3aa3 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/module_info_polyfill.rs @@ -0,0 +1,626 @@ +//! Polyfill skeleton that traverses the whole WebAssembly module and +//! 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. +//! +//! +use core::convert::TryFrom; +use std::vec::Vec; +use wasmer_types::entity::EntityRef; +use wasmer_types::{ + ExportIndex, FunctionIndex, FunctionType, GlobalIndex, GlobalType, ImportIndex, MemoryIndex, + MemoryType, ModuleInfo, Pages, SignatureIndex, TableIndex, TableType, Type, +}; + +use wasmparser::{ + self, BinaryReaderError, Export, ExportSectionReader, ExternalKind, FunctionSectionReader, + GlobalSectionReader, GlobalType as WPGlobalType, ImportSectionReader, MemorySectionReader, + MemoryType as WPMemoryType, NameSectionReader, Parser, Payload, TableSectionReader, TypeRef, + TypeSectionReader, +}; + +pub type WasmResult = Result; + +#[derive(Default)] +pub struct ModuleInfoPolyfill { + pub(crate) info: ModuleInfo, +} + +impl ModuleInfoPolyfill { + pub(crate) fn declare_export(&mut self, export: ExportIndex, name: &str) -> WasmResult<()> { + self.info.exports.insert(String::from(name), export); + Ok(()) + } + + pub(crate) fn declare_import( + &mut self, + import: ImportIndex, + module: &str, + field: &str, + ) -> WasmResult<()> { + self.info.imports.insert( + wasmer_types::ImportKey { + module: String::from(module), + field: String::from(field), + import_idx: self.info.imports.len() as u32, + }, + import, + ); + Ok(()) + } + + pub(crate) fn reserve_signatures(&mut self, num: u32) -> WasmResult<()> { + self.info + .signatures + .reserve_exact(usize::try_from(num).unwrap()); + Ok(()) + } + + pub(crate) fn declare_signature(&mut self, sig: FunctionType) -> WasmResult<()> { + self.info.signatures.push(sig); + Ok(()) + } + + pub(crate) fn declare_func_import( + &mut self, + sig_index: SignatureIndex, + module: &str, + field: &str, + ) -> WasmResult<()> { + debug_assert_eq!( + self.info.functions.len(), + self.info.num_imported_functions, + "Imported functions must be declared first" + ); + self.declare_import( + ImportIndex::Function(FunctionIndex::from_u32( + self.info.num_imported_functions as _, + )), + module, + field, + )?; + self.info.functions.push(sig_index); + self.info.num_imported_functions += 1; + Ok(()) + } + + pub(crate) fn declare_table_import( + &mut self, + table: TableType, + module: &str, + field: &str, + ) -> WasmResult<()> { + debug_assert_eq!( + self.info.tables.len(), + self.info.num_imported_tables, + "Imported tables must be declared first" + ); + self.declare_import( + ImportIndex::Table(TableIndex::from_u32(self.info.num_imported_tables as _)), + module, + field, + )?; + self.info.tables.push(table); + self.info.num_imported_tables += 1; + Ok(()) + } + + pub(crate) fn declare_memory_import( + &mut self, + memory: MemoryType, + module: &str, + field: &str, + ) -> WasmResult<()> { + debug_assert_eq!( + self.info.memories.len(), + self.info.num_imported_memories, + "Imported memories must be declared first" + ); + self.declare_import( + ImportIndex::Memory(MemoryIndex::from_u32(self.info.num_imported_memories as _)), + module, + field, + )?; + self.info.memories.push(memory); + self.info.num_imported_memories += 1; + Ok(()) + } + + pub(crate) fn declare_global_import( + &mut self, + global: GlobalType, + module: &str, + field: &str, + ) -> WasmResult<()> { + debug_assert_eq!( + self.info.globals.len(), + self.info.num_imported_globals, + "Imported globals must be declared first" + ); + self.declare_import( + ImportIndex::Global(GlobalIndex::from_u32(self.info.num_imported_globals as _)), + module, + field, + )?; + self.info.globals.push(global); + self.info.num_imported_globals += 1; + Ok(()) + } + + pub(crate) fn reserve_func_types(&mut self, num: u32) -> WasmResult<()> { + self.info + .functions + .reserve_exact(usize::try_from(num).unwrap()); + Ok(()) + } + + pub(crate) fn declare_func_type(&mut self, sig_index: SignatureIndex) -> WasmResult<()> { + self.info.functions.push(sig_index); + Ok(()) + } + + pub(crate) fn reserve_tables(&mut self, num: u32) -> WasmResult<()> { + self.info + .tables + .reserve_exact(usize::try_from(num).unwrap()); + Ok(()) + } + + pub(crate) fn declare_table(&mut self, table: TableType) -> WasmResult<()> { + self.info.tables.push(table); + Ok(()) + } + + pub(crate) fn reserve_memories(&mut self, num: u32) -> WasmResult<()> { + self.info + .memories + .reserve_exact(usize::try_from(num).unwrap()); + Ok(()) + } + + pub(crate) fn declare_memory(&mut self, memory: MemoryType) -> WasmResult<()> { + self.info.memories.push(memory); + Ok(()) + } + + pub(crate) fn reserve_globals(&mut self, num: u32) -> WasmResult<()> { + self.info + .globals + .reserve_exact(usize::try_from(num).unwrap()); + Ok(()) + } + + pub(crate) fn declare_global(&mut self, global: GlobalType) -> WasmResult<()> { + self.info.globals.push(global); + Ok(()) + } + + pub(crate) fn reserve_exports(&mut self, num: u32) -> WasmResult<()> { + self.info.exports.reserve(usize::try_from(num).unwrap()); + Ok(()) + } + + pub(crate) fn reserve_imports(&mut self, num: u32) -> WasmResult<()> { + self.info.imports.reserve(usize::try_from(num).unwrap()); + Ok(()) + } + + pub(crate) fn declare_func_export( + &mut self, + func_index: FunctionIndex, + name: &str, + ) -> WasmResult<()> { + self.declare_export(ExportIndex::Function(func_index), name) + } + + pub(crate) fn declare_table_export( + &mut self, + table_index: TableIndex, + name: &str, + ) -> WasmResult<()> { + self.declare_export(ExportIndex::Table(table_index), name) + } + + pub(crate) fn declare_memory_export( + &mut self, + memory_index: MemoryIndex, + name: &str, + ) -> WasmResult<()> { + self.declare_export(ExportIndex::Memory(memory_index), name) + } + + pub(crate) fn declare_global_export( + &mut self, + global_index: GlobalIndex, + name: &str, + ) -> WasmResult<()> { + self.declare_export(ExportIndex::Global(global_index), name) + } + + pub(crate) fn declare_module_name(&mut self, name: &str) -> WasmResult<()> { + self.info.name = Some(name.to_string()); + Ok(()) + } +} + +fn transform_err(err: BinaryReaderError) -> String { + err.message().into() +} + +/// Translate a sequence of bytes forming a valid Wasm binary into a +/// parsed ModuleInfo `ModuleInfoPolyfill`. +pub fn translate_module<'data>(data: &'data [u8]) -> WasmResult { + let mut module_info: ModuleInfoPolyfill = Default::default(); + + for payload in Parser::new(0).parse_all(data) { + match payload.map_err(transform_err)? { + Payload::TypeSection(types) => { + parse_type_section(types, &mut module_info)?; + } + + Payload::ImportSection(imports) => { + parse_import_section(imports, &mut module_info)?; + } + + Payload::FunctionSection(functions) => { + parse_function_section(functions, &mut module_info)?; + } + + Payload::TableSection(tables) => { + parse_table_section(tables, &mut module_info)?; + } + + Payload::MemorySection(memories) => { + parse_memory_section(memories, &mut module_info)?; + } + + Payload::GlobalSection(globals) => { + parse_global_section(globals, &mut module_info)?; + } + + Payload::ExportSection(exports) => { + parse_export_section(exports, &mut module_info)?; + } + + Payload::CustomSection(sectionreader) => { + // We still add the custom section data, but also read it as name section reader + let name = sectionreader.name(); + if name == "name" { + parse_name_section( + NameSectionReader::new(sectionreader.data(), sectionreader.data_offset()), + &mut module_info, + )?; + } + } + + _ => {} + } + } + + Ok(module_info) +} + +/// Helper function translating wasmparser types to Wasm Type. +pub fn wptype_to_type(ty: wasmparser::ValType) -> WasmResult { + match ty { + wasmparser::ValType::I32 => Ok(Type::I32), + wasmparser::ValType::I64 => Ok(Type::I64), + wasmparser::ValType::F32 => Ok(Type::F32), + wasmparser::ValType::F64 => Ok(Type::F64), + wasmparser::ValType::V128 => Ok(Type::V128), + wasmparser::ValType::Ref(ty) => wpreftype_to_type(ty), + } +} + +/// Converts a wasmparser ref type to a [`Type`]. +pub fn wpreftype_to_type(ty: wasmparser::RefType) -> WasmResult { + if ty.is_extern_ref() { + Ok(Type::ExternRef) + } else if ty.is_func_ref() { + Ok(Type::FuncRef) + } else { + Err(format!("Unsupported ref type: {:?}", ty)) + } +} + +/// Parses the Type section of the wasm module. +pub fn parse_type_section( + reader: TypeSectionReader, + module_info: &mut ModuleInfoPolyfill, +) -> WasmResult<()> { + module_info.reserve_signatures(reader.count())?; + + for res in reader { + let group = res.map_err(transform_err)?; + + for ty in group.into_types() { + match ty.composite_type { + wasmparser::CompositeType::Func(functype) => { + let params = functype.params(); + let returns = functype.results(); + let sig_params: Vec = params + .iter() + .map(|ty| { + wptype_to_type(*ty) + .expect("only numeric types are supported in function signatures") + }) + .collect(); + let sig_returns: Vec = returns + .iter() + .map(|ty| { + wptype_to_type(*ty) + .expect("only numeric types are supported in function signatures") + }) + .collect(); + let sig = FunctionType::new(sig_params, sig_returns); + module_info.declare_signature(sig)?; + } + _ => { + unimplemented!("GC is not implemented yet") + } + } + } + } + + Ok(()) +} + +/// Parses the Import section of the wasm module. +pub fn parse_import_section<'data>( + imports: ImportSectionReader<'data>, + module_info: &mut ModuleInfoPolyfill, +) -> WasmResult<()> { + module_info.reserve_imports(imports.count())?; + + for entry in imports { + let import = entry.map_err(transform_err)?; + let module_name = import.module; + let field_name = import.name; + + match import.ty { + TypeRef::Func(sig) => { + module_info.declare_func_import( + SignatureIndex::from_u32(sig), + module_name, + field_name, + )?; + } + TypeRef::Tag(_) => { + unimplemented!("exception handling not implemented yet") + } + TypeRef::Memory(WPMemoryType { + shared, + memory64, + initial, + maximum, + }) => { + if memory64 { + unimplemented!("64bit memory not implemented yet"); + } + module_info.declare_memory_import( + MemoryType { + minimum: Pages(initial as u32), + maximum: maximum.map(|p| Pages(p as u32)), + shared, + }, + module_name, + field_name, + )?; + } + TypeRef::Global(ref ty) => { + module_info.declare_global_import( + GlobalType { + ty: wptype_to_type(ty.content_type).unwrap(), + mutability: ty.mutable.into(), + }, + module_name, + field_name, + )?; + } + TypeRef::Table(ref tab) => { + module_info.declare_table_import( + TableType { + ty: wpreftype_to_type(tab.element_type).unwrap(), + minimum: tab.initial, + maximum: tab.maximum, + }, + module_name, + field_name, + )?; + } + } + } + Ok(()) +} + +/// Parses the Function section of the wasm module. +pub fn parse_function_section( + functions: FunctionSectionReader, + module_info: &mut ModuleInfoPolyfill, +) -> WasmResult<()> { + let num_functions = functions.count(); + module_info.reserve_func_types(num_functions)?; + + for entry in functions { + let sigindex = entry.map_err(transform_err)?; + module_info.declare_func_type(SignatureIndex::from_u32(sigindex))?; + } + + Ok(()) +} + +/// Parses the Table section of the wasm module. +pub fn parse_table_section( + tables: TableSectionReader, + module_info: &mut ModuleInfoPolyfill, +) -> WasmResult<()> { + module_info.reserve_tables(tables.count())?; + + for entry in tables { + let table = entry.map_err(transform_err)?; + module_info.declare_table(TableType { + ty: wpreftype_to_type(table.ty.element_type).unwrap(), + minimum: table.ty.initial, + maximum: table.ty.maximum, + })?; + } + + Ok(()) +} + +/// Parses the Memory section of the wasm module. +pub fn parse_memory_section( + memories: MemorySectionReader, + module_info: &mut ModuleInfoPolyfill, +) -> WasmResult<()> { + module_info.reserve_memories(memories.count())?; + + for entry in memories { + let WPMemoryType { + shared, + memory64, + initial, + maximum, + } = entry.map_err(transform_err)?; + if memory64 { + unimplemented!("64bit memory not implemented yet"); + } + module_info.declare_memory(MemoryType { + minimum: Pages(initial as u32), + maximum: maximum.map(|p| Pages(p as u32)), + shared, + })?; + } + + Ok(()) +} + +/// Parses the Global section of the wasm module. +pub fn parse_global_section( + globals: GlobalSectionReader, + module_info: &mut ModuleInfoPolyfill, +) -> WasmResult<()> { + module_info.reserve_globals(globals.count())?; + + for entry in globals { + let WPGlobalType { + content_type, + mutable, + } = entry.map_err(transform_err)?.ty; + let global = GlobalType { + ty: wptype_to_type(content_type).unwrap(), + mutability: mutable.into(), + }; + module_info.declare_global(global)?; + } + + Ok(()) +} + +/// Parses the Export section of the wasm module. +pub fn parse_export_section<'data>( + exports: ExportSectionReader<'data>, + module_info: &mut ModuleInfoPolyfill, +) -> WasmResult<()> { + module_info.reserve_exports(exports.count())?; + + for entry in exports { + let Export { + name, + ref kind, + index, + } = entry.map_err(transform_err)?; + + // The input has already been validated, so we should be able to + // assume valid UTF-8 and use `from_utf8_unchecked` if performance + // becomes a concern here. + let index = index as usize; + match *kind { + ExternalKind::Func => { + module_info.declare_func_export(FunctionIndex::new(index), name)? + } + ExternalKind::Table => { + module_info.declare_table_export(TableIndex::new(index), name)? + } + ExternalKind::Memory => { + module_info.declare_memory_export(MemoryIndex::new(index), name)? + } + ExternalKind::Global => { + module_info.declare_global_export(GlobalIndex::new(index), name)? + } + ExternalKind::Tag => { + unimplemented!("exception handling not implemented yet") + } + } + } + Ok(()) +} + +// /// Parses the Start section of the wasm module. +// pub fn parse_start_section(index: u32, module_info: &mut ModuleInfoPolyfill) -> WasmResult<()> { +// module_info.declare_start_function(FunctionIndex::from_u32(index))?; +// Ok(()) +// } + +/// Parses the Name section of the wasm module. +pub fn parse_name_section<'data>( + mut names: NameSectionReader<'data>, + module_info: &mut ModuleInfoPolyfill, +) -> WasmResult<()> { + while let Some(Ok(subsection)) = names.next() { + match subsection { + wasmparser::Name::Function(_function_subsection) => { + //for naming in function_subsection.into_iter().flatten() { + // if naming.index != std::u32::MAX { + // environ.declare_function_name( + // FunctionIndex::from_u32(naming.index), + // naming.name, + // )?; + // } + //} + } + wasmparser::Name::Module { + name, + name_range: _, + } => { + module_info.declare_module_name(name)?; + } + wasmparser::Name::Local(_) => {} + wasmparser::Name::Label(_) + | wasmparser::Name::Type(_) + | wasmparser::Name::Table(_) + | wasmparser::Name::Memory(_) + | wasmparser::Name::Global(_) + | wasmparser::Name::Element(_) + | wasmparser::Name::Data(_) + | wasmparser::Name::Tag(_) + | wasmparser::Name::Unknown { .. } => {} + }; + } + Ok(()) +} + +// fn parse_function_name_subsection( +// mut naming_reader: NamingReader<'_>, +// ) -> Option> { +// let mut function_names = HashMap::new(); +// for _ in 0..naming_reader.count() { +// let Naming { index, name } = naming_reader.read().ok()?; +// if index == std::u32::MAX { +// // We reserve `u32::MAX` for our own use. +// return None; +// } + +// if function_names +// .insert(FunctionIndex::from_u32(index), name) +// .is_some() +// { +// // If the function index has been previously seen, then we +// // break out of the loop and early return `None`, because these +// // should be unique. +// return None; +// } +// } +// Some(function_names) +// } diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/native_type.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/native_type.rs new file mode 100644 index 0000000..5879ff3 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/native_type.rs @@ -0,0 +1,966 @@ +//! This module permits to create native functions +//! easily in Rust, thanks to its advanced typing system. + +use wasmer_types::{NativeWasmType, RawValue, Type}; + +use crate::store::AsStoreRef; +use crate::vm::{VMExternRef, VMFuncRef}; + +use crate::{ExternRef, Function, TypedFunction}; +use std::array::TryFromSliceError; +use std::convert::Infallible; +use std::convert::TryInto; +use std::error::Error; + +use crate::store::AsStoreMut; + +/// `NativeWasmTypeInto` performs conversions from and into `NativeWasmType` +/// types with a context. +pub trait NativeWasmTypeInto: NativeWasmType + Sized { + #[doc(hidden)] + fn into_abi(self, store: &mut impl AsStoreMut) -> Self::Abi; + + #[doc(hidden)] + unsafe fn from_abi(store: &mut impl AsStoreMut, abi: Self::Abi) -> Self; + + /// Convert self to raw value representation. + fn into_raw(self, store: &mut impl AsStoreMut) -> RawValue; + + /// Convert to self from raw value representation. + /// + /// # Safety + /// + unsafe fn from_raw(store: &mut impl AsStoreMut, raw: RawValue) -> Self; +} + +impl NativeWasmTypeInto for i32 { + #[inline] + unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { + self + } + + #[inline] + fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { + RawValue { i32: self } + } + + #[inline] + unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: RawValue) -> Self { + raw.i32 + } +} + +impl NativeWasmTypeInto for u32 { + #[inline] + unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { + self + } + + #[inline] + fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { + RawValue { i32: self as _ } + } + + #[inline] + unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: RawValue) -> Self { + raw.i32 as _ + } +} + +impl NativeWasmTypeInto for i64 { + #[inline] + unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { + self + } + + #[inline] + fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { + RawValue { i64: self } + } + + #[inline] + unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: RawValue) -> Self { + raw.i64 + } +} + +impl NativeWasmTypeInto for u64 { + #[inline] + unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { + self + } + + #[inline] + fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { + RawValue { i64: self as _ } + } + + #[inline] + unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: RawValue) -> Self { + raw.i64 as _ + } +} + +impl NativeWasmTypeInto for f32 { + #[inline] + unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { + self + } + + #[inline] + fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { + RawValue { f32: self } + } + + #[inline] + unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: RawValue) -> Self { + raw.f32 + } +} + +impl NativeWasmTypeInto for f64 { + #[inline] + unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { + self + } + + #[inline] + fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { + RawValue { f64: self } + } + + #[inline] + unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: RawValue) -> Self { + raw.f64 + } +} + +impl NativeWasmTypeInto for u128 { + #[inline] + unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { + self + } + + #[inline] + fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { + RawValue { u128: self } + } + + #[inline] + unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: RawValue) -> Self { + raw.u128 + } +} + +impl NativeWasmType for ExternRef { + const WASM_TYPE: Type = Type::ExternRef; + type Abi = usize; +} + +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)) + } + + #[inline] + fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi { + self.map_or(0, |e| unsafe { e.vm_externref().into_raw().externref }) + } + + #[inline] + fn into_raw(self, _store: &mut impl AsStoreMut) -> RawValue { + self.map_or(RawValue { externref: 0 }, |e| e.vm_externref().into_raw()) + } + + #[inline] + unsafe fn from_raw(store: &mut impl AsStoreMut, raw: RawValue) -> Self { + VMExternRef::from_raw(raw).map(|e| ExternRef::from_vm_externref(store, e)) + } +} + +impl From> for Function +where + Args: WasmTypeList, + Rets: WasmTypeList, +{ + fn from(other: TypedFunction) -> Self { + other.into_function() + } +} + +impl NativeWasmType for Function { + const WASM_TYPE: Type = Type::FuncRef; + type Abi = usize; +} + +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)) + } + + #[inline] + fn into_abi(self, store: &mut impl AsStoreMut) -> Self::Abi { + self.map_or(0, |f| unsafe { f.vm_funcref(store).into_raw().externref }) + } + + #[inline] + fn into_raw(self, store: &mut impl AsStoreMut) -> RawValue { + self.map_or(RawValue { externref: 0 }, |e| { + e.vm_funcref(store).into_raw() + }) + } + + #[inline] + unsafe fn from_raw(store: &mut impl AsStoreMut, raw: RawValue) -> Self { + VMFuncRef::from_raw(raw).map(|f| Function::from_vm_funcref(store, f)) + } +} + +/// A trait to convert a Rust value to a `WasmNativeType` value, +/// or to convert `WasmNativeType` value to a Rust value. +/// +/// This trait should ideally be split into two traits: +/// `FromNativeWasmType` and `ToNativeWasmType` but it creates a +/// non-negligible complexity in the `WasmTypeList` +/// implementation. +/// +/// # Safety +/// This trait is unsafe given the nature of how values are written and read from the native +/// stack +pub unsafe trait FromToNativeWasmType +where + Self: Sized, +{ + /// Native Wasm type. + type Native: NativeWasmTypeInto; + + /// Convert a value of kind `Self::Native` to `Self`. + /// + /// # Panics + /// + /// This method panics if `native` cannot fit in the `Self` + /// type`. + fn from_native(native: Self::Native) -> Self; + + /// Convert self to `Self::Native`. + /// + /// # Panics + /// + /// This method panics if `self` cannot fit in the + /// `Self::Native` type. + fn to_native(self) -> Self::Native; + + /// Returns whether the given value is from the given store. + /// + /// This always returns true for primitive types that can be used with + /// any context. + fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true + } +} + +macro_rules! from_to_native_wasm_type { + ( $( $type:ty => $native_type:ty ),* ) => { + $( + #[allow(clippy::use_self)] + unsafe impl FromToNativeWasmType for $type { + type Native = $native_type; + + #[inline] + fn from_native(native: Self::Native) -> Self { + native as Self + } + + #[inline] + fn to_native(self) -> Self::Native { + self as Self::Native + } + } + )* + }; +} + +macro_rules! from_to_native_wasm_type_same_size { + ( $( $type:ty => $native_type:ty ),* ) => { + $( + #[allow(clippy::use_self)] + unsafe impl FromToNativeWasmType for $type { + type Native = $native_type; + + #[inline] + fn from_native(native: Self::Native) -> Self { + Self::from_ne_bytes(Self::Native::to_ne_bytes(native)) + } + + #[inline] + fn to_native(self) -> Self::Native { + Self::Native::from_ne_bytes(Self::to_ne_bytes(self)) + } + } + )* + }; +} + +from_to_native_wasm_type!( + i8 => i32, + u8 => i32, + i16 => i32, + u16 => i32 +); + +from_to_native_wasm_type_same_size!( + i32 => i32, + u32 => i32, + i64 => i64, + u64 => i64, + f32 => f32, + f64 => f64 +); + +unsafe impl FromToNativeWasmType for Option { + type Native = Self; + + fn to_native(self) -> Self::Native { + self + } + fn from_native(n: Self::Native) -> Self { + n + } + fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + self.as_ref().map_or(true, |e| e.is_from_store(store)) + } +} + +unsafe impl FromToNativeWasmType for Option { + type Native = Self; + + fn to_native(self) -> Self::Native { + self + } + fn from_native(n: Self::Native) -> Self { + n + } + fn is_from_store(&self, store: &impl AsStoreRef) -> bool { + self.as_ref().map_or(true, |f| f.is_from_store(store)) + } +} + +#[cfg(test)] +mod test_from_to_native_wasm_type { + use super::*; + + #[test] + fn test_to_native() { + assert_eq!(7i8.to_native(), 7i32); + assert_eq!(7u8.to_native(), 7i32); + assert_eq!(7i16.to_native(), 7i32); + assert_eq!(7u16.to_native(), 7i32); + assert_eq!(u32::MAX.to_native(), -1); + } + + #[test] + fn test_to_native_same_size() { + assert_eq!(7i32.to_native(), 7i32); + assert_eq!(7u32.to_native(), 7i32); + assert_eq!(7i64.to_native(), 7i64); + assert_eq!(7u64.to_native(), 7i64); + assert_eq!(7f32.to_native(), 7f32); + assert_eq!(7f64.to_native(), 7f64); + } +} + +/// The `WasmTypeList` trait represents a tuple (list) of Wasm +/// typed values. It is used to get low-level representation of +/// such a tuple. +pub trait WasmTypeList +where + Self: Sized, +{ + /// The C type (a struct) that can hold/represent all the + /// represented values. + type CStruct; + + /// The array type that can hold all the represented values. + /// + /// Note that all values are stored in their binary form. + type Array: AsMut<[RawValue]>; + + /// The size of the array + fn size() -> u32; + + /// Constructs `Self` based on an array of values. + /// + /// # Safety + unsafe fn from_array(store: &mut impl AsStoreMut, array: Self::Array) -> Self; + + /// Constructs `Self` based on a slice of values. + /// + /// `from_slice` returns a `Result` because it is possible + /// that the slice doesn't have the same size than + /// `Self::Array`, in which circumstance an error of kind + /// `TryFromSliceError` will be returned. + /// + /// # Safety + unsafe fn from_slice( + store: &mut impl AsStoreMut, + slice: &[RawValue], + ) -> Result; + + /// Builds and returns an array of type `Array` from a tuple + /// (list) of values. + /// + /// # Safety + unsafe fn into_array(self, store: &mut impl AsStoreMut) -> Self::Array; + + /// Allocates and return an empty array of type `Array` that + /// will hold a tuple (list) of values, usually to hold the + /// returned values of a WebAssembly function call. + fn empty_array() -> Self::Array; + + /// Builds a tuple (list) of values from a C struct of type + /// `CStruct`. + /// + /// # Safety + unsafe fn from_c_struct(store: &mut impl AsStoreMut, c_struct: Self::CStruct) -> Self; + + /// Builds and returns a C struct of type `CStruct` from a + /// tuple (list) of values. + /// + /// # Safety + unsafe fn into_c_struct(self, store: &mut impl AsStoreMut) -> Self::CStruct; + + /// Writes the contents of a C struct to an array of `RawValue`. + /// + /// # Safety + unsafe fn write_c_struct_to_ptr(c_struct: Self::CStruct, ptr: *mut RawValue); + + /// Get the Wasm types for the tuple (list) of currently + /// represented values. + fn wasm_types() -> &'static [Type]; +} + +/// The `IntoResult` trait turns a `WasmTypeList` into a +/// `Result`. +/// +/// It is mostly used to turn result values of a Wasm function +/// call into a `Result`. +pub trait IntoResult +where + T: WasmTypeList, +{ + /// The error type for this trait. + type Error: Error + Sync + Send + 'static; + + /// Transforms `Self` into a `Result`. + fn into_result(self) -> Result; +} + +impl IntoResult for T +where + T: WasmTypeList, +{ + // `T` is not a `Result`, it's already a value, so no error + // can be built. + type Error = Infallible; + + fn into_result(self) -> Result { + Ok(self) + } +} + +impl IntoResult for Result +where + T: WasmTypeList, + E: Error + Sync + Send + 'static, +{ + type Error = E; + + fn into_result(self) -> Self { + self + } +} + +#[cfg(test)] +mod test_into_result { + use super::*; + use std::convert::Infallible; + + #[test] + fn test_into_result_over_t() { + let x: i32 = 42; + let result_of_x: Result = x.into_result(); + + assert_eq!(result_of_x.unwrap(), x); + } + + #[test] + fn test_into_result_over_result() { + { + let x: Result = Ok(42); + let result_of_x: Result = x.into_result(); + + assert_eq!(result_of_x, x); + } + + { + use std::{error, fmt}; + + #[derive(Debug, PartialEq)] + struct E; + + impl fmt::Display for E { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "E") + } + } + + impl error::Error for E {} + + let x: Result = Err(E); + let result_of_x: Result = x.into_result(); + + assert_eq!(result_of_x.unwrap_err(), E); + } + } +} + +// Implement `WasmTypeList` on `Infallible`, which means that +// `Infallible` can be used as a returned type of a host function +// to express that it doesn't return, or to express that it cannot +// fail (with `Result<_, Infallible>`). +impl WasmTypeList for Infallible { + type CStruct = Self; + type Array = [RawValue; 0]; + + fn size() -> u32 { + 0 + } + + unsafe fn from_array(_: &mut impl AsStoreMut, _: Self::Array) -> Self { + unreachable!() + } + + unsafe fn from_slice( + _: &mut impl AsStoreMut, + _: &[RawValue], + ) -> Result { + unreachable!() + } + + unsafe fn into_array(self, _: &mut impl AsStoreMut) -> Self::Array { + [] + } + + fn empty_array() -> Self::Array { + [] + } + + unsafe fn from_c_struct(_: &mut impl AsStoreMut, self_: Self::CStruct) -> Self { + self_ + } + + unsafe fn into_c_struct(self, _: &mut impl AsStoreMut) -> Self::CStruct { + self + } + + unsafe fn write_c_struct_to_ptr(_: Self::CStruct, _: *mut RawValue) {} + + fn wasm_types() -> &'static [Type] { + &[] + } +} + +macro_rules! impl_wasmtypelist { + ( [$c_struct_representation:ident] + $c_struct_name:ident, + $( $x:ident ),* ) => { + + /// A structure with a C-compatible representation that can hold a set of Wasm values. + /// This type is used by `WasmTypeList::CStruct`. + #[repr($c_struct_representation)] + pub struct $c_struct_name< $( $x ),* > ( $( <<$x as FromToNativeWasmType>::Native as NativeWasmType>::Abi ),* ) + where + $( $x: FromToNativeWasmType ),*; + + // Implement `WasmTypeList` for a specific tuple. + #[allow(unused_parens, dead_code)] + impl< $( $x ),* > + WasmTypeList + for + ( $( $x ),* ) + where + $( $x: FromToNativeWasmType ),* + { + type CStruct = $c_struct_name< $( $x ),* >; + + type Array = [RawValue; count_idents!( $( $x ),* )]; + + fn size() -> u32 { + count_idents!( $( $x ),* ) as _ + } + + #[allow(unused_mut)] + #[allow(clippy::unused_unit)] + #[allow(clippy::missing_safety_doc)] + unsafe fn from_array(mut _store: &mut impl AsStoreMut, array: Self::Array) -> Self { + // Unpack items of the array. + #[allow(non_snake_case)] + let [ $( $x ),* ] = array; + + // Build the tuple. + ( + $( + FromToNativeWasmType::from_native(NativeWasmTypeInto::from_raw(_store, $x)) + ),* + ) + } + + #[allow(clippy::missing_safety_doc)] + unsafe fn from_slice(store: &mut impl AsStoreMut, slice: &[RawValue]) -> Result { + Ok(Self::from_array(store, slice.try_into()?)) + } + + #[allow(unused_mut)] + #[allow(clippy::missing_safety_doc)] + unsafe fn into_array(self, mut _store: &mut impl AsStoreMut) -> Self::Array { + // Unpack items of the tuple. + #[allow(non_snake_case)] + let ( $( $x ),* ) = self; + + // Build the array. + [ + $( + FromToNativeWasmType::to_native($x).into_raw(_store) + ),* + ] + } + + fn empty_array() -> Self::Array { + // Build an array initialized with `0`. + [RawValue { i32: 0 }; count_idents!( $( $x ),* )] + } + + #[allow(unused_mut)] + #[allow(clippy::unused_unit)] + #[allow(clippy::missing_safety_doc)] + unsafe fn from_c_struct(mut _store: &mut impl AsStoreMut, c_struct: Self::CStruct) -> Self { + // Unpack items of the C structure. + #[allow(non_snake_case)] + let $c_struct_name( $( $x ),* ) = c_struct; + + ( + $( + FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(_store, $x)) + ),* + ) + } + + #[allow(unused_parens, non_snake_case, unused_mut)] + #[allow(clippy::missing_safety_doc)] + unsafe fn into_c_struct(self, mut _store: &mut impl AsStoreMut) -> Self::CStruct { + // Unpack items of the tuple. + let ( $( $x ),* ) = self; + + // Build the C structure. + $c_struct_name( + $( + FromToNativeWasmType::to_native($x).into_abi(_store) + ),* + ) + } + + #[allow(non_snake_case)] + unsafe fn write_c_struct_to_ptr(c_struct: Self::CStruct, _ptr: *mut RawValue) { + // Unpack items of the tuple. + let $c_struct_name( $( $x ),* ) = c_struct; + + let mut _n = 0; + $( + *_ptr.add(_n).cast() = $x; + _n += 1; + )* + } + + fn wasm_types() -> &'static [Type] { + &[ + $( + $x::Native::WASM_TYPE + ),* + ] + } + } + + }; +} + +// 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 and `WasmTypeList` +// implementations. +impl_wasmtypelist!([C] S0,); +impl_wasmtypelist!([transparent] S1, A1); +impl_wasmtypelist!([C] S2, A1, A2); +impl_wasmtypelist!([C] S3, A1, A2, A3); +impl_wasmtypelist!([C] S4, A1, A2, A3, A4); +impl_wasmtypelist!([C] S5, A1, A2, A3, A4, A5); +impl_wasmtypelist!([C] S6, A1, A2, A3, A4, A5, A6); +impl_wasmtypelist!([C] S7, A1, A2, A3, A4, A5, A6, A7); +impl_wasmtypelist!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8); +impl_wasmtypelist!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9); +impl_wasmtypelist!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); +impl_wasmtypelist!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); +impl_wasmtypelist!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); +impl_wasmtypelist!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); +impl_wasmtypelist!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); +impl_wasmtypelist!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); +impl_wasmtypelist!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16); +impl_wasmtypelist!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17); +impl_wasmtypelist!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18); +impl_wasmtypelist!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19); +impl_wasmtypelist!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20); +impl_wasmtypelist!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21); +impl_wasmtypelist!([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_wasmtypelist!([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_wasmtypelist!([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_wasmtypelist!([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_wasmtypelist!([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); + +#[cfg(test)] +mod test_wasm_type_list { + use super::*; + use wasmer_types::Type; + /* + #[test] + fn test_from_array() { + let mut store = Store::default(); + let env = FunctionEnv::new(&mut store, ()); + assert_eq!(<()>::from_array(&mut env, []), ()); + assert_eq!(::from_array(&mut env, [RawValue{i32: 1}]), (1i32)); + assert_eq!(<(i32, i64)>::from_array(&mut env, [RawValue{i32:1}, RawValue{i64:2}]), (1i32, 2i64)); + assert_eq!( + <(i32, i64, f32, f64)>::from_array(&mut env, [ + RawValue{i32:1}, + RawValue{i64:2}, + RawValue{f32: 3.1f32}, + RawValue{f64: 4.2f64} + ]), + (1, 2, 3.1f32, 4.2f64) + ); + } + + #[test] + fn test_into_array() { + let mut store = Store::default(); + let env = FunctionEnv::new(&mut store, ()); + assert_eq!(().into_array(&mut store), [0i128; 0]); + assert_eq!((1i32).into_array(&mut store), [1i32]); + assert_eq!((1i32, 2i64).into_array(&mut store), [RawValue{i32: 1}, RawValue{i64: 2}]); + assert_eq!( + (1i32, 2i32, 3.1f32, 4.2f64).into_array(&mut store), + [RawValue{i32: 1}, RawValue{i32: 2}, RawValue{ f32: 3.1f32}, RawValue{f64: 4.2f64}] + ); + } + */ + #[test] + fn test_empty_array() { + assert_eq!(<()>::empty_array().len(), 0); + assert_eq!(::empty_array().len(), 1); + assert_eq!(<(i32, i64)>::empty_array().len(), 2); + } + /* + #[test] + fn test_from_c_struct() { + let mut store = Store::default(); + let env = FunctionEnv::new(&mut store, ()); + assert_eq!(<()>::from_c_struct(&mut store, S0()), ()); + assert_eq!(::from_c_struct(&mut store, S1(1)), (1i32)); + assert_eq!(<(i32, i64)>::from_c_struct(&mut store, S2(1, 2)), (1i32, 2i64)); + assert_eq!( + <(i32, i64, f32, f64)>::from_c_struct(&mut store, S4(1, 2, 3.1, 4.2)), + (1i32, 2i64, 3.1f32, 4.2f64) + ); + } + */ + #[test] + fn test_wasm_types_for_uni_values() { + assert_eq!(::wasm_types(), [Type::I32]); + assert_eq!(::wasm_types(), [Type::I64]); + assert_eq!(::wasm_types(), [Type::F32]); + assert_eq!(::wasm_types(), [Type::F64]); + } + + #[test] + fn test_wasm_types_for_multi_values() { + assert_eq!(<(i32, i32)>::wasm_types(), [Type::I32, Type::I32]); + assert_eq!(<(i64, i64)>::wasm_types(), [Type::I64, Type::I64]); + assert_eq!(<(f32, f32)>::wasm_types(), [Type::F32, Type::F32]); + assert_eq!(<(f64, f64)>::wasm_types(), [Type::F64, Type::F64]); + + assert_eq!( + <(i32, i64, f32, f64)>::wasm_types(), + [Type::I32, Type::I64, Type::F32, Type::F64] + ); + } +} +/* + #[allow(non_snake_case)] + #[cfg(test)] + mod test_function { + use super::*; + use crate::Store; + use crate::FunctionEnv; + use wasmer_types::Type; + + fn func() {} + fn func__i32() -> i32 { + 0 + } + fn func_i32( _a: i32) {} + fn func_i32__i32( a: i32) -> i32 { + a * 2 + } + fn func_i32_i32__i32( a: i32, b: i32) -> i32 { + a + b + } + fn func_i32_i32__i32_i32( a: i32, b: i32) -> (i32, i32) { + (a, b) + } + fn func_f32_i32__i32_f32( a: f32, b: i32) -> (i32, f32) { + (b, a) + } + + #[test] + fn test_function_types() { + let mut store = Store::default(); + let env = FunctionEnv::new(&mut store, ()); + use wasmer_types::FunctionType; + assert_eq!( + StaticFunction::new(func).ty(&mut store), + FunctionType::new(vec![], vec![]) + ); + assert_eq!( + StaticFunction::new(func__i32).ty(&mut store), + FunctionType::new(vec![], vec![Type::I32]) + ); + assert_eq!( + StaticFunction::new(func_i32).ty(), + FunctionType::new(vec![Type::I32], vec![]) + ); + assert_eq!( + StaticFunction::new(func_i32__i32).ty(), + FunctionType::new(vec![Type::I32], vec![Type::I32]) + ); + assert_eq!( + StaticFunction::new(func_i32_i32__i32).ty(), + FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]) + ); + assert_eq!( + StaticFunction::new(func_i32_i32__i32_i32).ty(), + FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32, Type::I32]) + ); + assert_eq!( + StaticFunction::new(func_f32_i32__i32_f32).ty(), + FunctionType::new(vec![Type::F32, Type::I32], vec![Type::I32, Type::F32]) + ); + } + + #[test] + fn test_function_pointer() { + let f = StaticFunction::new(func_i32__i32); + let function = unsafe { std::mem::transmute::<_, fn(usize, i32) -> i32>(f.address) }; + assert_eq!(function(0, 3), 6); + } + } +*/ + +#[cfg(test)] +mod test_native_type { + use super::*; + use wasmer_types::Type; + + #[test] + fn test_wasm_types() { + assert_eq!(i32::WASM_TYPE, Type::I32); + assert_eq!(i64::WASM_TYPE, Type::I64); + assert_eq!(f32::WASM_TYPE, Type::F32); + assert_eq!(f64::WASM_TYPE, Type::F64); + assert_eq!(u128::WASM_TYPE, Type::V128); + } + /* + #[test] + fn test_roundtrip() { + unsafe { + assert_eq!(i32::from_raw(42i32.into_raw()), 42i32); + assert_eq!(i64::from_raw(42i64.into_raw()), 42i64); + assert_eq!(f32::from_raw(42f32.into_raw()), 42f32); + assert_eq!(f64::from_raw(42f64.into_raw()), 42f64); + assert_eq!(u128::from_raw(42u128.into_raw()), 42u128); + } + } + */ +} + +// pub trait IntegerAtomic +// where +// Self: Sized +// { +// type Primitive; + +// fn add(&self, other: Self::Primitive) -> Self::Primitive; +// fn sub(&self, other: Self::Primitive) -> Self::Primitive; +// fn and(&self, other: Self::Primitive) -> Self::Primitive; +// fn or(&self, other: Self::Primitive) -> Self::Primitive; +// fn xor(&self, other: Self::Primitive) -> Self::Primitive; +// fn load(&self) -> Self::Primitive; +// fn store(&self, other: Self::Primitive) -> Self::Primitive; +// fn compare_exchange(&self, expected: Self::Primitive, new: Self::Primitive) -> Self::Primitive; +// fn swap(&self, other: Self::Primitive) -> Self::Primitive; +// } diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/ptr.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/ptr.rs new file mode 100644 index 0000000..b1443a2 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/ptr.rs @@ -0,0 +1,280 @@ +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; + +/// Alias for `WasmPtr. +pub type WasmPtr64 = WasmPtr; + +/// A zero-cost type that represents a pointer to something in Wasm linear +/// memory. +/// +/// This type can be used directly in the host function arguments: +/// ``` +/// # use wasmer::Memory; +/// # use wasmer::WasmPtr; +/// # use wasmer::FunctionEnvMut; +/// pub fn host_import(mut env: FunctionEnvMut<()>, memory: Memory, ptr: WasmPtr) { +/// let memory = memory.view(&env); +/// let derefed_ptr = ptr.deref(&memory); +/// let inner_val: u32 = derefed_ptr.read().expect("pointer in bounds"); +/// println!("Got {} from Wasm memory address 0x{:X}", inner_val, ptr.offset()); +/// // update the value being pointed to +/// derefed_ptr.write(inner_val + 1).expect("pointer in bounds"); +/// } +/// ``` +/// +/// This type can also be used with primitive-filled structs, but be careful of +/// guarantees required by `ValueType`. +/// ``` +/// # use wasmer::Memory; +/// # use wasmer::WasmPtr; +/// # use wasmer::ValueType; +/// # use wasmer::FunctionEnvMut; +/// +/// // This is safe as the 12 bytes represented by this struct +/// // are valid for all bit combinations. +/// #[derive(Copy, Clone, Debug, ValueType)] +/// #[repr(C)] +/// struct V3 { +/// x: f32, +/// y: f32, +/// z: f32 +/// } +/// +/// fn update_vector_3(mut env: FunctionEnvMut<()>, memory: Memory, ptr: WasmPtr) { +/// let memory = memory.view(&env); +/// let derefed_ptr = ptr.deref(&memory); +/// let mut inner_val: V3 = derefed_ptr.read().expect("pointer in bounds"); +/// println!("Got {:?} from Wasm memory address 0x{:X}", inner_val, ptr.offset()); +/// // update the value being pointed to +/// inner_val.x = 10.4; +/// derefed_ptr.write(inner_val).expect("pointer in bounds"); +/// } +/// ``` +#[repr(transparent)] +pub struct WasmPtr { + offset: M::Offset, + _phantom: PhantomData, +} + +impl WasmPtr { + /// Create a new `WasmPtr` at the given offset. + #[inline] + pub fn new(offset: M::Offset) -> Self { + Self { + offset, + _phantom: PhantomData, + } + } + + /// Get the offset into Wasm linear memory for this `WasmPtr`. + #[inline] + pub fn offset(&self) -> M::Offset { + self.offset + } + + /// Casts this `WasmPtr` to a `WasmPtr` of a different type. + #[inline] + pub fn cast(self) -> WasmPtr { + WasmPtr { + offset: self.offset, + _phantom: PhantomData, + } + } + + /// Returns a null `UserPtr`. + #[inline] + pub fn null() -> Self { + Self::new(M::ZERO) + } + + /// Checks whether the `WasmPtr` is null. + #[inline] + pub fn is_null(&self) -> bool { + self.offset.into() == 0 + } + + /// Calculates an offset from the current pointer address. The argument is + /// in units of `T`. + /// + /// This method returns an error if an address overflow occurs. + #[inline] + pub fn add_offset(self, offset: M::Offset) -> Result { + let base = self.offset.into(); + let index = offset.into(); + let offset = index + .checked_mul(mem::size_of::() as u64) + .ok_or(MemoryAccessError::Overflow)?; + let address = base + .checked_add(offset) + .ok_or(MemoryAccessError::Overflow)?; + let address = M::Offset::try_from(address).map_err(|_| MemoryAccessError::Overflow)?; + Ok(Self::new(address)) + } + + /// Calculates an offset from the current pointer address. The argument is + /// in units of `T`. + /// + /// This method returns an error if an address underflow occurs. + #[inline] + pub fn sub_offset(self, offset: M::Offset) -> Result { + let base = self.offset.into(); + let index = offset.into(); + let offset = index + .checked_mul(mem::size_of::() as u64) + .ok_or(MemoryAccessError::Overflow)?; + let address = base + .checked_sub(offset) + .ok_or(MemoryAccessError::Overflow)?; + let address = M::Offset::try_from(address).map_err(|_| MemoryAccessError::Overflow)?; + Ok(Self::new(address)) + } +} + +impl WasmPtr { + /// Creates a `WasmRef` from this `WasmPtr` which allows reading and + /// mutating of the value being pointed to. + #[inline] + pub fn deref<'a>(&self, view: &'a MemoryView) -> WasmRef<'a, T> { + WasmRef::new(view, self.offset.into()) + } + + /// Reads the address pointed to by this `WasmPtr` in a memory. + #[inline] + pub fn read(&self, view: &MemoryView) -> Result { + self.deref(view).read() + } + + /// Writes to the address pointed to by this `WasmPtr` in a memory. + #[inline] + pub fn write(&self, view: &MemoryView, val: T) -> Result<(), MemoryAccessError> { + self.deref(view).write(val) + } + + /// Creates a `WasmSlice` starting at this `WasmPtr` which allows reading + /// and mutating of an array of value being pointed to. + /// + /// Returns a `MemoryAccessError` if the slice length overflows a 64-bit + /// address. + #[inline] + pub fn slice<'a>( + &self, + view: &'a MemoryView, + len: M::Offset, + ) -> Result, MemoryAccessError> { + WasmSlice::new(view, self.offset.into(), len.into()) + } + + /// Reads a sequence of values from this `WasmPtr` until a value that + /// matches the given condition is found. + /// + /// This last value is not included in the returned vector. + #[inline] + pub fn read_until( + &self, + view: &MemoryView, + mut end: impl FnMut(&T) -> bool, + ) -> Result, MemoryAccessError> { + let mut vec = Vec::new(); + for i in 0u64.. { + let i = M::Offset::try_from(i).map_err(|_| MemoryAccessError::Overflow)?; + let val = self.add_offset(i)?.deref(view).read()?; + if end(&val) { + break; + } + vec.push(val); + } + Ok(vec) + } + + /// Creates a `WasmAccess` + #[inline] + pub fn access<'a>( + &self, + view: &'a MemoryView, + ) -> Result, MemoryAccessError> { + self.deref(view).access() + } +} + +impl WasmPtr { + /// Reads a UTF-8 string from the `WasmPtr` with the given length. + /// + /// This method is safe to call even if the memory is being concurrently + /// modified. + #[inline] + pub fn read_utf8_string( + &self, + view: &MemoryView, + len: M::Offset, + ) -> Result { + let vec = self.slice(view, len)?.read_to_vec()?; + Ok(String::from_utf8(vec)?) + } + + /// Reads a null-terminated UTF-8 string from the `WasmPtr`. + /// + /// This method is safe to call even if the memory is being concurrently + /// modified. + #[inline] + pub fn read_utf8_string_with_nul( + &self, + view: &MemoryView, + ) -> Result { + let vec = self.read_until(view, |&byte| byte == 0)?; + Ok(String::from_utf8(vec)?) + } +} + +unsafe impl FromToNativeWasmType for WasmPtr +where + ::Native: NativeWasmTypeInto, +{ + type Native = M::Native; + + fn to_native(self) -> Self::Native { + M::offset_to_native(self.offset) + } + fn from_native(n: Self::Native) -> Self { + Self { + offset: M::native_to_offset(n), + _phantom: PhantomData, + } + } + #[inline] + fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { + true // in Javascript there are no different stores + } +} + +unsafe impl ValueType for WasmPtr { + fn zero_padding_bytes(&self, _bytes: &mut [mem::MaybeUninit]) {} +} + +impl Clone for WasmPtr { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for WasmPtr {} + +impl PartialEq for WasmPtr { + fn eq(&self, other: &Self) -> bool { + self.offset.into() == other.offset.into() + } +} + +impl Eq for WasmPtr {} + +impl fmt::Debug for WasmPtr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}(@{})", std::any::type_name::(), self.offset.into()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/store.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/store.rs new file mode 100644 index 0000000..3dea7e6 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/store.rs @@ -0,0 +1,291 @@ +use crate::engine::{AsEngineRef, Engine, EngineRef}; +use derivative::Derivative; +use std::{ + fmt, + ops::{Deref, DerefMut}, +}; +#[cfg(feature = "sys")] +pub use wasmer_compiler::Tunables; +pub use wasmer_types::{OnCalledAction, StoreId}; +#[cfg(feature = "sys")] +use wasmer_vm::init_traps; +#[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}; + +/// 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. +#[derive(Derivative)] +#[derivative(Debug)] +pub(crate) struct StoreInner { + pub(crate) objects: StoreObjects, + #[derivative(Debug = "ignore")] + pub(crate) engine: Engine, + #[cfg(feature = "sys")] + #[derivative(Debug = "ignore")] + pub(crate) trap_handler: Option>>, + #[derivative(Debug = "ignore")] + pub(crate) on_called: Option, +} + +/// 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 { + // Make sure the signal handlers are installed. + // This is required for handling traps. + #[cfg(feature = "sys")] + init_traps(); + + Self { + inner: Box::new(StoreInner { + objects: Default::default(), + engine: engine.into(), + #[cfg(feature = "sys")] + trap_handler: None, + on_called: None, + }), + } + } + + #[cfg(feature = "sys")] + /// Set the trap handler in this store. + pub fn set_trap_handler(&mut self, handler: Option>>) { + self.inner.trap_handler = handler; + } + + /// Returns the [`Engine`]. + pub fn engine(&self) -> &Engine { + &self.inner.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<'_> { + EngineRef::new(&self.inner.engine) + } +} + +impl AsEngineRef for StoreRef<'_> { + fn as_engine_ref(&self) -> EngineRef<'_> { + EngineRef::new(&self.inner.engine) + } +} + +impl AsEngineRef for StoreMut<'_> { + fn as_engine_ref(&self) -> EngineRef<'_> { + EngineRef::new(&self.inner.engine) + } +} + +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.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 + .trap_handler + .as_ref() + .map(|handler| handler.as_ref() as *const _) + } +} + +/// 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.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.engine, &mut self.inner.objects) + } + + pub(crate) fn as_raw(&self) -> *mut StoreInner { + self.inner as *const StoreInner as *mut StoreInner + } + + 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/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/engine.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/engine.rs new file mode 100644 index 0000000..1bf6a23 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/engine.rs @@ -0,0 +1,152 @@ +use std::{path::Path, sync::Arc}; + +use shared_buffer::OwnedBuffer; +pub use wasmer_compiler::{ + Artifact, BaseTunables, CompilerConfig, Engine, EngineBuilder, Tunables, +}; +#[cfg(feature = "compiler")] +use wasmer_types::Features; +use wasmer_types::{DeserializeError, Target}; + +/// Get the default config for the sys Engine +#[allow(unreachable_code)] +pub fn get_default_compiler_config() -> Option> { + cfg_if::cfg_if! { + if #[cfg(feature = "cranelift")] { + Some(Box::::default()) + } else if #[cfg(feature = "llvm")] { + Some(Box::::default()) + } else if #[cfg(feature = "singlepass")] { + Some(Box::::default()) + } + else { + None + } + } +} + +/// Returns the default engine for the Sys engine +pub(crate) fn default_engine() -> Engine { + #[allow(unreachable_code, unused_mut)] + fn get_engine() -> Engine { + cfg_if::cfg_if! { + if #[cfg(feature = "compiler")] { + if let Some(config) = get_default_compiler_config() { + EngineBuilder::new(config) + .engine() + } else { + EngineBuilder::headless() + .engine() + } + } else { + EngineBuilder::headless().engine() + } + } + } + + let mut engine = get_engine(); + let tunables = BaseTunables::for_target(engine.target()); + engine.set_tunables(tunables); + engine +} + +/// The custom trait to access to all the `sys` function in the common +/// engine. +pub trait NativeEngineExt { + /// Create a new `Engine` with the given config + #[cfg(feature = "compiler")] + fn new(compiler_config: Box, target: Target, features: Features) -> Self; + + /// Create a headless `Engine` + /// + /// A headless engine is an engine without any compiler attached. + /// This is useful for assuring a minimal runtime for running + /// WebAssembly modules. + /// + /// For example, for running in IoT devices where compilers are very + /// expensive, or also to optimize startup speed. + /// + /// # Important + /// + /// Headless engines can't compile or validate any modules, + /// they just take already processed Modules (via `Module::serialize`). + fn headless() -> Self; + + /// Gets the target + fn target(&self) -> &Target; + + /// Attach a Tunable to this engine + fn set_tunables(&mut self, tunables: impl Tunables + Send + Sync + 'static); + + /// Get a reference to attached Tunable of this engine + fn tunables(&self) -> &dyn Tunables; + + /// Load a serialized WebAssembly module from a memory mapped file and deserialize it. + /// + /// NOTE: you should almost always prefer [`Self::deserialize_from_mmapped_file`]. + /// + /// # Safety + /// See [`Artifact::deserialize_unchecked`]. + unsafe fn deserialize_from_mmapped_file_unchecked( + &self, + file_ref: &Path, + ) -> Result; + + /// Load a serialized WebAssembly module from a memory mapped file and deserialize it. + /// + /// # Safety + /// See [`Artifact::deserialize`]. + unsafe fn deserialize_from_mmapped_file( + &self, + file_ref: &Path, + ) -> Result; +} + +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)) + } + + fn headless() -> Self { + Self(Engine::headless()) + } + + fn target(&self) -> &Target { + self.0.target() + } + + fn set_tunables(&mut self, tunables: impl Tunables + Send + Sync + 'static) { + self.0.set_tunables(tunables) + } + + fn tunables(&self) -> &dyn Tunables { + self.0.tunables() + } + + unsafe fn deserialize_from_mmapped_file_unchecked( + &self, + file_ref: &Path, + ) -> Result { + let bytes = std::fs::read(file_ref)?; + let artifact = Arc::new(Artifact::deserialize_unchecked(&self.0, bytes.into())?); + Ok(crate::Module(super::module::Module::from_artifact( + artifact, + ))) + } + + unsafe fn deserialize_from_mmapped_file( + &self, + file_ref: &Path, + ) -> Result { + let file = std::fs::File::open(file_ref)?; + let artifact = Arc::new(Artifact::deserialize( + &self.0, + OwnedBuffer::from_file(&file) + .map_err(|e| DeserializeError::Generic(format!("{e:?}")))?, + )?); + Ok(crate::Module(super::module::Module::from_artifact( + artifact, + ))) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/errors.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/errors.rs new file mode 100644 index 0000000..b5943be --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/errors.rs @@ -0,0 +1,24 @@ +use crate::{LinkError, RuntimeError}; +use wasmer_vm::Trap; + +impl From for LinkError { + fn from(other: wasmer_compiler::LinkError) -> Self { + match other { + wasmer_compiler::LinkError::Import(namespace, name, error) => { + Self::Import(namespace, name, error) + } + wasmer_compiler::LinkError::Trap(e) => Self::Trap(e.into()), + wasmer_compiler::LinkError::Resource(e) => Self::Resource(e), + } + } +} + +impl From for 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) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/extern_ref.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/extern_ref.rs new file mode 100644 index 0000000..2f50751 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/extern_ref.rs @@ -0,0 +1,49 @@ +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/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/function.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/function.rs new file mode 100644 index 0000000..41c292f --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/function.rs @@ -0,0 +1,661 @@ +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}; +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, +}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Function { + pub(crate) handle: StoreHandle, +} + +impl From> for Function { + fn from(handle: StoreHandle) -> Self { + Self { handle } + } +} + +impl Function { + 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, + { + let function_type = ty.into(); + let func_ty = function_type.clone(); + let func_env = env.clone(); + let raw_store = store.as_store_mut().as_raw() as *mut u8; + let wrapper = move |values_vec: *mut RawValue| -> Result<(), RuntimeError> { + unsafe { + let mut store = StoreMut::from_raw(raw_store as *mut StoreInner); + let mut args = Vec::with_capacity(func_ty.params().len()); + for (i, ty) in func_ty.params().iter().enumerate() { + args.push(Value::from_raw(&mut store, *ty, *values_vec.add(i))); + } + let store_mut = StoreMut::from_raw(raw_store as *mut StoreInner); + let env = FunctionEnvMut { + store_mut, + func_env: func_env.clone(), + }; + let returns = func(env, &args)?; + + // We need to dynamically check that the returns + // match the expected types, as well as expected length. + let return_types = returns.iter().map(|ret| ret.ty()); + if return_types.ne(func_ty.results().iter().copied()) { + return Err(RuntimeError::new(format!( + "Dynamic function returned wrong signature. Expected {:?} but got {:?}", + func_ty.results(), + returns.iter().map(|ret| ret.ty()) + ))); + } + for (i, ret) in returns.iter().enumerate() { + *values_vec.add(i) = ret.as_raw(&store); + } + } + Ok(()) + }; + let mut host_data = Box::new(VMDynamicFunctionContext { + address: std::ptr::null(), + ctx: DynamicFunction { func: wrapper }, + }); + host_data.address = host_data.ctx.func_body_ptr(); + + // We don't yet have the address with the Wasm ABI signature. + // The engine linker will replace the address with one pointing to a + // generated dynamic trampoline. + let func_ptr = std::ptr::null() as VMFunctionCallback; + let type_index = store + .as_store_mut() + .engine() + .0 + .register_signature(&function_type); + let vmctx = VMFunctionContext { + host_env: host_data.as_ref() as *const _ as *mut c_void, + }; + let call_trampoline = host_data.ctx.call_trampoline_address(); + let anyfunc = VMCallerCheckedAnyfunc { + func_ptr, + type_index, + vmctx, + call_trampoline, + }; + + let vm_function = VMFunction { + anyfunc: MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(anyfunc))), + kind: VMFunctionKind::Dynamic, + signature: function_type, + host_data, + }; + Self { + handle: StoreHandle::new(store.as_store_mut().objects_mut(), 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 env = FunctionEnv::new(store, ()); + let func_ptr = func.function_callback(); + let host_data = Box::new(StaticFunction { + raw_store: store.as_store_mut().as_raw() as *mut u8, + env, + func, + }); + let function_type = FunctionType::new(Args::wasm_types(), Rets::wasm_types()); + + let type_index = store + .as_store_mut() + .engine() + .0 + .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 anyfunc = VMCallerCheckedAnyfunc { + func_ptr, + type_index, + vmctx, + call_trampoline, + }; + + let vm_function = VMFunction { + anyfunc: MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(anyfunc))), + kind: VMFunctionKind::Static, + signature: function_type, + host_data, + }; + Self { + handle: StoreHandle::new(store.as_store_mut().objects_mut(), vm_function), + } + } + + 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, + { + let func_ptr = func.function_callback(); + let host_data = Box::new(StaticFunction { + raw_store: store.as_store_mut().as_raw() as *mut u8, + env: env.clone(), + func, + }); + let function_type = FunctionType::new(Args::wasm_types(), Rets::wasm_types()); + + let type_index = store + .as_store_mut() + .engine() + .0 + .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 anyfunc = VMCallerCheckedAnyfunc { + func_ptr, + type_index, + vmctx, + call_trampoline, + }; + + let vm_function = VMFunction { + anyfunc: MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(anyfunc))), + kind: VMFunctionKind::Static, + signature: function_type, + host_data, + }; + Self { + handle: StoreHandle::new(store.as_store_mut().objects_mut(), vm_function), + } + } + + pub fn ty(&self, store: &impl AsStoreRef) -> FunctionType { + self.handle + .get(store.as_store_ref().objects()) + .signature + .clone() + } + + fn call_wasm( + &self, + store: &mut impl AsStoreMut, + trampoline: VMTrampoline, + params: &[Value], + results: &mut [Value], + ) -> Result<(), RuntimeError> { + let format_types_for_error_message = |items: &[Value]| { + items + .iter() + .map(|param| param.ty().to_string()) + .collect::>() + .join(", ") + }; + // TODO: Avoid cloning the signature here, it's expensive. + let signature = self.ty(store); + if signature.params().len() != params.len() { + return Err(RuntimeError::new(format!( + "Parameters of type [{}] did not match signature {}", + format_types_for_error_message(params), + &signature + ))); + } + if signature.results().len() != results.len() { + return Err(RuntimeError::new(format!( + "Results of type [{}] did not match signature {}", + format_types_for_error_message(results), + &signature, + ))); + } + + let mut values_vec = vec![RawValue { i32: 0 }; max(params.len(), results.len())]; + + // Store the argument values into `values_vec`. + let param_tys = signature.params().iter(); + for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) { + if arg.ty() != *ty { + let param_types = format_types_for_error_message(params); + return Err(RuntimeError::new(format!( + "Parameters of type [{}] did not match signature {}", + param_types, &signature, + ))); + } + if !arg.is_from_store(store) { + return Err(RuntimeError::new("cross-`Store` values are not supported")); + } + *slot = arg.as_raw(store); + } + + // Invoke the call + self.call_wasm_raw(store, trampoline, values_vec, results)?; + Ok(()) + } + + fn call_wasm_raw( + &self, + store: &mut impl AsStoreMut, + trampoline: VMTrampoline, + mut params: Vec, + results: &mut [Value], + ) -> Result<(), RuntimeError> { + // Call the trampoline. + 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 storeref = store.as_store_ref(); + let vm_function = self.handle.get(storeref.objects()); + let config = storeref.engine().tunables().vmconfig(); + r = unsafe { + wasmer_call_trampoline( + store.as_store_ref().signal_handler(), + config, + vm_function.anyfunc.as_ptr().as_ref().vmctx, + trampoline, + vm_function.anyfunc.as_ptr().as_ref().func_ptr, + params.as_mut_ptr() as *mut u8, + ) + }; + let store_mut = store.as_store_mut(); + 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 + }; + if let Err(error) = result { + return Err(error.into()); + } + + // Load the return values out of `values_vec`. + let signature = self.ty(store); + for (index, &value_type) in signature.results().iter().enumerate() { + unsafe { + results[index] = Value::from_raw(store, value_type, params[index]); + } + } + + Ok(()) + } + + pub fn result_arity(&self, store: &impl AsStoreRef) -> usize { + self.ty(store).results().len() + } + + pub fn call( + &self, + store: &mut impl AsStoreMut, + params: &[Value], + ) -> Result, RuntimeError> { + let trampoline = unsafe { + self.handle + .get(store.as_store_ref().objects()) + .anyfunc + .as_ptr() + .as_ref() + .call_trampoline + }; + let mut results = vec![Value::null(); self.result_arity(store)]; + self.call_wasm(store, trampoline, params, &mut results)?; + Ok(results.into_boxed_slice()) + } + + #[doc(hidden)] + #[allow(missing_docs)] + pub fn call_raw( + &self, + store: &mut impl AsStoreMut, + params: Vec, + ) -> Result, RuntimeError> { + let trampoline = unsafe { + self.handle + .get(store.as_store_ref().objects()) + .anyfunc + .as_ptr() + .as_ref() + .call_trampoline + }; + let mut results = vec![Value::null(); self.result_arity(store)]; + self.call_wasm_raw(store, trampoline, params, &mut results)?; + Ok(results.into_boxed_slice()) + } + + pub(crate) fn vm_funcref(&self, store: &impl AsStoreRef) -> VMFuncRef { + let vm_function = self.handle.get(store.as_store_ref().objects()); + if vm_function.kind == VMFunctionKind::Dynamic { + panic!("dynamic functions cannot be used in tables or as funcrefs"); + } + VMFuncRef(vm_function.anyfunc.as_ptr()) + } + + pub(crate) unsafe fn from_vm_funcref(store: &mut impl AsStoreMut, funcref: VMFuncRef) -> Self { + let signature = store + .as_store_ref() + .engine() + .0 + .lookup_signature(funcref.0.as_ref().type_index) + .expect("Signature not found in store"); + let vm_function = VMFunction { + anyfunc: MaybeInstanceOwned::Instance(funcref.0), + signature, + // All functions in tables are already Static (as dynamic functions + // are converted to use the trampolines with static signatures). + kind: wasmer_vm::VMFunctionKind::Static, + host_data: Box::new(()), + }; + Self { + handle: StoreHandle::new(store.as_store_mut().objects_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) + }, + } + } + + /// Checks whether this `Function` can be used with the given store. + 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::Function(self.handle.internal_handle()) + } +} + +/// Host state for a dynamic function. +pub(crate) struct DynamicFunction { + func: F, +} + +impl DynamicFunction +where + F: Fn(*mut RawValue) -> Result<(), RuntimeError> + 'static, +{ + // This function wraps our func, to make it compatible with the + // reverse trampoline signature + unsafe extern "C" fn func_wrapper( + this: &mut VMDynamicFunctionContext, + values_vec: *mut RawValue, + ) { + let result = + on_host_stack(|| panic::catch_unwind(AssertUnwindSafe(|| (this.ctx.func)(values_vec)))); + + match result { + Ok(Ok(())) => {} + Ok(Err(trap)) => raise_user_trap(Box::new(trap)), + Err(panic) => resume_panic(panic), + } + } + + fn func_body_ptr(&self) -> VMFunctionCallback { + Self::func_wrapper as VMFunctionCallback + } + + fn call_trampoline_address(&self) -> VMTrampoline { + Self::call_trampoline + } + + unsafe extern "C" fn call_trampoline( + vmctx: *mut VMContext, + _body: VMFunctionCallback, + args: *mut RawValue, + ) { + // The VMFunctionCallback is null here: it is only filled in later + // by the engine linker. + let dynamic_function = &mut *(vmctx as *mut VMDynamicFunctionContext); + Self::func_wrapper(dynamic_function, args); + } +} + +/// Represents a low-level Wasm static host function. See +/// [`crate::Function::new_typed`] and +/// [`crate::Function::new_typed_with_env`] to learn more. +pub(crate) struct StaticFunction { + pub(crate) raw_store: *mut u8, + pub(crate) env: FunctionEnv, + pub(crate) func: F, +} + +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 + where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + RetsAsResult: IntoResult, + 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( 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 + } + + #[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> + } + + } + + // 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. + 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> + } + + } + }; + } + +// 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/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/global.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/global.rs new file mode 100644 index 0000000..8d43e80 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/global.rs @@ -0,0 +1,95 @@ +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}; + +#[derive(Debug, Clone)] +pub struct Global { + handle: StoreHandle, +} + +impl Global { + /// 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 { + if !val.is_from_store(store) { + return Err(RuntimeError::new("cross-`Store` values are not supported")); + } + let global = VMGlobal::new(GlobalType { + mutability, + ty: val.ty(), + }); + unsafe { + global.vmglobal().as_mut().val = val.as_raw(store); + } + + Ok(Self { + handle: StoreHandle::new(store.objects_mut(), global), + }) + } + + pub fn ty(&self, store: &impl AsStoreRef) -> GlobalType { + *self.handle.get(store.as_store_ref().objects()).ty() + } + + pub fn get(&self, store: &mut impl AsStoreMut) -> Value { + unsafe { + let global = self.handle.get(store.as_store_ref().objects()); + let raw = global.vmglobal().as_ref().val; + let ty = global.ty().ty; + Value::from_raw(store, ty, raw) + } + } + + pub 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")); + } + let global = self.handle.get_mut(store.objects_mut()); + if global.ty().mutability != Mutability::Var { + return Err(RuntimeError::new("Attempted to set an immutable global")); + } + if val.ty() != global.ty().ty { + return Err(RuntimeError::new(format!( + "Attempted to operate on a global of type {expected} as a global of type {found}", + expected = global.ty().ty, + found = val.ty(), + ))); + } + unsafe { + let mut dest = global.vmglobal(); + dest.as_mut().val = val.as_raw(store); + } + Ok(()) + } + + 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) + }, + } + } + + 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::Global(self.handle.internal_handle()) + } +} + +impl std::cmp::PartialEq for Global { + fn eq(&self, other: &Self) -> bool { + self.handle == other.handle + } +} + +impl std::cmp::Eq for Global {} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/memory.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/memory.rs new file mode 100644 index 0000000..b61adc4 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/memory.rs @@ -0,0 +1,310 @@ +use std::{ + convert::TryInto, + marker::PhantomData, + mem::{self, MaybeUninit}, + slice, +}; + +use tracing::warn; +use wasmer_types::Pages; +use wasmer_vm::{ + LinearMemory, MemoryError, StoreHandle, ThreadConditionsHandle, VMExtern, VMMemory, +}; + +use crate::{ + store::{AsStoreMut, AsStoreRef}, + sys::{engine::NativeEngineExt, externals::memory_view::MemoryView}, + vm::VMExternMemory, + MemoryAccessError, MemoryType, +}; + +#[derive(Debug, Clone)] +pub struct Memory { + pub(crate) handle: StoreHandle, +} + +impl Memory { + pub 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), + }) + } + + 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 fn ty(&self, store: &impl AsStoreRef) -> MemoryType { + self.handle.get(store.as_store_ref().objects()).ty() + } + + pub fn grow( + &self, + store: &mut impl AsStoreMut, + delta: IntoPages, + ) -> Result + where + IntoPages: Into, + { + self.handle.get_mut(store.objects_mut()).grow(delta.into()) + } + + pub fn grow_at_least( + &self, + store: &mut impl AsStoreMut, + min_size: u64, + ) -> Result<(), MemoryError> { + self.handle + .get_mut(store.objects_mut()) + .grow_at_least(min_size) + } + + pub fn reset(&self, store: &mut impl AsStoreMut) -> Result<(), MemoryError> { + self.handle.get_mut(store.objects_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) + }, + } + } + + /// Checks whether this `Memory` 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() + } + + /// 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()); + 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( + &self, + store: &impl AsStoreRef, + ) -> Result, MemoryError> { + let mut mem = self.try_clone(store)?; + mem.copy() + } + + pub fn as_shared(&self, store: &impl AsStoreRef) -> Option { + let mem = self.handle.get(store.as_store_ref().objects()); + let conds = mem.thread_conditions()?.downgrade(); + + Some(crate::SharedMemory::new(crate::Memory(self.clone()), conds)) + } + + /// To `VMExtern`. + pub(crate) fn to_vm_extern(&self) -> VMExtern { + VMExtern::Memory(self.handle.internal_handle()) + } +} + +impl crate::externals::memory::SharedMemoryOps for ThreadConditionsHandle { + fn notify( + &self, + dst: crate::externals::memory::MemoryLocation, + count: u32, + ) -> Result { + let count = self + .upgrade() + .ok_or(crate::AtomicsError::Unimplemented)? + .do_notify( + wasmer_vm::NotifyLocation { + address: dst.address, + }, + count, + ); + Ok(count) + } + + fn wait( + &self, + dst: crate::externals::memory::MemoryLocation, + timeout: Option, + ) -> Result { + self.upgrade() + .ok_or(crate::AtomicsError::Unimplemented)? + .do_wait( + wasmer_vm::NotifyLocation { + address: dst.address, + }, + timeout, + ) + .map_err(|e| match e { + wasmer_vm::WaiterError::Unimplemented => crate::AtomicsError::Unimplemented, + wasmer_vm::WaiterError::TooManyWaiters => crate::AtomicsError::TooManyWaiters, + wasmer_vm::WaiterError::AtomicsDisabled => crate::AtomicsError::AtomicsDisabled, + _ => crate::AtomicsError::Unimplemented, + }) + } + + fn disable_atomics(&self) -> Result<(), MemoryError> { + self.upgrade() + .ok_or_else(|| MemoryError::Generic("memory was dropped".to_string()))? + .disable_atomics(); + Ok(()) + } + + fn wake_all_atomic_waiters(&self) -> Result<(), MemoryError> { + self.upgrade() + .ok_or_else(|| MemoryError::Generic("memory was dropped".to_string()))? + .wake_all_atomic_waiters(); + Ok(()) + } +} + +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)?; + 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 { 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(mem::size_of::()); + *dst = dst.add(mem::size_of::()); + *len -= 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(mem::size_of::()); + *dst = dst.add(mem::size_of::()); + *len -= 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/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/memory_view.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/memory_view.rs new file mode 100644 index 0000000..b1dc5e1 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/memory_view.rs @@ -0,0 +1,202 @@ +use crate::store::AsStoreRef; +use crate::MemoryAccessError; +use std::marker::PhantomData; +use std::mem::MaybeUninit; +use std::slice; +use std::{convert::TryInto, ops::Range}; +use wasmer_types::Pages; +use wasmer_vm::LinearMemory; + +use super::memory::{Memory, MemoryBuffer}; + +/// 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: Pages, +} + +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 def = unsafe { definition.as_ref() }; + + Self { + buffer: MemoryBuffer { + base: def.base, + len: def.current_length, + marker: PhantomData, + }, + size, + } + } + + /// Returns the pointer to the raw bytes of the `Memory`. + // + // This used by wasmer-emscripten and 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] { + 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 { + 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/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/mod.rs new file mode 100644 index 0000000..3575fe2 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/mod.rs @@ -0,0 +1,5 @@ +pub(crate) mod function; +pub(crate) mod global; +pub(crate) mod memory; +pub(crate) mod memory_view; +pub(crate) mod table; diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/table.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/table.rs new file mode 100644 index 0000000..aba6844 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/externals/table.rs @@ -0,0 +1,161 @@ +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)] +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/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/instance.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/instance.rs new file mode 100644 index 0000000..bbeab94 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/instance.rs @@ -0,0 +1,92 @@ +use crate::errors::InstantiationError; +use crate::exports::Exports; +use crate::module::Module; +use wasmer_vm::{StoreHandle, VMInstance}; + +use crate::imports::Imports; +use crate::store::AsStoreMut; +use crate::Extern; + +#[derive(Clone, PartialEq, Eq)] +pub struct Instance { + _handle: StoreHandle, +} + +#[cfg(test)] +mod send_test { + use super::*; + + // Only here to statically ensure that `Instance` is `Send`. + // Will fail to compile otherwise. + #[allow(dead_code)] + fn instance_is_send(inst: Instance) { + fn is_send(t: impl Send) { + let _ = t; + } + + is_send(inst); + } +} + +impl From for InstantiationError { + fn from(other: wasmer_compiler::InstantiationError) -> Self { + match other { + wasmer_compiler::InstantiationError::Link(e) => Self::Link(e.into()), + wasmer_compiler::InstantiationError::Start(e) => Self::Start(e.into()), + wasmer_compiler::InstantiationError::CpuFeature(e) => Self::CpuFeature(e), + } + } +} + +impl Instance { + #[allow(clippy::result_large_err)] + pub(crate) fn new( + store: &mut impl AsStoreMut, + module: &Module, + imports: &Imports, + ) -> Result<(Self, Exports), InstantiationError> { + 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 instance = Self { + _handle: StoreHandle::new(store.objects_mut(), handle), + }; + + Ok((instance, exports)) + } + + #[allow(clippy::result_large_err)] + pub(crate) fn new_by_index( + store: &mut impl AsStoreMut, + module: &Module, + 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 instance = Self { + _handle: StoreHandle::new(store.objects_mut(), handle), + }; + + Ok((instance, exports)) + } + + fn get_exports( + store: &mut impl AsStoreMut, + module: &Module, + handle: &mut VMInstance, + ) -> Exports { + module + .exports() + .map(|export| { + let name = export.name().to_string(); + let export = handle.lookup(&name).expect("export"); + let extern_ = Extern::from_vm_extern(store, export); + (name, extern_) + }) + .collect::() + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/mem_access.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/mem_access.rs new file mode 100644 index 0000000..376c724 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/mem_access.rs @@ -0,0 +1,95 @@ +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/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/mod.rs new file mode 100644 index 0000000..5ed4ebb --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/mod.rs @@ -0,0 +1,41 @@ +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(super) mod tunables; +pub(crate) mod typed_function; + +pub use crate::sys::engine::{get_default_compiler_config, NativeEngineExt}; +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; + +pub(crate) mod vm { + //! The `vm` module re-exports wasmer-vm types. + 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; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/module.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/module.rs new file mode 100644 index 0000000..3aae35d --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/module.rs @@ -0,0 +1,201 @@ +use std::path::Path; +use std::sync::Arc; + +use bytes::Bytes; +use wasmer_compiler::{Artifact, ArtifactCreate}; +use wasmer_types::{ + CompileError, DeserializeError, ExportsIterator, ImportsIterator, ModuleInfo, SerializeError, +}; +use wasmer_types::{ExportType, ImportType}; + +use crate::{ + engine::AsEngineRef, sys::engine::NativeEngineExt, vm::VMInstance, AsStoreMut, AsStoreRef, + InstantiationError, IntoBytes, +}; + +#[derive(Clone, PartialEq, Eq)] +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. + // + // The reason for this is that dropping the Artifact will de-register the + // trap handling metadata from the global registry. This must be done before + // the code memory for the artifact is freed (which happens when the store + // is dropped) since there is a chance that this memory could be reused by + // another module which will try to register its own trap information. + // + // Note that in Rust, the drop order for struct fields is from top to + // bottom: the opposite of C++. + // + // In the future, this code should be refactored to properly describe the + // ownership of the code and its metadata. + artifact: Arc, +} + +impl Module { + pub(crate) fn from_binary( + engine: &impl AsEngineRef, + binary: &[u8], + ) -> Result { + Self::validate(engine, binary)?; + unsafe { Self::from_binary_unchecked(engine, binary) } + } + + pub(crate) unsafe fn from_binary_unchecked( + engine: &impl AsEngineRef, + binary: &[u8], + ) -> Result { + let module = Self::compile(engine, binary)?; + Ok(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) + } + + #[cfg(feature = "compiler")] + fn compile(engine: &impl AsEngineRef, binary: &[u8]) -> Result { + let artifact = engine.as_engine_ref().engine().0.compile(binary)?; + Ok(Self::from_artifact(artifact)) + } + + #[cfg(not(feature = "compiler"))] + fn compile(_engine: &impl AsEngineRef, _binary: &[u8]) -> Result { + Err(CompileError::UnsupportedTarget( + "The compiler feature is not enabled, but is required to compile a Module".to_string(), + )) + } + + pub(crate) fn serialize(&self) -> Result { + self.artifact.serialize().map(|bytes| bytes.into()) + } + + #[tracing::instrument(level = "debug", skip_all)] + pub unsafe fn deserialize_unchecked( + engine: &impl AsEngineRef, + bytes: impl IntoBytes, + ) -> Result { + let bytes = bytes.into_bytes(); + let artifact = engine + .as_engine_ref() + .engine() + .0 + .deserialize_unchecked(bytes.into())?; + Ok(Self::from_artifact(artifact)) + } + + #[tracing::instrument(level = "debug", skip_all)] + pub unsafe fn deserialize( + engine: &impl AsEngineRef, + bytes: impl IntoBytes, + ) -> Result { + let bytes = bytes.into_bytes(); + let artifact = engine + .as_engine_ref() + .engine() + .0 + .deserialize(bytes.into())?; + Ok(Self::from_artifact(artifact)) + } + + pub unsafe fn deserialize_from_file_unchecked( + engine: &impl AsEngineRef, + path: impl AsRef, + ) -> Result { + let artifact = engine + .as_engine_ref() + .engine() + .0 + .deserialize_from_file_unchecked(path.as_ref())?; + Ok(Self::from_artifact(artifact)) + } + + pub unsafe fn deserialize_from_file( + engine: &impl AsEngineRef, + path: impl AsRef, + ) -> Result { + let artifact = engine + .as_engine_ref() + .engine() + .0 + .deserialize_from_file(path.as_ref())?; + Ok(Self::from_artifact(artifact)) + } + + pub(super) fn from_artifact(artifact: Arc) -> Self { + Self { artifact } + } + + #[allow(clippy::result_large_err)] + pub(crate) fn instantiate( + &self, + store: &mut impl AsStoreMut, + imports: &[crate::Extern], + ) -> Result { + if !self.artifact.allocated() { + // Return an error mentioning that the artifact is compiled for a different + // platform. + return Err(InstantiationError::DifferentArchOS); + } + // Ensure all imports come from the same context. + for import in imports { + if !import.is_from_store(store) { + return Err(InstantiationError::DifferentStores); + } + } + let signal_handler = store.as_store_ref().signal_handler(); + let mut store_mut = store.as_store_mut(); + let (engine, objects) = store_mut.engine_and_objects_mut(); + let config = engine.tunables().vmconfig(); + unsafe { + let mut instance_handle = self.artifact.instantiate( + engine.tunables(), + &imports + .iter() + .map(crate::Extern::to_vm_extern) + .collect::>(), + objects, + )?; + + // After the instance handle is created, we need to initialize + // the data, call the start function and so. However, if any + // of this steps traps, we still need to keep the instance alive + // as some of the Instance elements may have placed in other + // instance tables. + self.artifact + .finish_instantiation(config, signal_handler, &mut instance_handle)?; + + Ok(instance_handle) + } + } + + pub(crate) fn name(&self) -> Option<&str> { + self.info().name.as_deref() + } + + pub(crate) fn set_name(&mut self, name: &str) -> bool { + Arc::get_mut(&mut self.artifact).map_or(false, |artifact| { + artifact.set_module_info_name(name.to_string()) + }) + } + + pub(crate) fn imports(&self) -> ImportsIterator + '_> { + self.info().imports() + } + + pub(crate) fn exports(&self) -> ExportsIterator + '_> { + self.info().exports() + } + + pub(crate) fn custom_sections<'a>( + &'a self, + name: &'a str, + ) -> impl Iterator> + 'a { + self.info().custom_sections(name) + } + + pub(crate) fn info(&self) -> &ModuleInfo { + self.artifact.module_info() + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/tunables.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/tunables.rs new file mode 100644 index 0000000..df63e3b --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/tunables.rs @@ -0,0 +1,471 @@ +pub use wasmer_compiler::BaseTunables; + +// All BaseTunable definition now is in wasmer_compile crate +// Tests are still here + +#[cfg(test)] +mod tests { + use super::*; + #[allow(unused)] + use crate::sys::NativeEngineExt; + use crate::TableType; + use std::cell::UnsafeCell; + use std::ptr::NonNull; + use wasmer_compiler::Tunables; + use wasmer_types::{MemoryType, Pages, WASM_PAGE_SIZE}; + use wasmer_vm::{ + LinearMemory, MemoryError, MemoryStyle, TableStyle, VMConfig, VMMemory, VMMemoryDefinition, + VMTable, VMTableDefinition, + }; + + #[test] + fn memory_style() { + let tunables = BaseTunables { + static_memory_bound: Pages(2048), + static_memory_offset_guard_size: 128, + dynamic_memory_offset_guard_size: 256, + }; + + // No maximum + let requested = MemoryType::new(3, None, true); + let style = tunables.memory_style(&requested); + match style { + MemoryStyle::Dynamic { offset_guard_size } => assert_eq!(offset_guard_size, 256), + s => panic!("Unexpected memory style: {:?}", s), + } + + // Large maximum + let requested = MemoryType::new(3, Some(5_000_000), true); + let style = tunables.memory_style(&requested); + match style { + MemoryStyle::Dynamic { offset_guard_size } => assert_eq!(offset_guard_size, 256), + s => panic!("Unexpected memory style: {:?}", s), + } + + // Small maximum + let requested = MemoryType::new(3, Some(16), true); + let style = tunables.memory_style(&requested); + match style { + MemoryStyle::Static { + bound, + offset_guard_size, + } => { + assert_eq!(bound, Pages(2048)); + assert_eq!(offset_guard_size, 128); + } + s => panic!("Unexpected memory style: {:?}", s), + } + } + + #[derive(Debug)] + struct VMTinyMemory { + mem: Vec, + memory_definition: Option>, + } + + unsafe impl Send for VMTinyMemory {} + unsafe impl Sync for VMTinyMemory {} + + impl VMTinyMemory { + pub fn new() -> Result { + let sz = 18 * WASM_PAGE_SIZE; + let mut memory = Vec::new(); + memory.resize(sz, 0); + let mut ret = Self { + mem: memory, + memory_definition: None, + }; + ret.memory_definition = Some(UnsafeCell::new(VMMemoryDefinition { + base: ret.mem.as_ptr() as _, + current_length: sz, + })); + Ok(ret) + } + } + + impl LinearMemory for VMTinyMemory { + fn ty(&self) -> MemoryType { + MemoryType { + minimum: Pages::from(18u32), + maximum: Some(Pages::from(18u32)), + shared: false, + } + } + fn size(&self) -> Pages { + Pages::from(18u32) + } + fn style(&self) -> MemoryStyle { + MemoryStyle::Static { + bound: Pages::from(18u32), + offset_guard_size: 0, + } + } + fn grow(&mut self, delta: Pages) -> Result { + Err(MemoryError::CouldNotGrow { + current: Pages::from(100u32), + attempted_delta: delta, + }) + } + + fn grow_at_least(&mut self, min_size: u64) -> Result<(), MemoryError> { + let cur_size = self.size().0 as u64 * WASM_PAGE_SIZE as u64; + if min_size > cur_size { + let delta = min_size - cur_size; + return Err(MemoryError::CouldNotGrow { + current: Pages::from(100u32), + attempted_delta: Pages(delta as u32), + }); + } + Ok(()) + } + + fn reset(&mut self) -> Result<(), MemoryError> { + self.mem.fill(0); + Ok(()) + } + + fn vmmemory(&self) -> NonNull { + unsafe { + NonNull::new( + self.memory_definition + .as_ref() + .unwrap() + .get() + .as_mut() + .unwrap() as _, + ) + .unwrap() + } + } + + fn try_clone(&self) -> Result, MemoryError> { + Err(MemoryError::InvalidMemory { + reason: "VMTinyMemory can not be cloned".to_string(), + }) + } + + fn copy(&mut self) -> Result, MemoryError> { + let mem = self.mem.clone(); + Ok(Box::new(Self { + memory_definition: Some(UnsafeCell::new(VMMemoryDefinition { + base: mem.as_ptr() as _, + current_length: mem.len(), + })), + mem, + })) + } + /* + // this code allow custom memory to be ignoring init_memory + use wasmer_vm::Trap; + unsafe fn initialize_with_data(&self, _start: usize, _data: &[u8]) -> Result<(), Trap> { + Ok(()) + } + */ + } + + impl From for wasmer_vm::VMMemory { + fn from(mem: VMTinyMemory) -> Self { + Self(Box::new(mem)) + } + } + + struct TinyTunables; + impl Tunables for TinyTunables { + fn memory_style(&self, _memory: &MemoryType) -> MemoryStyle { + MemoryStyle::Static { + bound: Pages::from(18u32), + offset_guard_size: 0, + } + } + + /// Construct a `TableStyle` for the provided `TableType` + fn table_style(&self, _table: &TableType) -> TableStyle { + TableStyle::CallerChecksSignature + } + fn create_host_memory( + &self, + _ty: &MemoryType, + _style: &MemoryStyle, + ) -> Result { + let memory = VMTinyMemory::new().unwrap(); + Ok(VMMemory::from_custom(memory)) + } + unsafe fn create_vm_memory( + &self, + _ty: &MemoryType, + _style: &MemoryStyle, + vm_definition_location: NonNull, + ) -> Result { + let memory = VMTinyMemory::new().unwrap(); + // now, it's important to update vm_definition_location with the memory information! + let mut ptr = vm_definition_location; + let md = ptr.as_mut(); + let unsafecell = memory.memory_definition.as_ref().unwrap(); + let def = unsafecell.get().as_ref().unwrap(); + md.base = def.base; + md.current_length = def.current_length; + Ok(memory.into()) + } + + /// Create a table owned by the host given a [`TableType`] and a [`TableStyle`]. + fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result { + VMTable::new(ty, style) + } + + /// Create a table owned by the VM given a [`TableType`] and a [`TableStyle`]. + /// + /// # Safety + /// - `vm_definition_location` must point to a valid location in VM memory. + unsafe fn create_vm_table( + &self, + ty: &TableType, + style: &TableStyle, + vm_definition_location: NonNull, + ) -> Result { + VMTable::from_definition(ty, style, vm_definition_location) + } + + // Will use a minimum stack size of 8kb, not the 1Mb default + fn vmconfig(&self) -> &crate::vm::VMConfig { + &VMConfig { + wasm_stack_size: Some(8 * 1024), + } + } + } + + #[test] + fn check_linearmemory() { + let tunables = TinyTunables {}; + let vmmemory = tunables.create_host_memory( + &MemoryType::new(1u32, Some(100u32), true), + &MemoryStyle::Static { + bound: Pages::from(18u32), + offset_guard_size: 0u64, + }, + ); + let mut vmmemory = vmmemory.unwrap(); + assert!(vmmemory.grow(Pages::from(50u32)).is_err()); + assert_eq!(vmmemory.size(), Pages::from(18u32)); + assert_eq!( + vmmemory.grow(Pages::from(0u32)).err().unwrap(), + MemoryError::CouldNotGrow { + current: Pages::from(100u32), + attempted_delta: Pages::from(0u32) + } + ); + } + + #[test] + fn check_customtunables() -> Result<(), Box> { + use crate::{imports, wat2wasm, Engine, Instance, Memory, Module, Store}; + use wasmer_compiler_cranelift::Cranelift; + + let wasm_bytes = wat2wasm( + br#"(module + (memory (;0;) 18) + (global (;0;) (mut i32) i32.const 1048576) + (export "memory" (memory 0)) + (data (;0;) (i32.const 1048576) "*\00\00\00") + )"#, + )?; + let compiler = Cranelift::default(); + + let tunables = TinyTunables {}; + #[allow(deprecated)] + let mut engine = Engine::new(compiler.into(), Default::default(), Default::default()); + engine.set_tunables(tunables); + let mut store = Store::new(engine); + //let mut store = Store::new(compiler); + let module = Module::new(&store, wasm_bytes)?; + let import_object = imports! {}; + let instance = Instance::new(&mut store, &module, &import_object)?; + + let mut memories: Vec = instance + .exports + .iter() + .memories() + .map(|pair| pair.1.clone()) + .collect(); + assert_eq!(memories.len(), 1); + let first_memory = memories.pop().unwrap(); + assert_eq!(first_memory.ty(&store).maximum.unwrap(), Pages(18)); + let view = first_memory.view(&store); + let x = unsafe { view.data_unchecked_mut() }[0]; + assert_eq!(x, 0); + + Ok(()) + } + + #[test] + #[cfg(all( + feature = "singlepass", + not(any( + target_os = "windows", + all(target_os = "macos", target_arch = "aarch64") + )) + ))] + fn check_small_stack() -> Result<(), Box> { + use crate::{imports, wat2wasm, Engine, Instance, Module, Store}; + use wasmer_compiler_singlepass::Singlepass; + // This test needs Singlepass compiler + // because Cranelift will optimize the webassembly file + // and remove all the unused local, even at optimization level "None" + // But this test needs the huge amount of locals (1024 + a few) + // so that the small stack is overflown (stack is only 8K, 1024 i64 local = 8K) + // tWindows is disable as it seems Stack frame protection is not 100% efficient + let wasm_bytes = wat2wasm( + br#"(module + (func (;0;) (result i64) + (local i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64) + i64.const 0 + i64.const 5555 + i64.add + local.set 8 + i64.const 0 + i64.const 5555 + i64.add + local.set 9 + i64.const 0 + i64.const 5555 + i64.add + local.set 10 + local.get 10 + ) + (func $large_local (export "large_local") (result i64) + (local + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + + i64 + ) + (local.set 0 (i64.const 1)) + (local.set 1 (i64.const 1)) + (local.set 2 (i64.const 1)) + (local.set 3 (i64.const 1)) + (local.set 1024 (i64.const 2)) + (call 0) + local.set 1024 + local.get 6 + local.get 7 + i64.add + local.get 8 + i64.add + (call 0) + local.set 10 + local.get 9 + i64.add + local.get 10 + i64.add + local.get 11 + i64.add + local.get 12 + i64.add + (call 0) + local.set 512 + local.get 13 + i64.add + local.get 14 + i64.add + local.get 15 + i64.add + local.get 1024 + i64.add + local.get 0 + i64.add + ) + ) + "#, + )?; + let compiler = Singlepass::default(); + + let tunables = TinyTunables {}; + #[allow(deprecated)] + let mut engine = Engine::new(compiler.into(), Default::default(), Default::default()); + engine.set_tunables(tunables); + let mut store = Store::new(engine); + let module = Module::new(&store, wasm_bytes)?; + let import_object = imports! {}; + let instance = Instance::new(&mut store, &module, &import_object)?; + + let result = instance + .exports + .get_function("large_local")? + .call(&mut store, &[]); + + println!("result = {:?}", result); + assert!(result.is_err()); + + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/typed_function.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/typed_function.rs new file mode 100644 index 0000000..1ff5d25 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/sys/typed_function.rs @@ -0,0 +1,216 @@ +use crate::{FromToNativeWasmType, RuntimeError, TypedFunction, WasmTypeList}; +use wasmer_types::RawValue; + +use crate::native_type::NativeWasmTypeInto; +use crate::store::{AsStoreMut, AsStoreRef}; +use crate::sys::engine::NativeEngineExt; + +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(unused_mut)] + #[allow(clippy::too_many_arguments)] + pub fn call(&self, store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result { + let anyfunc = unsafe { + *self.func.0 + .handle + .get(store.as_store_ref().objects()) + .anyfunc + .as_ptr() + .as_ref() + }; + // Ensure all parameters come from the same context. + if $(!FromToNativeWasmType::is_from_store(&$x, store) ||)* false { + return Err(RuntimeError::new( + "cross-`Store` values are not supported", + )); + } + // TODO: when `const fn` related features mature more, we can declare a single array + // of the correct size here. + let mut params_list = [ $( $x.to_native().into_raw(store) ),* ]; + let mut rets_list_array = Rets::empty_array(); + let rets_list: &mut [RawValue] = rets_list_array.as_mut(); + let using_rets_array; + let args_rets: &mut [RawValue] = if params_list.len() > rets_list.len() { + using_rets_array = false; + params_list.as_mut() + } else { + using_rets_array = true; + for (i, &arg) in params_list.iter().enumerate() { + rets_list[i] = arg; + } + rets_list.as_mut() + }; + + let mut r; + loop { + let storeref = store.as_store_ref(); + let config = storeref.engine().tunables().vmconfig(); + r = unsafe { + wasmer_vm::wasmer_call_trampoline( + store.as_store_ref().signal_handler(), + config, + anyfunc.vmctx, + anyfunc.call_trampoline, + anyfunc.func_ptr, + args_rets.as_mut_ptr() as *mut u8, + ) + }; + let store_mut = store.as_store_mut(); + 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 num_rets = rets_list.len(); + if !using_rets_array && num_rets > 0 { + let src_pointer = params_list.as_ptr(); + let rets_list = &mut rets_list_array.as_mut()[0] as *mut RawValue; + unsafe { + // TODO: we can probably remove this copy by doing some clever `transmute`s. + // we know it's not overlapping because `using_rets_array` is false + std::ptr::copy_nonoverlapping(src_pointer, + rets_list, + num_rets); + } + } + Ok(unsafe { Rets::from_array(store, rets_list_array) }) + // TODO: When the Host ABI and Wasm ABI are the same, we could do this instead: + // but we can't currently detect whether that's safe. + // + // let results = unsafe { + // wasmer_vm::catch_traps_with_result(self.vmctx, || { + // let f = std::mem::transmute::<_, unsafe extern "C" fn( *mut VMContext, $( $x, )*) -> Rets::CStruct>(self.address()); + // // We always pass the vmctx + // f( self.vmctx, $( $x, )* ) + // }).map_err(RuntimeError::from_trap)? + // }; + // Ok(Rets::from_c_struct(results)) + } + + #[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 { + let anyfunc = unsafe { + *self.func.0 + .handle + .get(store.as_store_ref().objects()) + .anyfunc + .as_ptr() + .as_ref() + }; + // TODO: when `const fn` related features mature more, we can declare a single array + // of the correct size here. + let mut rets_list_array = Rets::empty_array(); + let rets_list: &mut [RawValue] = rets_list_array.as_mut(); + let using_rets_array; + let args_rets: &mut [RawValue] = if params_list.len() > rets_list.len() { + using_rets_array = false; + params_list.as_mut() + } else { + using_rets_array = true; + for (i, &arg) in params_list.iter().enumerate() { + rets_list[i] = arg; + } + rets_list.as_mut() + }; + + let mut r; + loop { + let storeref = store.as_store_ref(); + let config = storeref.engine().tunables().vmconfig(); + r = unsafe { + wasmer_vm::wasmer_call_trampoline( + store.as_store_ref().signal_handler(), + config, + anyfunc.vmctx, + anyfunc.call_trampoline, + anyfunc.func_ptr, + args_rets.as_mut_ptr() as *mut u8, + ) + }; + let store_mut = store.as_store_mut(); + if let Some(callback) = store_mut.inner.on_called.take() { + // TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 + 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 num_rets = rets_list.len(); + if !using_rets_array && num_rets > 0 { + let src_pointer = params_list.as_ptr(); + let rets_list = &mut rets_list_array.as_mut()[0] as *mut RawValue; + unsafe { + // TODO: we can probably remove this copy by doing some clever `transmute`s. + // we know it's not overlapping because `using_rets_array` is false + std::ptr::copy_nonoverlapping(src_pointer, + rets_list, + num_rets); + } + } + Ok(unsafe { Rets::from_array(store, rets_list_array) }) + // TODO: When the Host ABI and Wasm ABI are the same, we could do this instead: + // but we can't currently detect whether that's safe. + // + // let results = unsafe { + // wasmer_vm::catch_traps_with_result(self.vmctx, || { + // let f = std::mem::transmute::<_, unsafe extern "C" fn( *mut VMContext, $( $x, )*) -> Rets::CStruct>(self.address()); + // // We always pass the vmctx + // f( self.vmctx, $( $x, )* ) + // }).map_err(RuntimeError::from_trap)? + // }; + // Ok(Rets::from_c_struct(results)) + } + } + }; +} + +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/arbitrator/tools/module_roots/wasmer/lib/api/src/typed_function.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/typed_function.rs new file mode 100644 index 0000000..ccaadcf --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/typed_function.rs @@ -0,0 +1,42 @@ +//! 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(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/arbitrator/tools/module_roots/wasmer/lib/api/src/value.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/value.rs new file mode 100644 index 0000000..15ec16e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/value.rs @@ -0,0 +1,475 @@ +use std::convert::TryFrom; +use std::fmt; +use std::string::{String, ToString}; + +use wasmer_types::Type; + +use crate::vm::{VMExternRef, VMFuncRef}; + +use crate::ExternRef; +use crate::Function; + +use crate::store::AsStoreRef; + +pub use wasmer_types::RawValue; + +/// WebAssembly computations manipulate values of basic value types: +/// * Integers (32 or 64 bit width) +/// * Floating-point (32 or 64 bit width) +/// * Vectors (128 bits, with 32 or 64 bit lanes) +/// +/// Spec: +#[derive(Clone)] +pub enum Value { + /// A 32-bit integer. + /// + /// In Wasm integers are sign-agnostic, i.e. this can either be signed or unsigned. + I32(i32), + + /// A 64-bit integer. + /// + /// In Wasm integers are sign-agnostic, i.e. this can either be signed or unsigned. + I64(i64), + + /// A 32-bit float. + F32(f32), + + /// A 64-bit float. + F64(f64), + + /// An `externref` value which can hold opaque data to the wasm instance itself. + ExternRef(Option), + + /// A first-class reference to a WebAssembly function. + FuncRef(Option), + + /// A 128-bit number + V128(u128), +} + +macro_rules! accessors { + ($bind:ident $(($variant:ident($ty:ty) $get:ident $unwrap:ident $cvt:expr))*) => ($( + /// Attempt to access the underlying value of this `Value`, returning + /// `None` if it is not the correct type. + pub fn $get(&self) -> Option<$ty> { + if let Self::$variant($bind) = self { + Some($cvt) + } else { + None + } + } + + /// Returns the underlying value of this `Value`, panicking if it's the + /// wrong type. + /// + /// # Panics + /// + /// Panics if `self` is not of the right type. + pub fn $unwrap(&self) -> $ty { + self.$get().expect(concat!("expected ", stringify!($ty))) + } + )*) +} + +impl Value { + /// Returns a null `externref` value. + pub fn null() -> Self { + Self::ExternRef(None) + } + + /// Returns the corresponding [`Type`] for this `Value`. + pub fn ty(&self) -> Type { + match self { + Self::I32(_) => Type::I32, + Self::I64(_) => Type::I64, + Self::F32(_) => Type::F32, + Self::F64(_) => Type::F64, + Self::ExternRef(_) => Type::ExternRef, + Self::FuncRef(_) => Type::FuncRef, + Self::V128(_) => Type::V128, + } + } + + /// Converts the `Value` into a `RawValue`. + pub fn as_raw(&self, store: &impl AsStoreRef) -> RawValue { + match *self { + Self::I32(i32) => RawValue { i32 }, + Self::I64(i64) => RawValue { i64 }, + Self::F32(f32) => RawValue { f32 }, + 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 }, + } + } + + /// Converts a `RawValue` to a `Value`. + /// + /// # Safety + /// + pub unsafe fn from_raw(store: &mut impl crate::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)), + ), + } + } + + /// Checks whether a value 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 self { + Self::I32(_) + | Self::I64(_) + | Self::F32(_) + | Self::F64(_) + | Self::V128(_) + | Self::ExternRef(None) + | Self::FuncRef(None) => true, + Self::ExternRef(Some(e)) => e.is_from_store(store), + Self::FuncRef(Some(f)) => f.is_from_store(store), + } + } + + accessors! { + e + (I32(i32) i32 unwrap_i32 *e) + (I64(i64) i64 unwrap_i64 *e) + (F32(f32) f32 unwrap_f32 *e) + (F64(f64) f64 unwrap_f64 *e) + (ExternRef(&Option) externref unwrap_externref e) + (FuncRef(&Option) funcref unwrap_funcref e) + (V128(u128) v128 unwrap_v128 *e) + } +} + +impl fmt::Debug for Value { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::I32(v) => write!(f, "I32({:?})", v), + Self::I64(v) => write!(f, "I64({:?})", v), + Self::F32(v) => write!(f, "F32({:?})", v), + Self::F64(v) => write!(f, "F64({:?})", v), + Self::ExternRef(None) => write!(f, "Null ExternRef"), + Self::ExternRef(Some(v)) => write!(f, "ExternRef({:?})", v), + Self::FuncRef(None) => write!(f, "Null FuncRef"), + Self::FuncRef(Some(v)) => write!(f, "FuncRef({:?})", v), + Self::V128(v) => write!(f, "V128({:?})", v), + } + } +} + +impl ToString for Value { + fn to_string(&self) -> String { + match self { + Self::I32(v) => v.to_string(), + Self::I64(v) => v.to_string(), + Self::F32(v) => v.to_string(), + Self::F64(v) => v.to_string(), + Self::ExternRef(_) => "externref".to_string(), + Self::FuncRef(_) => "funcref".to_string(), + Self::V128(v) => v.to_string(), + } + } +} + +impl PartialEq for Value { + fn eq(&self, o: &Self) -> bool { + match (self, o) { + (Self::I32(a), Self::I32(b)) => a == b, + (Self::I64(a), Self::I64(b)) => a == b, + (Self::F32(a), Self::F32(b)) => a == b, + (Self::F64(a), Self::F64(b)) => a == b, + (Self::V128(a), Self::V128(b)) => a == b, + _ => false, + } + } +} + +impl From for Value { + fn from(val: i32) -> Self { + Self::I32(val) + } +} + +impl From for Value { + fn from(val: u32) -> Self { + // In Wasm integers are sign-agnostic, so i32 is basically a 4 byte storage we can use for signed or unsigned 32-bit integers. + Self::I32(val as i32) + } +} + +impl From for Value { + fn from(val: i64) -> Self { + Self::I64(val) + } +} + +impl From for Value { + fn from(val: u64) -> Self { + // In Wasm integers are sign-agnostic, so i64 is basically an 8 byte storage we can use for signed or unsigned 64-bit integers. + Self::I64(val as i64) + } +} + +impl From for Value { + fn from(val: f32) -> Self { + Self::F32(val) + } +} + +impl From for Value { + fn from(val: f64) -> Self { + Self::F64(val) + } +} + +impl From for Value { + fn from(val: Function) -> Self { + Self::FuncRef(Some(val)) + } +} + +impl From> for Value { + fn from(val: Option) -> Self { + Self::FuncRef(val) + } +} + +impl From for Value { + fn from(val: ExternRef) -> Self { + Self::ExternRef(Some(val)) + } +} + +impl From> for Value { + fn from(val: Option) -> Self { + Self::ExternRef(val) + } +} + +const NOT_I32: &str = "Value is not of Wasm type i32"; +const NOT_I64: &str = "Value is not of Wasm type i64"; +const NOT_F32: &str = "Value is not of Wasm type f32"; +const NOT_F64: &str = "Value is not of Wasm type f64"; +const NOT_FUNCREF: &str = "Value is not of Wasm type funcref"; +const NOT_EXTERNREF: &str = "Value is not of Wasm type externref"; + +impl TryFrom for i32 { + type Error = &'static str; + + fn try_from(value: Value) -> Result { + value.i32().ok_or(NOT_I32) + } +} + +impl TryFrom for u32 { + type Error = &'static str; + + fn try_from(value: Value) -> Result { + value.i32().ok_or(NOT_I32).map(|int| int as Self) + } +} + +impl TryFrom for i64 { + type Error = &'static str; + + fn try_from(value: Value) -> Result { + value.i64().ok_or(NOT_I64) + } +} + +impl TryFrom for u64 { + type Error = &'static str; + + fn try_from(value: Value) -> Result { + value.i64().ok_or(NOT_I64).map(|int| int as Self) + } +} + +impl TryFrom for f32 { + type Error = &'static str; + + fn try_from(value: Value) -> Result { + value.f32().ok_or(NOT_F32) + } +} + +impl TryFrom for f64 { + type Error = &'static str; + + fn try_from(value: Value) -> Result { + value.f64().ok_or(NOT_F64) + } +} + +impl TryFrom for Option { + type Error = &'static str; + + fn try_from(value: Value) -> Result { + match value { + Value::FuncRef(f) => Ok(f), + _ => Err(NOT_FUNCREF), + } + } +} + +impl TryFrom for Option { + type Error = &'static str; + + fn try_from(value: Value) -> Result { + match value { + Value::ExternRef(e) => Ok(e), + _ => Err(NOT_EXTERNREF), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_value_i32_from_u32() { + let bytes = [0x00, 0x00, 0x00, 0x00]; + let v = Value::from(u32::from_be_bytes(bytes)); + assert_eq!(v, Value::I32(i32::from_be_bytes(bytes))); + + let bytes = [0x00, 0x00, 0x00, 0x01]; + let v = Value::from(u32::from_be_bytes(bytes)); + assert_eq!(v, Value::I32(i32::from_be_bytes(bytes))); + + let bytes = [0xAA, 0xBB, 0xCC, 0xDD]; + let v = Value::from(u32::from_be_bytes(bytes)); + assert_eq!(v, Value::I32(i32::from_be_bytes(bytes))); + + let bytes = [0xFF, 0xFF, 0xFF, 0xFF]; + let v = Value::from(u32::from_be_bytes(bytes)); + assert_eq!(v, Value::I32(i32::from_be_bytes(bytes))); + } + + #[test] + fn test_value_i64_from_u64() { + let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + let v = Value::from(u64::from_be_bytes(bytes)); + assert_eq!(v, Value::I64(i64::from_be_bytes(bytes))); + + let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]; + let v = Value::from(u64::from_be_bytes(bytes)); + assert_eq!(v, Value::I64(i64::from_be_bytes(bytes))); + + let bytes = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11]; + let v = Value::from(u64::from_be_bytes(bytes)); + assert_eq!(v, Value::I64(i64::from_be_bytes(bytes))); + + let bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; + let v = Value::from(u64::from_be_bytes(bytes)); + assert_eq!(v, Value::I64(i64::from_be_bytes(bytes))); + } + + #[test] + fn convert_value_to_i32() { + let value = Value::I32(5678); + let result = i32::try_from(value); + assert_eq!(result.unwrap(), 5678); + + let value = Value::from(u32::MAX); + let result = i32::try_from(value); + assert_eq!(result.unwrap(), -1); + + let value = Value::V128(42); + let result = i32::try_from(value); + assert_eq!(result.unwrap_err(), "Value is not of Wasm type i32"); + } + + #[test] + fn convert_value_to_u32() { + let value = Value::from(u32::MAX); + let result = u32::try_from(value); + assert_eq!(result.unwrap(), u32::MAX); + + let value = Value::I32(-1); + let result = u32::try_from(value); + assert_eq!(result.unwrap(), u32::MAX); + + let value = Value::V128(42); + let result = u32::try_from(value); + assert_eq!(result.unwrap_err(), "Value is not of Wasm type i32"); + } + + #[test] + fn convert_value_to_i64() { + let value = Value::I64(5678); + let result = i64::try_from(value); + assert_eq!(result.unwrap(), 5678); + + let value = Value::from(u64::MAX); + let result = i64::try_from(value); + assert_eq!(result.unwrap(), -1); + + let value = Value::V128(42); + let result = i64::try_from(value); + assert_eq!(result.unwrap_err(), "Value is not of Wasm type i64"); + } + + #[test] + fn convert_value_to_u64() { + let value = Value::from(u64::MAX); + let result = u64::try_from(value); + assert_eq!(result.unwrap(), u64::MAX); + + let value = Value::I64(-1); + let result = u64::try_from(value); + assert_eq!(result.unwrap(), u64::MAX); + + let value = Value::V128(42); + let result = u64::try_from(value); + assert_eq!(result.unwrap_err(), "Value is not of Wasm type i64"); + } + + #[test] + fn convert_value_to_f32() { + let value = Value::F32(1.234); + let result = f32::try_from(value); + assert_eq!(result.unwrap(), 1.234); + + let value = Value::V128(42); + let result = f32::try_from(value); + assert_eq!(result.unwrap_err(), "Value is not of Wasm type f32"); + + let value = Value::F64(1.234); + let result = f32::try_from(value); + assert_eq!(result.unwrap_err(), "Value is not of Wasm type f32"); + } + + #[test] + fn convert_value_to_f64() { + let value = Value::F64(1.234); + let result = f64::try_from(value); + assert_eq!(result.unwrap(), 1.234); + + let value = Value::V128(42); + let result = f64::try_from(value); + assert_eq!(result.unwrap_err(), "Value is not of Wasm type f64"); + + let value = Value::F32(1.234); + let result = f64::try_from(value); + assert_eq!(result.unwrap_err(), "Value is not of Wasm type f64"); + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/src/vm.rs b/arbitrator/tools/module_roots/wasmer/lib/api/src/vm.rs new file mode 100644 index 0000000..2347c0b --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/src/vm.rs @@ -0,0 +1,40 @@ +//! 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 = "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}; + +// 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/arbitrator/tools/module_roots/wasmer/lib/api/tests/externals.rs b/arbitrator/tools/module_roots/wasmer/lib/api/tests/externals.rs new file mode 100644 index 0000000..9a991dc --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/tests/externals.rs @@ -0,0 +1,402 @@ +use macro_wasmer_universal_test::universal_test; +#[cfg(feature = "js")] +use wasm_bindgen_test::*; + +use wasmer::*; + +#[universal_test] +fn global_new() -> Result<(), String> { + let mut store = Store::default(); + let global = Global::new(&mut store, Value::I32(10)); + assert_eq!( + global.ty(&store), + GlobalType { + ty: Type::I32, + mutability: Mutability::Const + } + ); + + let global_mut = Global::new_mut(&mut store, Value::I32(10)); + assert_eq!( + global_mut.ty(&store), + GlobalType { + ty: Type::I32, + mutability: Mutability::Var + } + ); + + Ok(()) +} + +#[universal_test] +fn global_get() -> Result<(), String> { + let mut store = Store::default(); + + let global_i32 = Global::new(&mut store, Value::I32(10)); + assert_eq!(global_i32.get(&mut store), Value::I32(10)); + + // 64-bit values are not yet fully supported in some versions of Node + #[cfg(feature = "sys")] + { + let global_i64 = Global::new(&mut store, Value::I64(20)); + assert_eq!(global_i64.get(&mut store), Value::I64(20)); + } + + let global_f32 = Global::new(&mut store, Value::F32(10.0)); + assert_eq!(global_f32.get(&mut store), Value::F32(10.0)); + + // 64-bit values are not yet fully supported in some versions of Node + #[cfg(feature = "sys")] + { + let global_f64 = Global::new(&mut store, Value::F64(20.0)); + assert_eq!(global_f64.get(&mut store), Value::F64(20.0)); + } + + Ok(()) +} + +#[universal_test] +fn global_set() -> Result<(), String> { + let mut store = Store::default(); + let global_i32 = Global::new(&mut store, Value::I32(10)); + // Set on a constant should error + assert!(global_i32.set(&mut store, Value::I32(20)).is_err()); + + let global_i32_mut = Global::new_mut(&mut store, Value::I32(10)); + // Set on different type should error + assert!(global_i32_mut.set(&mut store, Value::I64(20)).is_err()); + + // Set on same type should succeed + global_i32_mut + .set(&mut store, Value::I32(20)) + .map_err(|e| format!("{e:?}"))?; + assert_eq!(global_i32_mut.get(&mut store), Value::I32(20)); + + Ok(()) +} + +#[universal_test] +fn table_new() -> Result<(), String> { + let mut store = Store::default(); + let table_type = TableType { + ty: Type::FuncRef, + minimum: 0, + maximum: None, + }; + let f = Function::new_typed(&mut store, || {}); + let table = Table::new(&mut store, table_type, Value::FuncRef(Some(f))) + .map_err(|e| format!("{e:?}"))?; + assert_eq!(table.ty(&store), table_type); + + // Anyrefs not yet supported + // let table_type = TableType { + // ty: Type::ExternRef, + // minimum: 0, + // maximum: None, + // }; + // let table = Table::new(&store, table_type, Value::ExternRef(ExternRef::Null)).map_err(|e| format!("{e:?}"))?; + // assert_eq!(*table.ty(), table_type); + + Ok(()) +} + +#[universal_test] +fn table_get() -> Result<(), String> { + // Tables are not yet fully supported in Wasm + // This test was marked as #[ignore] on -sys, which is why it is commented out. + + // let mut store = Store::default(); + // let table_type = TableType { + // ty: Type::FuncRef, + // minimum: 0, + // maximum: Some(1), + // }; + // let f = Function::new_typed(&mut store, |num: i32| num + 1); + // let table = Table::new(&mut store, table_type, Value::FuncRef(Some(f))) + // .map_err(|e| format!("{e:?}"))?; + // assert_eq!(table.ty(&mut store), table_type); + // let _elem = table.get(&mut store, 0).unwrap(); + // assert_eq!(elem.funcref().unwrap(), f); + + Ok(()) +} + +#[universal_test] +fn table_set() -> Result<(), String> { + // Table set not yet tested + Ok(()) +} + +#[universal_test] +fn table_grow() -> Result<(), String> { + // Tables are not yet fully supported in Wasm + #[cfg(feature = "sys")] + { + let mut store = Store::default(); + let table_type = TableType { + ty: Type::FuncRef, + minimum: 0, + maximum: Some(10), + }; + let f = Function::new_typed(&mut store, |num: i32| num + 1); + let table = Table::new(&mut store, table_type, Value::FuncRef(Some(f.clone()))) + .map_err(|e| format!("{e:?}"))?; + // Growing to a bigger maximum should return None + let old_len = table.grow(&mut store, 12, Value::FuncRef(Some(f.clone()))); + assert!(old_len.is_err()); + + // Growing to a bigger maximum should return None + let old_len = table + .grow(&mut store, 5, Value::FuncRef(Some(f))) + .map_err(|e| format!("{e:?}"))?; + assert_eq!(old_len, 0); + } + + Ok(()) +} + +#[universal_test] +fn table_copy() -> Result<(), String> { + // TODO: table copy test not yet implemented + Ok(()) +} + +#[universal_test] +fn memory_new() -> Result<(), String> { + let mut store = Store::default(); + let memory_type = MemoryType { + shared: false, + minimum: Pages(0), + maximum: Some(Pages(10)), + }; + let memory = Memory::new(&mut store, memory_type).map_err(|e| format!("{e:?}"))?; + assert_eq!(memory.view(&store).size(), Pages(0)); + assert_eq!(memory.ty(&store), memory_type); + Ok(()) +} + +#[universal_test] +fn memory_grow() -> Result<(), String> { + let mut store = Store::default(); + let desc = MemoryType::new(Pages(10), Some(Pages(16)), false); + let memory = Memory::new(&mut store, desc).map_err(|e| format!("{e:?}"))?; + assert_eq!(memory.view(&store).size(), Pages(10)); + + let result = memory.grow(&mut store, Pages(2)).unwrap(); + assert_eq!(result, Pages(10)); + assert_eq!(memory.view(&store).size(), Pages(12)); + + let result = memory.grow(&mut store, Pages(10)); + assert_eq!( + result, + Err(MemoryError::CouldNotGrow { + current: 12.into(), + attempted_delta: 10.into() + }) + ); + + // JS will never give BadMemory unless V8 is broken somehow + #[cfg(feature = "sys")] + { + let bad_desc = MemoryType::new(Pages(15), Some(Pages(10)), false); + let bad_result = Memory::new(&mut store, bad_desc); + assert!(matches!(bad_result, Err(MemoryError::InvalidMemory { .. }))); + } + + Ok(()) +} + +#[universal_test] +fn function_new() -> Result<(), String> { + let mut store = Store::default(); + let function = Function::new_typed(&mut store, || {}); + assert_eq!(function.ty(&store), FunctionType::new(vec![], vec![])); + let function = Function::new_typed(&mut store, |_a: i32| {}); + assert_eq!( + function.ty(&store), + FunctionType::new(vec![Type::I32], vec![]) + ); + let function = Function::new_typed(&mut store, |_a: i32, _b: i64, _c: f32, _d: f64| {}); + assert_eq!( + function.ty(&store), + FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]) + ); + let function = Function::new_typed(&mut store, || -> i32 { 1 }); + assert_eq!( + function.ty(&store), + FunctionType::new(vec![], vec![Type::I32]) + ); + let function = Function::new_typed(&mut store, || -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) }); + assert_eq!( + function.ty(&store), + FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]) + ); + Ok(()) +} + +#[universal_test] +fn function_new_env() -> Result<(), String> { + let mut store = Store::default(); + #[derive(Clone)] + struct MyEnv {} + + let my_env = MyEnv {}; + let env = FunctionEnv::new(&mut store, my_env); + let function = Function::new_typed_with_env(&mut store, &env, |_env: FunctionEnvMut| {}); + assert_eq!(function.ty(&store), FunctionType::new(vec![], vec![])); + let function = + Function::new_typed_with_env(&mut store, &env, |_env: FunctionEnvMut, _a: i32| {}); + assert_eq!( + function.ty(&store), + FunctionType::new(vec![Type::I32], vec![]) + ); + let function = Function::new_typed_with_env( + &mut store, + &env, + |_env: FunctionEnvMut, _a: i32, _b: i64, _c: f32, _d: f64| {}, + ); + assert_eq!( + function.ty(&store), + FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]) + ); + let function = + Function::new_typed_with_env(&mut store, &env, |_env: FunctionEnvMut| -> i32 { 1 }); + assert_eq!( + function.ty(&store), + FunctionType::new(vec![], vec![Type::I32]) + ); + let function = Function::new_typed_with_env( + &mut store, + &env, + |_env: FunctionEnvMut| -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) }, + ); + assert_eq!( + function.ty(&store), + FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]) + ); + Ok(()) +} + +#[universal_test] +fn function_new_dynamic() -> Result<(), String> { + let mut store = Store::default(); + + // Using &FunctionType signature + let function_type = FunctionType::new(vec![], vec![]); + let function = Function::new( + &mut store, + &function_type, + |_values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&store), function_type); + let function_type = FunctionType::new(vec![Type::I32], vec![]); + let function = Function::new( + &mut store, + &function_type, + |_values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&store), function_type); + let function_type = FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]); + let function = Function::new( + &mut store, + &function_type, + |_values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&store), function_type); + let function_type = FunctionType::new(vec![], vec![Type::I32]); + let function = Function::new( + &mut store, + &function_type, + |_values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&store), function_type); + let function_type = FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]); + let function = Function::new( + &mut store, + &function_type, + |_values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&store), function_type); + + // Using array signature + let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]); + let function = Function::new( + &mut store, + function_type, + |_values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&store).params(), [Type::V128]); + assert_eq!( + function.ty(&store).results(), + [Type::I32, Type::F32, Type::F64] + ); + + Ok(()) +} + +#[universal_test] +fn function_new_dynamic_env() -> Result<(), String> { + let mut store = Store::default(); + #[derive(Clone)] + struct MyEnv {} + let my_env = MyEnv {}; + let env = FunctionEnv::new(&mut store, my_env); + + // Using &FunctionType signature + let function_type = FunctionType::new(vec![], vec![]); + let function = Function::new_with_env( + &mut store, + &env, + &function_type, + |_env: FunctionEnvMut, _values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&store), function_type); + let function_type = FunctionType::new(vec![Type::I32], vec![]); + let function = Function::new_with_env( + &mut store, + &env, + &function_type, + |_env: FunctionEnvMut, _values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&store), function_type); + let function_type = FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]); + let function = Function::new_with_env( + &mut store, + &env, + &function_type, + |_env: FunctionEnvMut, _values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&store), function_type); + let function_type = FunctionType::new(vec![], vec![Type::I32]); + let function = Function::new_with_env( + &mut store, + &env, + &function_type, + |_env: FunctionEnvMut, _values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&store), function_type); + let function_type = FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]); + let function = Function::new_with_env( + &mut store, + &env, + &function_type, + |_env: FunctionEnvMut, _values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&store), function_type); + + // Using array signature + let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]); + let function = Function::new_with_env( + &mut store, + &env, + function_type, + |_env: FunctionEnvMut, _values: &[Value]| unimplemented!(), + ); + assert_eq!(function.ty(&store).params(), [Type::V128]); + assert_eq!( + function.ty(&store).results(), + [Type::I32, Type::F32, Type::F64] + ); + + Ok(()) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/tests/function_env.rs b/arbitrator/tools/module_roots/wasmer/lib/api/tests/function_env.rs new file mode 100644 index 0000000..87e1904 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/tests/function_env.rs @@ -0,0 +1,43 @@ +use macro_wasmer_universal_test::universal_test; +#[cfg(feature = "js")] +use wasm_bindgen_test::*; + +use wasmer::*; + +#[universal_test] +fn data_and_store_mut() -> Result<(), String> { + let mut store = Store::default(); + let global_mut = Global::new_mut(&mut store, Value::I32(10)); + struct Env { + value: i32, + global: Global, + } + let env = FunctionEnv::new( + &mut store, + Env { + value: 0i32, + global: global_mut, + }, + ); + let mut envmut = env.into_mut(&mut store); + + let (mut data, mut storemut) = envmut.data_and_store_mut(); + + assert_eq!( + data.global.ty(&storemut), + GlobalType { + ty: Type::I32, + mutability: Mutability::Var + } + ); + assert_eq!(data.global.get(&mut storemut), Value::I32(10)); + data.value = data.global.get(&mut storemut).unwrap_i32() + 10; + + data.global + .set(&mut storemut, Value::I32(data.value)) + .unwrap(); + + assert_eq!(data.global.get(&mut storemut), Value::I32(data.value)); + + Ok(()) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/tests/import_function.rs b/arbitrator/tools/module_roots/wasmer/lib/api/tests/import_function.rs new file mode 100644 index 0000000..007b82c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/tests/import_function.rs @@ -0,0 +1,151 @@ +use macro_wasmer_universal_test::universal_test; +#[cfg(feature = "js")] +use wasm_bindgen_test::*; + +use anyhow::Result; +use wasmer::*; + +#[universal_test] +fn pass_i64_between_host_and_plugin() -> Result<(), String> { + let mut store = Store::default(); + + let wat = r#"(module + (func $add_one_i64 (import "host" "add_one_i64") (param i64) (result i64)) + (func (export "add_three_i64") (param i64) (result i64) + (i64.add (call $add_one_i64 (i64.add (local.get 0) (i64.const 1))) (i64.const 1)) + ) + )"#; + let module = Module::new(&store, wat).map_err(|e| format!("{e:?}"))?; + + let imports = { + imports! { + "host" => { + "add_one_i64" => Function::new_typed(&mut store, |value: i64| value.wrapping_add(1)), + }, + } + }; + + let instance = Instance::new(&mut store, &module, &imports).map_err(|e| format!("{e:?}"))?; + let add_three_i64 = instance + .exports + .get_typed_function::(&store, "add_three_i64") + .map_err(|e| format!("{e:?}"))?; + + let mut numbers = Vec::::new(); + numbers.extend(-4..=4); + numbers.extend((i64::MAX - 4)..=i64::MAX); + numbers.extend((i64::MIN)..=(i64::MIN + 4)); + + for number in numbers { + let wasm_result = add_three_i64 + .call(&mut store, number) + .map_err(|e| format!("{e:?}"))?; + let compare_result = number.wrapping_add(3); + + assert_eq!(wasm_result, compare_result) + } + Ok(()) +} + +#[universal_test] +fn pass_u64_between_host_and_plugin() -> Result<(), String> { + let mut store = Store::default(); + + let wat = r#"(module + (func $add_one_u64 (import "host" "add_one_u64") (param i64) (result i64)) + (func (export "add_three_u64") (param i64) (result i64) + (i64.add (call $add_one_u64 (i64.add (local.get 0) (i64.const 1))) (i64.const 1)) + ) + )"#; + let module = Module::new(&store, wat).map_err(|e| format!("{e:?}"))?; + + let imports = { + imports! { + "host" => { + "add_one_u64" => Function::new_typed(&mut store, |value: u64| value.wrapping_add(1)), + }, + } + }; + + let instance = Instance::new(&mut store, &module, &imports).map_err(|e| format!("{e:?}"))?; + let add_three_u64 = instance + .exports + .get_typed_function::(&store, "add_three_u64") + .map_err(|e| format!("{e:?}"))?; + + let mut numbers = Vec::::new(); + numbers.extend(0..=4); + numbers.extend((u64::MAX / 2 - 4)..=(u64::MAX / 2 + 4)); + numbers.extend((u64::MAX - 4)..=u64::MAX); + + for number in numbers { + let wasm_result = add_three_u64 + .call(&mut store, number) + .map_err(|e| format!("{e:?}"))?; + let compare_result = number.wrapping_add(3); + + assert_eq!(wasm_result, compare_result) + } + Ok(()) +} + +#[universal_test] +fn calling_function_exports() -> Result<()> { + let mut store = Store::default(); + let wat = r#"(module + (func (export "add") (param $lhs i32) (param $rhs i32) (result i32) + local.get $lhs + local.get $rhs + i32.add) +)"#; + let module = Module::new(&store, wat)?; + let imports = imports! { + // "host" => { + // "host_func1" => Function::new_typed(&mut store, |p: u64| { + // println!("host_func1: Found number {}", p); + // // assert_eq!(p, u64::max_value()); + // }), + // } + }; + let instance = Instance::new(&mut store, &module, &imports)?; + + let add: TypedFunction<(i32, i32), i32> = instance.exports.get_typed_function(&store, "add")?; + + let result = add.call(&mut store, 10, 20)?; + assert_eq!(result, 30); + + Ok(()) +} + +#[universal_test] +fn back_and_forth_with_imports() -> Result<()> { + let mut store = Store::default(); + // We can use the WAT syntax as well! + let module = Module::new( + &store, + br#"(module + (func $sum (import "env" "sum") (param i32 i32) (result i32)) + (func (export "add_one") (param i32) (result i32) + (call $sum (local.get 0) (i32.const 1)) + ) + )"#, + )?; + + fn sum(a: i32, b: i32) -> i32 { + println!("Summing: {}+{}", a, b); + a + b + } + + let import_object = imports! { + "env" => { + "sum" => Function::new_typed(&mut store, sum), + } + }; + let instance = Instance::new(&mut store, &module, &import_object)?; + + let add_one: TypedFunction = + instance.exports.get_typed_function(&store, "add_one")?; + add_one.call(&mut store, 1)?; + + Ok(()) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/tests/instance.rs b/arbitrator/tools/module_roots/wasmer/lib/api/tests/instance.rs new file mode 100644 index 0000000..bffdfd8 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/tests/instance.rs @@ -0,0 +1,80 @@ +use macro_wasmer_universal_test::universal_test; +#[cfg(feature = "js")] +use wasm_bindgen_test::*; + +use wasmer::*; + +#[universal_test] +fn exports_work_after_multiple_instances_have_been_freed() -> Result<(), String> { + let mut store = Store::default(); + let module = Module::new( + &store, + " +(module + (type $sum_t (func (param i32 i32) (result i32))) + (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) + local.get $x + local.get $y + i32.add) + (export \"sum\" (func $sum_f))) +", + ) + .map_err(|e| format!("{e:?}"))?; + + let imports = Imports::new(); + let instance = Instance::new(&mut store, &module, &imports).map_err(|e| format!("{e:?}"))?; + let instance2 = instance.clone(); + let instance3 = instance.clone(); + + // The function is cloned to “break” the connection with `instance`. + let sum = instance + .exports + .get_function("sum") + .map_err(|e| format!("{e:?}"))? + .clone(); + + drop(instance); + drop(instance2); + drop(instance3); + + // All instances have been dropped, but `sum` continues to work! + assert_eq!( + sum.call(&mut store, &[Value::I32(1), Value::I32(2)]) + .map_err(|e| format!("{e:?}"))? + .into_vec(), + vec![Value::I32(3)], + ); + + Ok(()) +} + +#[universal_test] +fn unit_native_function_env() -> Result<(), String> { + let mut store = Store::default(); + + #[derive(Clone)] + struct Env { + multiplier: u32, + } + + fn imported_fn(env: FunctionEnvMut, args: &[Value]) -> Result, RuntimeError> { + let value = env.data().multiplier * args[0].unwrap_i32() as u32; + Ok(vec![Value::I32(value as _)]) + } + + // We create the environment + let env = Env { multiplier: 3 }; + // We move the environment to the store, so it can be used by the `Function` + let env = FunctionEnv::new(&mut store, env); + + let imported_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]); + let imported = Function::new_with_env(&mut store, &env, imported_signature, imported_fn); + + let expected = vec![Value::I32(12)].into_boxed_slice(); + let result = imported + .call(&mut store, &[Value::I32(4)]) + .map_err(|e| format!("{e:?}"))?; + assert_eq!(result, expected); + + Ok(()) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/tests/memory.rs b/arbitrator/tools/module_roots/wasmer/lib/api/tests/memory.rs new file mode 100644 index 0000000..bf5f6ec --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/tests/memory.rs @@ -0,0 +1,78 @@ +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; + +use wasmer::{imports, Instance, Memory, MemoryLocation, MemoryType, Module, Store}; + +#[test] +fn test_shared_memory_atomics_notify_send() { + let mut store = Store::default(); + let wat = r#"(module +(import "host" "memory" (memory 10 65536 shared)) +)"#; + let module = Module::new(&store, wat) + .map_err(|e| format!("{e:?}")) + .unwrap(); + + let mem = Memory::new(&mut store, MemoryType::new(10, Some(65536), true)).unwrap(); + + let imports = imports! { + "host" => { + "memory" => mem.clone(), + }, + }; + + let _inst = Instance::new(&mut store, &module, &imports).unwrap(); + + let mem = if let Some(m) = mem.as_shared(&store) { + m + } else { + #[cfg(feature = "sys")] + panic!("Memory is not shared"); + #[cfg(not(feature = "sys"))] + return; + }; + + // Test basic notify. + let mem2 = mem.clone(); + std::thread::spawn(move || loop { + if mem2.notify(MemoryLocation::new_32(10), 1).unwrap() > 0 { + break; + } + }); + + mem.wait(MemoryLocation::new_32(10), None).unwrap(); + + // Test wake_all + + let done = Arc::new(AtomicBool::new(false)); + + std::thread::spawn({ + let mem = mem.clone(); + let done = done.clone(); + move || { + while !done.load(Ordering::SeqCst) { + mem.wake_all_atomic_waiters().ok(); + } + } + }); + + mem.wait(MemoryLocation::new_32(10), None).unwrap(); + done.store(true, Ordering::SeqCst); +} + +#[cfg(feature = "sys")] +#[test] +fn test_shared_memory_disable_atomics() { + use wasmer::AtomicsError; + + let mut store = Store::default(); + let mem = Memory::new(&mut store, MemoryType::new(10, Some(65536), true)).unwrap(); + + let mem = mem.as_shared(&store).unwrap(); + mem.disable_atomics().unwrap(); + + let err = mem.wait(MemoryLocation::new_32(1), None).unwrap_err(); + assert_eq!(err, AtomicsError::AtomicsDisabled); +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/tests/module.rs b/arbitrator/tools/module_roots/wasmer/lib/api/tests/module.rs new file mode 100644 index 0000000..22683f5 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/tests/module.rs @@ -0,0 +1,292 @@ +use macro_wasmer_universal_test::universal_test; +#[cfg(feature = "js")] +use wasm_bindgen_test::*; + +use wasmer::*; + +#[universal_test] +fn module_get_name() -> Result<(), String> { + let store = Store::default(); + let wat = r#"(module)"#; + let module = Module::new(&store, wat) + .map_err(|e| format!("{e:?}")) + .map_err(|e| format!("{e:?}"))?; + assert_eq!(module.name(), None); + + Ok(()) +} + +#[universal_test] +fn module_set_name() -> Result<(), String> { + let store = Store::default(); + let wat = r#"(module $name)"#; + let mut module = Module::new(&store, wat).map_err(|e| format!("{e:?}"))?; + assert_eq!(module.name(), Some("name")); + + module.set_name("new_name"); + assert_eq!(module.name(), Some("new_name")); + + Ok(()) +} + +#[universal_test] +fn imports() -> Result<(), String> { + let store = Store::default(); + let wat = r#"(module +(import "host" "func" (func)) +(import "host" "memory" (memory 1)) +(import "host" "table" (table 1 anyfunc)) +(import "host" "global" (global i32)) +)"#; + let module = Module::new(&store, wat).map_err(|e| format!("{e:?}"))?; + assert_eq!( + module.imports().collect::>(), + vec![ + ImportType::new( + "host", + "func", + ExternType::Function(FunctionType::new(vec![], vec![])) + ), + ImportType::new( + "host", + "memory", + ExternType::Memory(MemoryType::new(Pages(1), None, false)) + ), + ImportType::new( + "host", + "table", + ExternType::Table(TableType::new(Type::FuncRef, 1, None)) + ), + ImportType::new( + "host", + "global", + ExternType::Global(GlobalType::new(Type::I32, Mutability::Const)) + ) + ] + ); + + // Now we test the iterators + assert_eq!( + module.imports().functions().collect::>(), + vec![ImportType::new( + "host", + "func", + FunctionType::new(vec![], vec![]) + ),] + ); + assert_eq!( + module.imports().memories().collect::>(), + vec![ImportType::new( + "host", + "memory", + MemoryType::new(Pages(1), None, false) + ),] + ); + assert_eq!( + module.imports().tables().collect::>(), + vec![ImportType::new( + "host", + "table", + TableType::new(Type::FuncRef, 1, None) + ),] + ); + assert_eq!( + module.imports().globals().collect::>(), + vec![ImportType::new( + "host", + "global", + GlobalType::new(Type::I32, Mutability::Const) + ),] + ); + Ok(()) +} + +#[test] +fn exports() -> Result<(), String> { + let store = Store::default(); + let wat = r#"(module +(func (export "func") nop) +(memory (export "memory") 1) +(table (export "table") 1 funcref) +(global (export "global") i32 (i32.const 0)) +)"#; + let module = Module::new(&store, wat).map_err(|e| format!("{e:?}"))?; + assert_eq!( + module.exports().collect::>(), + vec![ + ExportType::new( + "func", + ExternType::Function(FunctionType::new(vec![], vec![])) + ), + ExportType::new( + "memory", + ExternType::Memory(MemoryType::new(Pages(1), None, false)) + ), + ExportType::new( + "table", + ExternType::Table(TableType::new(Type::FuncRef, 1, None)) + ), + ExportType::new( + "global", + ExternType::Global(GlobalType::new(Type::I32, Mutability::Const)) + ) + ] + ); + + // Now we test the iterators + assert_eq!( + module.exports().functions().collect::>(), + vec![ExportType::new("func", FunctionType::new(vec![], vec![])),] + ); + assert_eq!( + module.exports().memories().collect::>(), + vec![ExportType::new( + "memory", + MemoryType::new(Pages(1), None, false) + ),] + ); + assert_eq!( + module.exports().tables().collect::>(), + vec![ExportType::new( + "table", + TableType::new(Type::FuncRef, 1, None) + ),] + ); + assert_eq!( + module.exports().globals().collect::>(), + vec![ExportType::new( + "global", + GlobalType::new(Type::I32, Mutability::Const) + ),] + ); + Ok(()) +} + +#[universal_test] +fn calling_host_functions_with_negative_values_works() -> Result<(), String> { + let mut store = Store::default(); + let wat = r#"(module +(import "host" "host_func1" (func (param i64))) +(import "host" "host_func2" (func (param i32))) +(import "host" "host_func3" (func (param i64))) +(import "host" "host_func4" (func (param i32))) +(import "host" "host_func5" (func (param i32))) +(import "host" "host_func6" (func (param i32))) +(import "host" "host_func7" (func (param i32))) +(import "host" "host_func8" (func (param i32))) + +(func (export "call_host_func1") + (call 0 (i64.const -1))) +(func (export "call_host_func2") + (call 1 (i32.const -1))) +(func (export "call_host_func3") + (call 2 (i64.const -1))) +(func (export "call_host_func4") + (call 3 (i32.const -1))) +(func (export "call_host_func5") + (call 4 (i32.const -1))) +(func (export "call_host_func6") + (call 5 (i32.const -1))) +(func (export "call_host_func7") + (call 6 (i32.const -1))) +(func (export "call_host_func8") + (call 7 (i32.const -1))) +)"#; + let module = Module::new(&store, wat).map_err(|e| format!("{e:?}"))?; + let imports = imports! { + "host" => { + "host_func1" => Function::new_typed(&mut store, |p: u64| { + println!("host_func1: Found number {}", p); + assert_eq!(p, u64::max_value()); + }), + "host_func2" => Function::new_typed(&mut store, |p: u32| { + println!("host_func2: Found number {}", p); + assert_eq!(p, u32::max_value()); + }), + "host_func3" => Function::new_typed(&mut store, |p: i64| { + println!("host_func3: Found number {}", p); + assert_eq!(p, -1); + }), + "host_func4" => Function::new_typed(&mut store, |p: i32| { + println!("host_func4: Found number {}", p); + assert_eq!(p, -1); + }), + "host_func5" => Function::new_typed(&mut store, |p: i16| { + println!("host_func5: Found number {}", p); + assert_eq!(p, -1); + }), + "host_func6" => Function::new_typed(&mut store, |p: u16| { + println!("host_func6: Found number {}", p); + assert_eq!(p, u16::max_value()); + }), + "host_func7" => Function::new_typed(&mut store, |p: i8| { + println!("host_func7: Found number {}", p); + assert_eq!(p, -1); + }), + "host_func8" => Function::new_typed(&mut store, |p: u8| { + println!("host_func8: Found number {}", p); + assert_eq!(p, u8::max_value()); + }), + } + }; + let instance = Instance::new(&mut store, &module, &imports).map_err(|e| format!("{e:?}"))?; + + let f1: TypedFunction<(), ()> = instance + .exports + .get_typed_function(&store, "call_host_func1") + .map_err(|e| format!("{e:?}"))?; + let f2: TypedFunction<(), ()> = instance + .exports + .get_typed_function(&store, "call_host_func2") + .map_err(|e| format!("{e:?}"))?; + let f3: TypedFunction<(), ()> = instance + .exports + .get_typed_function(&store, "call_host_func3") + .map_err(|e| format!("{e:?}"))?; + let f4: TypedFunction<(), ()> = instance + .exports + .get_typed_function(&store, "call_host_func4") + .map_err(|e| format!("{e:?}"))?; + let f5: TypedFunction<(), ()> = instance + .exports + .get_typed_function(&store, "call_host_func5") + .map_err(|e| format!("{e:?}"))?; + let f6: TypedFunction<(), ()> = instance + .exports + .get_typed_function(&store, "call_host_func6") + .map_err(|e| format!("{e:?}"))?; + let f7: TypedFunction<(), ()> = instance + .exports + .get_typed_function(&store, "call_host_func7") + .map_err(|e| format!("{e:?}"))?; + let f8: TypedFunction<(), ()> = instance + .exports + .get_typed_function(&store, "call_host_func8") + .map_err(|e| format!("{e:?}"))?; + + f1.call(&mut store).map_err(|e| format!("{e:?}"))?; + f2.call(&mut store).map_err(|e| format!("{e:?}"))?; + f3.call(&mut store).map_err(|e| format!("{e:?}"))?; + f4.call(&mut store).map_err(|e| format!("{e:?}"))?; + f5.call(&mut store).map_err(|e| format!("{e:?}"))?; + f6.call(&mut store).map_err(|e| format!("{e:?}"))?; + f7.call(&mut store).map_err(|e| format!("{e:?}"))?; + f8.call(&mut store).map_err(|e| format!("{e:?}"))?; + + Ok(()) +} + +#[universal_test] +fn module_custom_sections() -> Result<(), String> { + let store = Store::default(); + let custom_section_wasm_bytes = include_bytes!("simple-name-section.wasm"); + let module = Module::new(&store, custom_section_wasm_bytes).map_err(|e| format!("{e:?}"))?; + let sections = module.custom_sections("name"); + let sections_vec: Vec> = sections.collect(); + assert_eq!(sections_vec.len(), 1); + assert_eq!( + sections_vec[0], + vec![2, 2, 36, 105, 1, 0, 0, 0].into_boxed_slice() + ); + Ok(()) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/tests/reference_types.rs b/arbitrator/tools/module_roots/wasmer/lib/api/tests/reference_types.rs new file mode 100644 index 0000000..b713854 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/api/tests/reference_types.rs @@ -0,0 +1,490 @@ +#[cfg(feature = "sys")] +pub mod reference_types { + + use anyhow::Result; + use macro_wasmer_universal_test::universal_test; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::Arc; + #[cfg(feature = "js")] + use wasm_bindgen_test::*; + use wasmer::*; + + #[universal_test] + fn func_ref_passed_and_returned() -> Result<()> { + let mut store = Store::default(); + let wat = r#"(module +(import "env" "func_ref_identity" (func (param funcref) (result funcref))) +(type $ret_i32_ty (func (result i32))) +(table $table (export "table") 2 2 funcref) + +(func (export "run") (param) (result funcref) + (call 0 (ref.null func))) +(func (export "call_set_value") (param $fr funcref) (result i32) + (table.set $table (i32.const 0) (local.get $fr)) + (call_indirect $table (type $ret_i32_ty) (i32.const 0))) +)"#; + let module = Module::new(&store, wat)?; + #[derive(Clone, Debug)] + pub struct Env(Arc); + let env = Env(Arc::new(AtomicBool::new(false))); + let env = FunctionEnv::new(&mut store, env); + let imports = imports! { + "env" => { + "func_ref_identity" => Function::new_with_env(&mut store, &env, FunctionType::new([Type::FuncRef], [Type::FuncRef]), |_env: FunctionEnvMut, values: &[Value]| -> Result, _> { + Ok(vec![values[0].clone()]) + }) + }, + }; + + let instance = Instance::new(&mut store, &module, &imports)?; + + let f: &Function = instance.exports.get_function("run")?; + let results = f.call(&mut store, &[]).unwrap(); + if let Value::FuncRef(fr) = &results[0] { + assert!(fr.is_none()); + } else { + panic!("funcref not found!"); + } + + let func_to_call = + Function::new_typed_with_env(&mut store, &env, |env: FunctionEnvMut| -> i32 { + env.data().0.store(true, Ordering::SeqCst); + 343 + }); + let call_set_value: &Function = instance.exports.get_function("call_set_value")?; + let results: Box<[Value]> = + call_set_value.call(&mut store, &[Value::FuncRef(Some(func_to_call))])?; + assert!(env.as_ref(&store.as_store_ref()).0.load(Ordering::SeqCst)); + assert_eq!(&*results, &[Value::I32(343)]); + + Ok(()) + } + + #[universal_test] + fn func_ref_passed_and_called() -> Result<()> { + let mut store = Store::default(); + let wat = r#"(module +(func $func_ref_call (import "env" "func_ref_call") (param funcref) (result i32)) +(type $ret_i32_ty (func (result i32))) +(table $table (export "table") 2 2 funcref) + +(func $product (param $x i32) (param $y i32) (result i32) + (i32.mul (local.get $x) (local.get $y))) +;; TODO: figure out exactly why this statement is needed +(elem declare func $product) +(func (export "call_set_value") (param $fr funcref) (result i32) + (table.set $table (i32.const 0) (local.get $fr)) + (call_indirect $table (type $ret_i32_ty) (i32.const 0))) +(func (export "call_func") (param $fr funcref) (result i32) + (call $func_ref_call (local.get $fr))) +(func (export "call_host_func_with_wasm_func") (result i32) + (call $func_ref_call (ref.func $product))) +)"#; + let module = Module::new(&store, wat)?; + let env = FunctionEnv::new(&mut store, ()); + fn func_ref_call( + mut env: FunctionEnvMut<()>, + values: &[Value], + ) -> Result, RuntimeError> { + // TODO: look into `Box<[Value]>` being returned breakage + let f = values[0].unwrap_funcref().as_ref().unwrap(); + let f: TypedFunction<(i32, i32), i32> = f.typed(&env)?; + Ok(vec![Value::I32(f.call(&mut env, 7, 9)?)]) + } + + let imports = imports! { + "env" => { + "func_ref_call" => Function::new_with_env( + &mut store, + &env, + FunctionType::new([Type::FuncRef], [Type::I32]), + func_ref_call + ), + // "func_ref_call_native" => Function::new_native(&mut store, |f: Function| -> Result { + // let f: TypedFunction::<(i32, i32), i32> = f.typed(&mut store)?; + // f.call(&mut store, 7, 9) + // }) + }, + }; + + let instance = Instance::new(&mut store, &module, &imports)?; + { + fn sum(a: i32, b: i32) -> i32 { + a + b + } + let sum_func = Function::new_typed(&mut store, sum); + + let call_func: &Function = instance.exports.get_function("call_func")?; + let result = call_func.call(&mut store, &[Value::FuncRef(Some(sum_func))])?; + assert_eq!(result[0].unwrap_i32(), 16); + } + + { + let f: TypedFunction<(), i32> = instance + .exports + .get_typed_function(&store, "call_host_func_with_wasm_func")?; + let result = f.call(&mut store)?; + assert_eq!(result, 63); + } + + Ok(()) + } + + #[macro_wasmer_universal_test::universal_test] + fn extern_ref_passed_and_returned() -> Result<()> { + use std::collections::HashMap; + let mut store = Store::default(); + let wat = r#"(module + (func $extern_ref_identity (import "env" "extern_ref_identity") (param externref) (result externref)) + (func $extern_ref_identity_native (import "env" "extern_ref_identity_native") (param externref) (result externref)) + (func $get_new_extern_ref (import "env" "get_new_extern_ref") (result externref)) + (func $get_new_extern_ref_native (import "env" "get_new_extern_ref_native") (result externref)) + + (func (export "run") (param) (result externref) + (call $extern_ref_identity (ref.null extern))) + (func (export "run_native") (param) (result externref) + (call $extern_ref_identity_native (ref.null extern))) + (func (export "get_hashmap") (param) (result externref) + (call $get_new_extern_ref)) + (func (export "get_hashmap_native") (param) (result externref) + (call $get_new_extern_ref_native)) +)"#; + let module = Module::new(&store, wat)?; + let env = FunctionEnv::new(&mut store, ()); + let imports = imports! { + "env" => { + "extern_ref_identity" => Function::new_with_env(&mut store, &env, FunctionType::new([Type::ExternRef], [Type::ExternRef]), |_env, values| -> Result, _> { + Ok(vec![values[0].clone()]) + }), + "extern_ref_identity_native" => Function::new_typed(&mut store, |er: Option| -> Option { + er + }), + "get_new_extern_ref" => Function::new_with_env(&mut store, &env, FunctionType::new([], [Type::ExternRef]), |mut env, _| -> Result, _> { + let inner = + [("hello".to_string(), "world".to_string()), + ("color".to_string(), "orange".to_string())] + .iter() + .cloned() + .collect::>(); + let new_extern_ref = ExternRef::new(&mut env, inner); + Ok(vec![Value::ExternRef(Some(new_extern_ref))]) + }), + "get_new_extern_ref_native" => Function::new_typed_with_env(&mut store, &env, |mut env: FunctionEnvMut<()>| -> Option { + let inner = + [("hello".to_string(), "world".to_string()), + ("color".to_string(), "orange".to_string())] + .iter() + .cloned() + .collect::>(); + Some(ExternRef::new(&mut env.as_store_mut(), inner)) + }) + }, + }; + + let instance = Instance::new(&mut store, &module, &imports)?; + for run in &["run", "run_native"] { + let f: &Function = instance.exports.get_function(run)?; + let results = f.call(&mut store, &[]).unwrap(); + if let Value::ExternRef(er) = &results[0] { + assert!(er.is_none()); + } else { + panic!("result is not an extern ref!"); + } + + let f: TypedFunction<(), Option> = + instance.exports.get_typed_function(&store, run)?; + let result: Option = f.call(&mut store)?; + assert!(result.is_none()); + } + + for get_hashmap in &["get_hashmap", "get_hashmap_native"] { + let f: &Function = instance.exports.get_function(get_hashmap)?; + let results = f.call(&mut store, &[]).unwrap(); + if let Value::ExternRef(er) = &results[0] { + let inner: &HashMap = + er.as_ref().unwrap().downcast(&store).unwrap(); + assert_eq!(inner["hello"], "world"); + assert_eq!(inner["color"], "orange"); + } else { + panic!("result is not an extern ref!"); + } + + let f: TypedFunction<(), Option> = + instance.exports.get_typed_function(&store, get_hashmap)?; + + let result: Option = f.call(&mut store)?; + let inner: &HashMap = result.unwrap().downcast(&store).unwrap(); + assert_eq!(inner["hello"], "world"); + assert_eq!(inner["color"], "orange"); + } + + Ok(()) + } + + #[universal_test] + fn extern_ref_ref_counting_basic() -> Result<()> { + let mut store = Store::default(); + let wat = r#"(module + (func (export "drop") (param $er externref) (result) + (drop (local.get $er))) +)"#; + let module = Module::new(&store, wat)?; + let instance = Instance::new(&mut store, &module, &imports! {})?; + let f: TypedFunction, ()> = + instance.exports.get_typed_function(&store, "drop")?; + + let er = ExternRef::new(&mut store, 3u32); + f.call(&mut store, Some(er.clone()))?; + + let tmp: Option<&u32> = er.downcast(&store); + assert_eq!(tmp.unwrap(), &3); + + Ok(()) + } + + #[universal_test] + fn refs_in_globals() -> Result<()> { + let mut store = Store::default(); + let wat = r#"(module + (global $er_global (export "er_global") (mut externref) (ref.null extern)) + (global $fr_global (export "fr_global") (mut funcref) (ref.null func)) + (global $fr_immutable_global (export "fr_immutable_global") funcref (ref.func $hello)) + (func $hello (param) (result i32) + (i32.const 73)) +)"#; + let module = Module::new(&store, wat)?; + let instance = Instance::new(&mut store, &module, &imports! {})?; + { + let er_global: &Global = instance.exports.get_global("er_global")?; + + if let Value::ExternRef(er) = er_global.get(&mut store) { + assert!(er.is_none()); + } else { + panic!("Did not find extern ref in the global"); + } + let extref = Some(ExternRef::new(&mut store, 3u32)); + er_global.set(&mut store, Value::ExternRef(extref))?; + + if let Value::ExternRef(er) = er_global.get(&mut store) { + let tmp: Option<&u32> = er.unwrap().downcast(&store); + assert_eq!(tmp.unwrap(), &3); + } else { + panic!("Did not find extern ref in the global"); + } + } + + { + let fr_global: &Global = instance.exports.get_global("fr_immutable_global")?; + + if let Value::FuncRef(Some(f)) = fr_global.get(&mut store) { + let native_func: TypedFunction<(), u32> = f.typed(&store)?; + assert_eq!(native_func.call(&mut store)?, 73); + } else { + panic!("Did not find non-null func ref in the global"); + } + } + + { + let fr_global: &Global = instance.exports.get_global("fr_global")?; + + if let Value::FuncRef(None) = fr_global.get(&mut store) { + } else { + panic!("Did not find a null func ref in the global"); + } + + let f = Function::new_typed(&mut store, |arg1: i32, arg2: i32| -> i32 { arg1 + arg2 }); + + fr_global.set(&mut store, Value::FuncRef(Some(f)))?; + + if let Value::FuncRef(Some(f)) = fr_global.get(&mut store) { + let native: TypedFunction<(i32, i32), i32> = f.typed(&store)?; + assert_eq!(native.call(&mut store, 5, 7)?, 12); + } else { + panic!("Did not find extern ref in the global"); + } + } + + Ok(()) + } + + #[universal_test] + fn extern_ref_ref_counting_table_basic() -> Result<()> { + let mut store = Store::default(); + let wat = r#"(module + (global $global (export "global") (mut externref) (ref.null extern)) + (table $table (export "table") 4 4 externref) + (func $insert (param $er externref) (param $idx i32) + (table.set $table (local.get $idx) (local.get $er))) + (func $intermediate (param $er externref) (param $idx i32) + (call $insert (local.get $er) (local.get $idx))) + (func $insert_into_table (export "insert_into_table") (param $er externref) (param $idx i32) (result externref) + (call $intermediate (local.get $er) (local.get $idx)) + (local.get $er)) +)"#; + let module = Module::new(&store, wat)?; + let instance = Instance::new(&mut store, &module, &imports! {})?; + + let f: TypedFunction<(Option, i32), Option> = instance + .exports + .get_typed_function(&store, "insert_into_table")?; + + let er = ExternRef::new(&mut store, 3usize); + + let er = f.call(&mut store, Some(er), 1)?; + assert!(er.is_some()); + + let table: &Table = instance.exports.get_table("table")?; + + { + let er2 = table.get(&mut store, 1).unwrap(); + let er2 = er2.externref().unwrap(); + assert!(er2.is_some()); + } + + assert!(er.is_some()); + table.set(&mut store, 1, Value::ExternRef(None))?; + + assert!(er.is_some()); + + Ok(()) + } + + #[universal_test] + fn extern_ref_ref_counting_global_basic() -> Result<()> { + let mut store = Store::default(); + let wat = r#"(module + (global $global (export "global") (mut externref) (ref.null extern)) + (func $get_from_global (export "get_from_global") (result externref) + (drop (global.get $global)) + (global.get $global)) +)"#; + let module = Module::new(&store, wat)?; + let instance = Instance::new(&mut store, &module, &imports! {})?; + + let global: &Global = instance.exports.get_global("global")?; + { + let er = ExternRef::new(&mut store, 3usize); + global.set(&mut store, Value::ExternRef(Some(er)))?; + } + let get_from_global: TypedFunction<(), Option> = instance + .exports + .get_typed_function(&store, "get_from_global")?; + + let er = get_from_global.call(&mut store)?; + assert!(er.is_some()); + global.set(&mut store, Value::ExternRef(None))?; + assert!(er.is_some()); + + Ok(()) + } + + #[universal_test] + fn extern_ref_ref_counting_traps() -> Result<()> { + let mut store = Store::default(); + let wat = r#"(module + (func $pass_er (export "pass_extern_ref") (param externref) + (local.get 0) + (unreachable)) +)"#; + let module = Module::new(&store, wat)?; + let instance = Instance::new(&mut store, &module, &imports! {})?; + + let pass_extern_ref: TypedFunction, ()> = instance + .exports + .get_typed_function(&store, "pass_extern_ref")?; + + let er = ExternRef::new(&mut store, 3usize); + + let result = pass_extern_ref.call(&mut store, Some(er)); + assert!(result.is_err()); + + Ok(()) + } + + #[universal_test] + fn extern_ref_ref_counting_table_instructions() -> Result<()> { + let mut store = Store::default(); + let wat = r#"(module + (table $table1 (export "table1") 2 12 externref) + (table $table2 (export "table2") 6 12 externref) + (func $grow_table_with_ref (export "grow_table_with_ref") (param $er externref) (param $size i32) (result i32) + (table.grow $table1 (local.get $er) (local.get $size))) + (func $fill_table_with_ref (export "fill_table_with_ref") (param $er externref) (param $start i32) (param $end i32) + (table.fill $table1 (local.get $start) (local.get $er) (local.get $end))) + (func $copy_into_table2 (export "copy_into_table2") + (table.copy $table2 $table1 (i32.const 0) (i32.const 0) (i32.const 4))) +)"#; + let module = Module::new(&store, wat)?; + let instance = Instance::new(&mut store, &module, &imports! {})?; + + let grow_table_with_ref: TypedFunction<(Option, i32), i32> = instance + .exports + .get_typed_function(&store, "grow_table_with_ref")?; + let fill_table_with_ref: TypedFunction<(Option, i32, i32), ()> = instance + .exports + .get_typed_function(&store, "fill_table_with_ref")?; + let copy_into_table2: TypedFunction<(), ()> = instance + .exports + .get_typed_function(&store, "copy_into_table2")?; + let table1: &Table = instance.exports.get_table("table1")?; + let table2: &Table = instance.exports.get_table("table2")?; + + let er1 = ExternRef::new(&mut store, 3usize); + let er2 = ExternRef::new(&mut store, 5usize); + let er3 = ExternRef::new(&mut store, 7usize); + { + let result = grow_table_with_ref.call(&mut store, Some(er1.clone()), 0)?; + assert_eq!(result, 2); + + let result = grow_table_with_ref.call(&mut store, Some(er1.clone()), 10_000)?; + assert_eq!(result, -1); + + let result = grow_table_with_ref.call(&mut store, Some(er1), 8)?; + assert_eq!(result, 2); + + for i in 2..10 { + let v = table1.get(&mut store, i); + let e = v.as_ref().unwrap().unwrap_externref(); + let e_val: Option<&usize> = e.as_ref().unwrap().downcast(&store); + assert_eq!(*e_val.unwrap(), 3); + } + } + + { + fill_table_with_ref.call(&mut store, Some(er2), 0, 2)?; + } + + { + table2.set(&mut store, 0, Value::ExternRef(Some(er3.clone())))?; + table2.set(&mut store, 1, Value::ExternRef(Some(er3.clone())))?; + table2.set(&mut store, 2, Value::ExternRef(Some(er3.clone())))?; + table2.set(&mut store, 3, Value::ExternRef(Some(er3.clone())))?; + table2.set(&mut store, 4, Value::ExternRef(Some(er3)))?; + } + + { + copy_into_table2.call(&mut store)?; + for i in 1..5 { + let v = table2.get(&mut store, i); + let e = v.as_ref().unwrap().unwrap_externref(); + let value: &usize = e.as_ref().unwrap().downcast(&store).unwrap(); + match i { + 0 | 1 => assert_eq!(*value, 5), + 4 => assert_eq!(*value, 7), + _ => assert_eq!(*value, 3), + } + } + } + + { + for i in 0..table1.size(&store) { + table1.set(&mut store, i, Value::ExternRef(None))?; + } + for i in 0..table2.size(&store) { + table2.set(&mut store, i, Value::ExternRef(None))?; + } + } + + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/api/tests/simple-name-section.wasm b/arbitrator/tools/module_roots/wasmer/lib/api/tests/simple-name-section.wasm new file mode 100644 index 0000000..2969b62 Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/api/tests/simple-name-section.wasm differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/backend-api/CHANGELOG.md b/arbitrator/tools/module_roots/wasmer/lib/backend-api/CHANGELOG.md new file mode 100644 index 0000000..5d313e1 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/backend-api/CHANGELOG.md @@ -0,0 +1,94 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.0.1 (2023-04-27) + +* Removed legacy API implementation in favour of the new cynic client +* Fixed log querying +* Added methods for retrieving DeployApp/Version by unique id + +## 0.1.0-alpha.1 (2023-03-29) + + + + + + + + + + + +### Refactor (BREAKING) + + - Rename wasmer-deploy-core to wasmer-deploy-schema + -schema is a more sensible / expressive name for the crate, + since it just holds type definitions. + + Done in preparation for publishing the crate, since it will need to be + used by downstream consumers like the Wasmer repo + +### Chore + + - Add description to wasmer-api Cargo.toml + Required for releasing. + +### Other + + - Dependency cleanup + * Lift some dependencies to workspace.dependencies to avoid duplication + * Remove a bunch of unused dependencies + - Add crate metadata and prepare for first CLI release + - "app list" filters + Extend the "app list" command to query either a namespace, a users apps, + or all apps accessible by a user. + (--namepsace X, --all) + - Add a webc app config fetch tests + - Make serde_json a workspace dependency + To avoid duplication... + - Lift serde to be a workspace dependency + Easier version management... + - Lift anyhow, time and clap to workspace dependnecies + Less version management... + +### Bug Fixes + + - Use token for webc fetching + If the api is configured with a token, use the token for fetching webcs. + Previously it just used anonymous access. + - Update deployment config generation to backend changes + The generateDeployConfig GraphQL API has changed + + * Takes a DeployConfigVersion id instead of DeployConfig id +* Returns a DeployConfigVersion + +### New Features + + - Add generate_deploy_token to new cynic GQL api client + Will be needed for various commands + - Add getPackage GQL query + - Add query for DeployAppVersion + - Add new namespace and app commands + - Add a CapabilityLoggingV1 config + Allows to configure the logging behaviour of workloads. + + Will be used very soon to implement instance log forwarding. + +### Documentation + + - add some changelogs + - Add REAMDE to Cargo.toml of to-be-published crates + +### Chore + + - Remove download_url from WebcPackageIdentifierV1 + Not needed anymore, since now we have a deployment config registry. + + +Needed to update relevant callsites + + diff --git a/arbitrator/tools/module_roots/wasmer/lib/backend-api/Cargo.toml b/arbitrator/tools/module_roots/wasmer/lib/backend-api/Cargo.toml new file mode 100644 index 0000000..194f92d --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/backend-api/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "wasmer-api" +version = "0.0.25" +description = "Client library for the Wasmer GraphQL API" +readme = "README.md" +documentation = "https://docs.rs/wasmer-api" + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# Wasmer dependencies. +edge-schema.workspace = true +webc = "5" + +# crates.io dependencies. +anyhow = "1" +serde = { version = "1", features = ["derive"] } +time = { version = "0.3", features = ["formatting", "parsing"] } +tokio = { version = "1.23.0" } +serde_json = "1" +url = "2" +futures = "0.3" +tracing = "0.1" +cynic = { version = "=3.4.3", features = ["http-reqwest"] } +pin-project-lite = "0.2.10" +serde_path_to_error = "0.1.14" +harsh = "0.2.2" +reqwest = { version = "0.11.13", default-features = false, features = ["json"] } + +[dev-dependencies] +base64 = "0.13.1" +tokio = { version = "1.3", features = ["macros", "rt"] } +uuid = { version = "1", features = ["v4"] } diff --git a/arbitrator/tools/module_roots/wasmer/lib/backend-api/README.md b/arbitrator/tools/module_roots/wasmer/lib/backend-api/README.md new file mode 100644 index 0000000..a5c2839 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/backend-api/README.md @@ -0,0 +1,46 @@ +# wasmer-api + +GraphQL API client for the [Wasmer](https://wasmer.io) backend. + +## Development + +This client is built on the [cynic][cynic-api-docs] crate, +a GraphQL client library that allows tight integration between Rust and +GraphQL types. + +It was chosen over other implementations like `graphql-client` because it +significantly reduces boilerplate and improves the development experience. + +The downside is that the underlying GraphQL queries are much less obvious when +looking at the code. This can be remedied with some strategies mentioned below. + +Consult the Cynic docs at [cynic-rs.dev][cynic-website] for more +information. + +### Backend GraphQL Schema + +The GraphQL schema for the backend is stored in `./schema.graphql`. + +To update the schema, simply download the latest version and replace the local +file. + +It can be retrieved from +https://github.com/wasmerio/wapm.io-backend/blob/master/backend/graphql/schema.graphql. + +### Writing/Updating Queries + +You can use the [Cynic web UI][cynic-web-ui] to easily create the types for new +queries. + +Simply upload the local schema from `./schema.graphql` and use the UI to build +your desired query. + +NOTE: Where possible, do not duplicate types that are already defined, +and instead reuse/extend them where possible. + +This is not always sensible though, depending on which nested data you want to +fetch. + +[cynic-api-docs]: https://docs.rs/cynic/latest/cynic/ +[cynic-web-ui]: https://generator.cynic-rs.dev/ +[cynic-website]: https://cynic-rs.dev diff --git a/arbitrator/tools/module_roots/wasmer/lib/backend-api/schema.graphql b/arbitrator/tools/module_roots/wasmer/lib/backend-api/schema.graphql new file mode 100644 index 0000000..b290541 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/backend-api/schema.graphql @@ -0,0 +1,3920 @@ +""" +Directs the executor to include this field or fragment only when the user is not logged in. +""" +directive @includeIfLoggedIn on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +""" +Directs the executor to skip this field or fragment when the user is not logged in. +""" +directive @skipIfLoggedIn on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +interface Node { + """The ID of the object""" + id: ID! +} + +type PublicKey implements Node { + """The ID of the object""" + id: ID! + owner: User! + keyId: String! + key: String! + revokedAt: DateTime + uploadedAt: DateTime! + verifyingSignature: Signature + revoked: Boolean! +} + +type User implements Node & PackageOwner & Owner { + firstName: String! + lastName: String! + email: String! + dateJoined: DateTime! + + """Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.""" + username: String! + isEmailValidated: Boolean! + bio: String + location: String + websiteUrl: String + + """The ID of the object""" + id: ID! + globalName: String! + globalId: ID! + avatar(size: Int = 80): String! + isViewer: Boolean! + hasUsablePassword: Boolean + fullName: String! + githubUrl: String + twitterUrl: String + companyRole: String + companyDescription: String + publicActivity(offset: Int, before: String, after: String, first: Int, last: Int): ActivityEventConnection! + billing: Billing + waitlist(name: String!): WaitlistMember + namespaces(role: GrapheneRole, offset: Int, before: String, after: String, first: Int, last: Int): NamespaceConnection! + packages(collaborating: Boolean = false, offset: Int, before: String, after: String, first: Int, last: Int): PackageConnection! + apps(collaborating: Boolean = false, sortBy: DeployAppsSortBy, offset: Int, before: String, after: String, first: Int, last: Int): DeployAppConnection! + usageMetrics(forRange: MetricRange!, variant: MetricType!): [UsageMetric]! + domains(offset: Int, before: String, after: String, first: Int, last: Int): DNSDomainConnection! + isStaff: Boolean + packageVersions(offset: Int, before: String, after: String, first: Int, last: Int): PackageVersionConnection! + packageTransfersIncoming(offset: Int, before: String, after: String, first: Int, last: Int): PackageTransferRequestConnection! + packageInvitesIncoming(offset: Int, before: String, after: String, first: Int, last: Int): PackageCollaboratorInviteConnection! + namespaceInvitesIncoming(offset: Int, before: String, after: String, first: Int, last: Int): NamespaceCollaboratorInviteConnection! + apiTokens(before: String, after: String, first: Int, last: Int): APITokenConnection! + notifications(before: String, after: String, first: Int, last: Int): UserNotificationConnection! + dashboardActivity(offset: Int, before: String, after: String, first: Int, last: Int): ActivityEventConnection! + loginMethods: [LoginMethod!]! + githubUser: SocialAuth + githubScopes: [String]! +} + +"""Setup for backwards compatibility with existing frontends.""" +interface PackageOwner { + globalName: String! + globalId: ID! +} + +"""An owner of a package.""" +interface Owner { + globalName: String! + globalId: ID! +} + +""" +The `DateTime` scalar type represents a DateTime +value as specified by +[iso8601](https://en.wikipedia.org/wiki/ISO_8601). +""" +scalar DateTime + +type ActivityEventConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [ActivityEventEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +""" +The Relay compliant `PageInfo` type, containing data necessary to paginate this connection. +""" +type PageInfo { + """When paginating forwards, are there more items?""" + hasNextPage: Boolean! + + """When paginating backwards, are there more items?""" + hasPreviousPage: Boolean! + + """When paginating backwards, the cursor to continue.""" + startCursor: String + + """When paginating forwards, the cursor to continue.""" + endCursor: String +} + +"""A Relay edge containing a `ActivityEvent` and its cursor.""" +type ActivityEventEdge { + """The item at the end of the edge""" + node: ActivityEvent + + """A cursor for use in pagination""" + cursor: String! +} + +type ActivityEvent implements Node { + """The ID of the object""" + id: ID! + body: EventBody! + actorIcon: String! + createdAt: DateTime! +} + +type EventBody { + text: String! + ranges: [NodeBodyRange!]! +} + +type NodeBodyRange { + entity: Node! + offset: Int! + length: Int! +} + +type WaitlistMember implements Node { + waitlist: Waitlist! + joinedAt: DateTime! + approvedAt: DateTime + + """The ID of the object""" + id: ID! + member: Owner! + approved: Boolean! +} + +type Waitlist implements Node { + name: String! + createdAt: DateTime! + updatedAt: DateTime! + + """The ID of the object""" + id: ID! +} + +type NamespaceConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [NamespaceEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `Namespace` and its cursor.""" +type NamespaceEdge { + """The item at the end of the edge""" + node: Namespace + + """A cursor for use in pagination""" + cursor: String! +} + +type Namespace implements Node & PackageOwner & Owner { + """The ID of the object""" + id: ID! + name: String! + displayName: String + description: String! + avatar: String! + avatarUpdatedAt: DateTime + twitterHandle: String + githubHandle: String + websiteUrl: String + createdAt: DateTime! + updatedAt: DateTime! + maintainerInvites(offset: Int, before: String, after: String, first: Int, last: Int): NamespaceCollaboratorInviteConnection! + userSet(offset: Int, before: String, after: String, first: Int, last: Int): UserConnection! + globalName: String! + globalId: ID! + packages(offset: Int, before: String, after: String, first: Int, last: Int): PackageConnection! + apps(sortBy: DeployAppsSortBy, offset: Int, before: String, after: String, first: Int, last: Int): DeployAppConnection! + packageVersions(offset: Int, before: String, after: String, first: Int, last: Int): PackageVersionConnection! + collaborators(offset: Int, before: String, after: String, first: Int, last: Int): NamespaceCollaboratorConnection! + publicActivity(before: String, after: String, first: Int, last: Int): ActivityEventConnection! + pendingInvites(offset: Int, before: String, after: String, first: Int, last: Int): NamespaceCollaboratorInviteConnection! + viewerHasRole(role: GrapheneRole!): Boolean! + viewerAsCollaborator(role: GrapheneRole): NamespaceCollaborator + + """Whether the current user is invited to the namespace""" + viewerIsInvited: Boolean! + + """The invitation for the current user to the namespace""" + viewerInvitation: NamespaceCollaboratorInvite + packageTransfersIncoming(offset: Int, before: String, after: String, first: Int, last: Int): PackageTransferRequestConnection! + usageMetrics(forRange: MetricRange!, variant: MetricType!): [UsageMetric]! + domains(offset: Int, before: String, after: String, first: Int, last: Int): DNSDomainConnection! +} + +type NamespaceCollaboratorInviteConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [NamespaceCollaboratorInviteEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +""" +A Relay edge containing a `NamespaceCollaboratorInvite` and its cursor. +""" +type NamespaceCollaboratorInviteEdge { + """The item at the end of the edge""" + node: NamespaceCollaboratorInvite + + """A cursor for use in pagination""" + cursor: String! +} + +type NamespaceCollaboratorInvite implements Node { + """The ID of the object""" + id: ID! + requestedBy: User! + user: User + inviteEmail: String + namespace: Namespace! + role: RegistryNamespaceMaintainerInviteRoleChoices! + accepted: NamespaceCollaborator + approvedBy: User + declinedBy: User + createdAt: DateTime! + expiresAt: DateTime! + closedAt: DateTime +} + +enum RegistryNamespaceMaintainerInviteRoleChoices { + """Owner""" + OWNER + + """Admin""" + ADMIN + + """Editor""" + EDITOR + + """Viewer""" + VIEWER +} + +type NamespaceCollaborator implements Node { + """The ID of the object""" + id: ID! + user: User! + role: RegistryNamespaceMaintainerRoleChoices! + namespace: Namespace! + createdAt: DateTime! + updatedAt: DateTime! + invite: NamespaceCollaboratorInvite +} + +enum RegistryNamespaceMaintainerRoleChoices { + """Owner""" + OWNER + + """Admin""" + ADMIN + + """Editor""" + EDITOR + + """Viewer""" + VIEWER +} + +type UserConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [UserEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `User` and its cursor.""" +type UserEdge { + """The item at the end of the edge""" + node: User + + """A cursor for use in pagination""" + cursor: String! +} + +type PackageConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [PackageEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `Package` and its cursor.""" +type PackageEdge { + """The item at the end of the edge""" + node: Package + + """A cursor for use in pagination""" + cursor: String! +} + +type Package implements Likeable & Node & PackageOwner { + """The ID of the object""" + id: ID! + name: String! + private: Boolean! + createdAt: DateTime! + updatedAt: DateTime! + maintainers: [User]! @deprecated(reason: "Please use collaborators instead") + curated: Boolean! + ownerObjectId: Int! + lastVersion: PackageVersion + + """The app icon. It should be formatted in the same way as Apple icons""" + icon: String! + totalDownloads: Int! + iconUpdatedAt: DateTime + watchersCount: Int! + webcs(offset: Int, before: String, after: String, first: Int, last: Int): WebcImageConnection! + + """List of app templates for this package""" + appTemplates(offset: Int, before: String, after: String, first: Int, last: Int): AppTemplateConnection! + packagewebcSet(offset: Int, before: String, after: String, first: Int, last: Int): PackageWebcConnection! + versions: [PackageVersion]! + collectionSet: [Collection!]! + categories(offset: Int, before: String, after: String, first: Int, last: Int): CategoryConnection! + keywords(offset: Int, before: String, after: String, first: Int, last: Int): PackageKeywordConnection! + likersCount: Int! + viewerHasLiked: Boolean! + globalName: String! + globalId: ID! + alias: String + namespace: String! + displayName: String! + + """The name of the package without the owner""" + packageName: String! + + """The app icon. It should be formatted in the same way as Apple icons""" + appIcon: String! @deprecated(reason: "Please use icon instead") + + """The total number of downloads of the package""" + downloadsCount: Int + + """The public keys for all the published versions""" + publicKeys: [PublicKey!]! + collaborators(offset: Int, before: String, after: String, first: Int, last: Int): PackageCollaboratorConnection! + pendingInvites(offset: Int, before: String, after: String, first: Int, last: Int): PackageCollaboratorInviteConnection! + viewerHasRole(role: GrapheneRole!): Boolean! + viewerAsCollaborator(role: GrapheneRole): PackageCollaborator + owner: PackageOwner! + isTransferring: Boolean! + activeTransferRequest: PackageTransferRequest + isArchived: Boolean! + viewerIsWatching: Boolean! + showDeployButton: Boolean! + similarPackageVersions(before: String, after: String, first: Int, last: Int): PackageSearchConnection! + + """Whether the current user is invited to the package""" + viewerIsInvited: Boolean! + + """The invitation for the current user to the package""" + viewerInvitation: PackageCollaboratorInvite +} + +interface Likeable { + id: ID! + likersCount: Int! + viewerHasLiked: Boolean! +} + +type PackageVersion implements Node & PackageInstance { + """The ID of the object""" + id: ID! + package: Package! + webcGenerationErrors: String + version: String! + description: String! + manifest: String! + license: String + licenseFile: String + readme: String + witMd: String + repository: String + homepage: String + createdAt: DateTime! + updatedAt: DateTime! + staticObjectsCompiled: Boolean! + nativeExecutablesCompiled: Boolean! + publishedBy: User! + clientName: String + signature: Signature + isArchived: Boolean! + file: String! + + """""" + fileSize: BigInt! + webc: WebcImage + totalDownloads: Int! + bindingsState: RegistryPackageVersionBindingsStateChoices! + nativeExecutablesState: RegistryPackageVersionNativeExecutablesStateChoices! + + """List of direct dependencies of this package version""" + dependencies(offset: Int, before: String, after: String, first: Int, last: Int): PackageVersionConnection! + deployappversionSet(offset: Int, before: String, after: String, first: Int, last: Int): DeployAppVersionConnection! + lastversionPackage(offset: Int, before: String, after: String, first: Int, last: Int): PackageConnection! + commands: [Command!]! + nativeexecutableSet(offset: Int, before: String, after: String, first: Int, last: Int): NativeExecutableConnection! + bindingsgeneratorSet(offset: Int, before: String, after: String, first: Int, last: Int): BindingsGeneratorConnection! + javascriptlanguagebindingSet(offset: Int, before: String, after: String, first: Int, last: Int): PackageVersionNPMBindingConnection! + pythonlanguagebindingSet(offset: Int, before: String, after: String, first: Int, last: Int): PackageVersionPythonBindingConnection! + piritaManifest: JSONString + piritaOffsets: JSONString + piritaVolumes: JSONString + piritaFile: String @deprecated(reason: "Please use distribution.piritaDownloadUrl instead.") + piritaFileSize: Int @deprecated(reason: "Please use distribution.piritaSize instead.") + pirita256hash: String @deprecated(reason: "Please use distribution.piritaSha256Hash instead.") + distribution: PackageDistribution! + filesystem: [PackageVersionFilesystem]! + isLastVersion: Boolean! + witFile: String + isSigned: Boolean! + moduleInterfaces: [InterfaceVersion!]! + modules: [PackageVersionModule!]! + getPiritaContents(volume: String! = "atom", base: String! = ""): [PiritaFilesystemItem!]! + getWebcContents(volume: String! = "atom", base: String! = "/"): [WEBCFilesystemItem!]! + nativeExecutables(triple: String, wasmerCompilerVersion: String): [NativeExecutable] + bindings: [PackageVersionLanguageBinding]! + npmBindings: PackageVersionNPMBinding + pythonBindings: PackageVersionPythonBinding + bindingsSet(before: String, after: String, first: Int, last: Int): PackageVersionBindingConnection + hasBindings: Boolean! + hasCommands: Boolean! + showDeployButton: Boolean! + isCorrupt: Boolean! +} + +interface PackageInstance { + piritaManifest: JSONString + piritaOffsets: JSONString + piritaVolumes: JSONString + isArchived: Boolean! + clientName: String + publishedBy: User! + createdAt: DateTime! + updatedAt: DateTime! + package: Package! + webc: WebcImage +} + +""" +Allows use of a JSON String for input / output from the GraphQL schema. + +Use of this type is *not recommended* as you lose the benefits of having a defined, static +schema (one of the key benefits of GraphQL). +""" +scalar JSONString + +type WebcImage implements Node { + """The ID of the object""" + id: ID! + + """""" + fileSize: BigInt! + manifest: JSONString! + volumes: JSONString! + offsets: JSONString! + webcSha256: String! + targzSha256: String + createdAt: DateTime! + updatedAt: DateTime! + webcUrl: String! +} + +""" +The `BigInt` scalar type represents non-fractional whole numeric values. +`BigInt` is not constrained to 32-bit like the `Int` type and thus is a less +compatible type. +""" +scalar BigInt + +enum RegistryPackageVersionBindingsStateChoices { + """Bindings are not detected""" + NOT_PRESENT + + """Bindings are being built""" + GENERATING + + """Bindings generation has failed""" + ERROR + + """Bindings are built and present""" + GENERATED_AND_PRESENT +} + +enum RegistryPackageVersionNativeExecutablesStateChoices { + """Native Executables are not detected""" + NOT_PRESENT + + """Native Executables are being built""" + GENERATING + + """Native Executables generation has failed""" + ERROR + + """Native Executables are built and present""" + GENERATED_AND_PRESENT +} + +type PackageVersionConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [PackageVersionEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `PackageVersion` and its cursor.""" +type PackageVersionEdge { + """The item at the end of the edge""" + node: PackageVersion + + """A cursor for use in pagination""" + cursor: String! +} + +type DeployAppVersionConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [DeployAppVersionEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `DeployAppVersion` and its cursor.""" +type DeployAppVersionEdge { + """The item at the end of the edge""" + node: DeployAppVersion + + """A cursor for use in pagination""" + cursor: String! +} + +type DeployAppVersion implements Node { + """The ID of the object""" + id: ID! + app: DeployApp! + yamlConfig: String! + userYamlConfig: String! + clientName: String + signature: String + description: String + publishedBy: User! + createdAt: DateTime! + updatedAt: DateTime! + configWebc: String @deprecated(reason: "webc support has been deprecated for apps") + config: String! @deprecated(reason: "Please use jsonConfig instead") + jsonConfig: String! + url: String! + permalink: String! + urls: [String]! + version: String! + isActive: Boolean! + manifest: String! + logs( + """ + Get logs starting from this timestamp. Takes EPOCH timestamp in seconds. + """ + startingFrom: Float + + """Get logs starting from this timestamp. Takes ISO timestamp.""" + startingFromISO: DateTime + + """Fetch logs until this timestamp. Takes EPOCH timestamp in seconds.""" + until: Float + + """List of streams to fetch logs from. e.g. stdout, stderr.""" + streams: [LogStream] + + """List of instance ids to fetch logs from.""" + instanceIds: [String] + before: String + after: String + first: Int + last: Int + ): LogConnection! + usageMetrics(forRange: MetricRange!, variant: MetricType!): [UsageMetric]! + sourcePackageVersion: PackageVersion! + aggregateMetrics: AggregateMetrics! + volumes: [AppVersionVolume] + favicon: URL + screenshot: URL +} + +type DeployApp implements Node & Owner { + """The ID of the object""" + id: ID! + createdBy: User! + createdAt: DateTime! + updatedAt: DateTime! + activeVersion: DeployAppVersion! + globalName: String! + globalId: ID! + url: String! + adminUrl: String! + permalink: String! + urls: [String]! + description: String + name: String! + owner: Owner! + versions(sortBy: DeployAppVersionsSortBy, createdAfter: DateTime, offset: Int, before: String, after: String, first: Int, last: Int): DeployAppVersionConnection! + aggregateMetrics: AggregateMetrics! + aliases(offset: Int, before: String, after: String, first: Int, last: Int): AppAliasConnection! + usageMetrics(forRange: MetricRange!, variant: MetricType!): [UsageMetric]! + deleted: Boolean! + favicon: URL + screenshot: URL +} + +enum DeployAppVersionsSortBy { + NEWEST + OLDEST +} + +type AggregateMetrics { + cpuTime: String! + memoryTime: String! + ingress: String! + egress: String! + noRequests: String! + noFailedRequests: String! + monthlyCost: String! +} + +type AppAliasConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [AppAliasEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `AppAlias` and its cursor.""" +type AppAliasEdge { + """The item at the end of the edge""" + node: AppAlias + + """A cursor for use in pagination""" + cursor: String! +} + +type AppAlias implements Node { + name: String! + app: DeployApp! + isDefault: Boolean! + + """The ID of the object""" + id: ID! + url: String! +} + +type UsageMetric { + variant: MetricType! + value: Float! + unit: MetricUnit! + timestamp: DateTime! +} + +enum MetricType { + cpu_time + memory_time + network_egress + network_ingress + no_of_requests + no_of_failed_requests + cost +} + +"""Units for metrics""" +enum MetricUnit { + """represents the unit of "seconds".""" + SEC + + """represents the unit of "milliseconds".""" + MS + + """represents the unit of "kilobytes".""" + KB + + """represents the unit of "kilobytes per second".""" + KBS + + """represents the unit of "number of requests".""" + NO_REQUESTS + + """represents the unit of "cost" in USD.""" + DOLLARS +} + +enum MetricRange { + LAST_24_HOURS + LAST_30_DAYS + LAST_1_HOUR +} + +""" +The `URL` scalar type represents a URL as text, represented as UTF-8 +character sequences. +""" +scalar URL + +type LogConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [LogEdge]! +} + +"""A Relay edge containing a `Log` and its cursor.""" +type LogEdge { + """The item at the end of the edge""" + node: Log + + """A cursor for use in pagination""" + cursor: String! +} + +enum LogStream { + STDOUT + STDERR + RUNTIME +} + +type AppVersionVolume { + name: String! + mountPaths: [AppVersionVolumeMountPath]! + size: Int + usedSize: Int +} + +type AppVersionVolumeMountPath { + path: String! + subpath: String! +} + +type Command { + command: String! + packageVersion: PackageVersion! + module: PackageVersionModule! +} + +type PackageVersionModule { + name: String! + source: String! + abi: String + publicUrl: String! + atom: PiritaFilesystemFile! + rangeHeader: String! +} + +type PiritaFilesystemFile { + name(display: PiritaFilesystemNameDisplay): String! + size: Int! + offset: Int! +} + +enum PiritaFilesystemNameDisplay { + RELATIVE + ABSOLUTE +} + +type NativeExecutableConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [NativeExecutableEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `NativeExecutable` and its cursor.""" +type NativeExecutableEdge { + """The item at the end of the edge""" + node: NativeExecutable + + """A cursor for use in pagination""" + cursor: String! +} + +type NativeExecutable implements Node { + """The ID of the object""" + id: ID! + module: String! @deprecated(reason: "Use filename instead") + filename: String! + filesize: Int! + targetTriple: String! + downloadUrl: String! +} + +type BindingsGeneratorConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [BindingsGeneratorEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `BindingsGenerator` and its cursor.""" +type BindingsGeneratorEdge { + """The item at the end of the edge""" + node: BindingsGenerator + + """A cursor for use in pagination""" + cursor: String! +} + +type BindingsGenerator implements Node { + """The ID of the object""" + id: ID! + packageVersion: PackageVersion! + active: Boolean! + commandName: String! + registryJavascriptlanguagebindings(offset: Int, before: String, after: String, first: Int, last: Int): PackageVersionNPMBindingConnection! + registryPythonlanguagebindings(offset: Int, before: String, after: String, first: Int, last: Int): PackageVersionPythonBindingConnection! +} + +type PackageVersionNPMBindingConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [PackageVersionNPMBindingEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `PackageVersionNPMBinding` and its cursor.""" +type PackageVersionNPMBindingEdge { + """The item at the end of the edge""" + node: PackageVersionNPMBinding + + """A cursor for use in pagination""" + cursor: String! +} + +type PackageVersionNPMBinding implements PackageVersionLanguageBinding & Node { + """The ID of the object""" + id: ID! + language: ProgrammingLanguage! + + """The URL of the generated artifacts on Wasmer CDN.""" + url: String! + + """When the binding was generated""" + createdAt: DateTime! + + """Package version used to generate this binding""" + generator: BindingsGenerator! + name: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") + kind: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") + + """Name of package source""" + packageName: String! + + """Name of the package to import""" + importablePackageName: String! + + """Code snippet example to use the package""" + codeSnippetExample: String! + module: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") + npmDefaultInstallPackageName(url: String): String! @deprecated(reason: "Please use packageName instead") +} + +interface PackageVersionLanguageBinding { + id: ID! + language: ProgrammingLanguage! + + """The URL of the generated artifacts on Wasmer CDN.""" + url: String! + + """When the binding was generated""" + createdAt: DateTime! + + """Package version used to generate this binding""" + generator: BindingsGenerator! + name: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") + kind: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") + + """Name of package source""" + packageName: String! + + """Name of the package to import""" + importablePackageName: String! + + """Code snippet example to use the package""" + codeSnippetExample: String! + module: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") +} + +enum ProgrammingLanguage { + PYTHON + JAVASCRIPT +} + +type PackageVersionPythonBindingConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [PackageVersionPythonBindingEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +""" +A Relay edge containing a `PackageVersionPythonBinding` and its cursor. +""" +type PackageVersionPythonBindingEdge { + """The item at the end of the edge""" + node: PackageVersionPythonBinding + + """A cursor for use in pagination""" + cursor: String! +} + +type PackageVersionPythonBinding implements PackageVersionLanguageBinding & Node { + """The ID of the object""" + id: ID! + language: ProgrammingLanguage! + + """The URL of the generated artifacts on Wasmer CDN.""" + url: String! + + """When the binding was generated""" + createdAt: DateTime! + + """Package version used to generate this binding""" + generator: BindingsGenerator! + name: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") + kind: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") + + """Name of package source""" + packageName: String! + + """Name of the package to import""" + importablePackageName: String! + + """Code snippet example to use the package""" + codeSnippetExample: String! + module: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") + pythonDefaultInstallPackageName(url: String): String! +} + +type PackageDistribution { + """ + Download URL of the tar.gz file. + If the package was published with webc only,this will contain download URL for webc file instead. + """ + downloadUrl: String! + expiresInSeconds: Int + size: Int + piritaDownloadUrl: String + piritaExpiresInSeconds: Int + piritaSize: Int + piritaSha256Hash: String + webcDownloadUrl: String + webcExpiresInSeconds: Int + webcSize: Int + webcSha256Hash: String +} + +type PackageVersionFilesystem { + wasm: String! + host: String! +} + +type InterfaceVersion implements Node { + """The ID of the object""" + id: ID! + interface: Interface! + version: String! + content: String! + createdAt: DateTime! + updatedAt: DateTime! + publishedBy: User! + packageVersions(offset: Int, before: String, after: String, first: Int, last: Int): PackageVersionConnection! +} + +type Interface implements Node { + """The ID of the object""" + id: ID! + name: String! + displayName: String! + description: String! + homepage: String + icon: String + createdAt: DateTime! + updatedAt: DateTime! + versions(offset: Int, before: String, after: String, first: Int, last: Int): InterfaceVersionConnection! + lastVersion: InterfaceVersion +} + +type InterfaceVersionConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [InterfaceVersionEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `InterfaceVersion` and its cursor.""" +type InterfaceVersionEdge { + """The item at the end of the edge""" + node: InterfaceVersion + + """A cursor for use in pagination""" + cursor: String! +} + +union PiritaFilesystemItem = PiritaFilesystemFile | PiritaFilesystemDir + +type PiritaFilesystemDir { + name(display: PiritaFilesystemNameDisplay): String! +} + +type WEBCFilesystemItem { + name: String! + checksum: String! + size: Int! + offset: Int! +} + +type PackageVersionBindingConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [PackageVersionBindingEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `PackageVersionBinding` and its cursor.""" +type PackageVersionBindingEdge { + """The item at the end of the edge""" + node: PackageVersionBinding + + """A cursor for use in pagination""" + cursor: String! +} + +union PackageVersionBinding = PackageVersionNPMBinding | PackageVersionPythonBinding + +type WebcImageConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [WebcImageEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `WebcImage` and its cursor.""" +type WebcImageEdge { + """The item at the end of the edge""" + node: WebcImage + + """A cursor for use in pagination""" + cursor: String! +} + +type AppTemplateConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [AppTemplateEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `AppTemplate` and its cursor.""" +type AppTemplateEdge { + """The item at the end of the edge""" + node: AppTemplate + + """A cursor for use in pagination""" + cursor: String! +} + +type AppTemplate implements Node { + """The ID of the object""" + id: ID! + name: String! + slug: String! + description: String! + demoUrl: String! + repoUrl: String! + category: AppTemplateCategory! + isPublic: Boolean! + createdAt: DateTime! + updatedAt: DateTime! + readme: String! + useCases: JSONString! + framework: String! + language: String! + repoLicense: String! + usingPackage: Package + defaultImage: String +} + +type AppTemplateCategory implements Node { + """The ID of the object""" + id: ID! + name: String! + slug: String! + description: String! + createdAt: DateTime! + updatedAt: DateTime! + appTemplates(offset: Int, before: String, after: String, first: Int, last: Int): AppTemplateConnection! +} + +type PackageWebcConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [PackageWebcEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `PackageWebc` and its cursor.""" +type PackageWebcEdge { + """The item at the end of the edge""" + node: PackageWebc + + """A cursor for use in pagination""" + cursor: String! +} + +type PackageWebc implements Node & PackageInstance { + """The ID of the object""" + id: ID! + package: Package! + webc: WebcImage + createdAt: DateTime! + updatedAt: DateTime! + piritaManifest: JSONString + piritaOffsets: JSONString + piritaVolumes: JSONString + isArchived: Boolean! + clientName: String + publishedBy: User! + webcUrl: String! +} + +type Collection { + slug: String! + displayName: String! + description: String! + createdAt: DateTime! + banner: String! + packages(before: String, after: String, first: Int, last: Int): PackageConnection! +} + +type CategoryConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [CategoryEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `Category` and its cursor.""" +type CategoryEdge { + """The item at the end of the edge""" + node: Category + + """A cursor for use in pagination""" + cursor: String! +} + +type Category implements Node { + """The ID of the object""" + id: ID! + + """A category is a label that can be attached to a package.""" + name: String! + packages(offset: Int, before: String, after: String, first: Int, last: Int): PackageConnection +} + +type PackageKeywordConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [PackageKeywordEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `PackageKeyword` and its cursor.""" +type PackageKeywordEdge { + """The item at the end of the edge""" + node: PackageKeyword + + """A cursor for use in pagination""" + cursor: String! +} + +type PackageKeyword implements Node { + """The ID of the object""" + id: ID! + name: String! +} + +type PackageCollaboratorConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [PackageCollaboratorEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `PackageCollaborator` and its cursor.""" +type PackageCollaboratorEdge { + """The item at the end of the edge""" + node: PackageCollaborator + + """A cursor for use in pagination""" + cursor: String! +} + +type PackageCollaborator implements Node { + """The ID of the object""" + id: ID! + user: User! + role: RegistryPackageMaintainerRoleChoices! + package: Package! + createdAt: DateTime! + updatedAt: DateTime! + invite: PackageCollaboratorInvite +} + +enum RegistryPackageMaintainerRoleChoices { + """Owner""" + OWNER + + """Admin""" + ADMIN + + """Editor""" + EDITOR + + """Viewer""" + VIEWER +} + +type PackageCollaboratorInvite implements Node { + """The ID of the object""" + id: ID! + requestedBy: User! + user: User + inviteEmail: String + package: Package! + role: RegistryPackageMaintainerInviteRoleChoices! + accepted: PackageCollaborator + approvedBy: User + declinedBy: User + createdAt: DateTime! + expiresAt: DateTime! + closedAt: DateTime +} + +enum RegistryPackageMaintainerInviteRoleChoices { + """Owner""" + OWNER + + """Admin""" + ADMIN + + """Editor""" + EDITOR + + """Viewer""" + VIEWER +} + +type PackageCollaboratorInviteConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [PackageCollaboratorInviteEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `PackageCollaboratorInvite` and its cursor.""" +type PackageCollaboratorInviteEdge { + """The item at the end of the edge""" + node: PackageCollaboratorInvite + + """A cursor for use in pagination""" + cursor: String! +} + +enum GrapheneRole { + OWNER + ADMIN + EDITOR + VIEWER +} + +type PackageTransferRequest implements Node { + """The ID of the object""" + id: ID! + requestedBy: User! + previousOwnerObjectId: Int! + newOwnerObjectId: Int! + package: Package! + approvedBy: User + declinedBy: User + createdAt: DateTime! + expiresAt: DateTime! + closedAt: DateTime + previousOwner: PackageOwner! + newOwner: PackageOwner! +} + +type PackageSearchConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [PackageSearchEdge]! + totalCount: Int +} + +"""A Relay edge containing a `PackageSearch` and its cursor.""" +type PackageSearchEdge { + """The item at the end of the edge""" + node: PackageVersion + + """A cursor for use in pagination""" + cursor: String! +} + +type DeployAppConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [DeployAppEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `DeployApp` and its cursor.""" +type DeployAppEdge { + """The item at the end of the edge""" + node: DeployApp + + """A cursor for use in pagination""" + cursor: String! +} + +enum DeployAppsSortBy { + NEWEST + OLDEST + MOST_ACTIVE +} + +type NamespaceCollaboratorConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [NamespaceCollaboratorEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `NamespaceCollaborator` and its cursor.""" +type NamespaceCollaboratorEdge { + """The item at the end of the edge""" + node: NamespaceCollaborator + + """A cursor for use in pagination""" + cursor: String! +} + +type PackageTransferRequestConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [PackageTransferRequestEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `PackageTransferRequest` and its cursor.""" +type PackageTransferRequestEdge { + """The item at the end of the edge""" + node: PackageTransferRequest + + """A cursor for use in pagination""" + cursor: String! +} + +type DNSDomainConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [DNSDomainEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `DNSDomain` and its cursor.""" +type DNSDomainEdge { + """The item at the end of the edge""" + node: DNSDomain + + """A cursor for use in pagination""" + cursor: String! +} + +type DNSDomain implements Node { + name: String! + + """This zone will be accessible at /dns/{slug}/.""" + slug: String! + zoneFile: String! + createdAt: DateTime! + updatedAt: DateTime! + deletedAt: DateTime + + """The ID of the object""" + id: ID! + records: [DNSRecord] + owner: Owner! +} + +union DNSRecord = ARecord | AAAARecord | CNAMERecord | TXTRecord | MXRecord | NSRecord | CAARecord | DNAMERecord | PTRRecord | SOARecord | SRVRecord | SSHFPRecord + +type ARecord implements Node & DNSRecordInterface { + createdAt: DateTime! + updatedAt: DateTime! + deletedAt: DateTime + address: String! + + """The ID of the object""" + id: ID! + name: String! + ttl: Int! + dnsClass: String + text: String! + domain: DNSDomain! +} + +interface DNSRecordInterface { + name: String! + ttl: Int! + dnsClass: String + text: String! + domain: DNSDomain! + createdAt: DateTime! + updatedAt: DateTime! + deletedAt: DateTime +} + +type AAAARecord implements Node & DNSRecordInterface { + createdAt: DateTime! + updatedAt: DateTime! + deletedAt: DateTime + address: String! + + """The ID of the object""" + id: ID! + name: String! + ttl: Int! + dnsClass: String + text: String! + domain: DNSDomain! +} + +type CNAMERecord implements Node & DNSRecordInterface { + createdAt: DateTime! + updatedAt: DateTime! + deletedAt: DateTime + + """This domain name will alias to this canonical name.""" + cName: String! + + """The ID of the object""" + id: ID! + name: String! + ttl: Int! + dnsClass: String + text: String! + domain: DNSDomain! +} + +type TXTRecord implements Node & DNSRecordInterface { + createdAt: DateTime! + updatedAt: DateTime! + deletedAt: DateTime + data: String! + + """The ID of the object""" + id: ID! + name: String! + ttl: Int! + dnsClass: String + text: String! + domain: DNSDomain! +} + +type MXRecord implements Node & DNSRecordInterface { + createdAt: DateTime! + updatedAt: DateTime! + deletedAt: DateTime + preference: Int! + exchange: String! + + """The ID of the object""" + id: ID! + name: String! + ttl: Int! + dnsClass: String + text: String! + domain: DNSDomain! +} + +type NSRecord implements Node & DNSRecordInterface { + createdAt: DateTime! + updatedAt: DateTime! + deletedAt: DateTime + nsdname: String! + + """The ID of the object""" + id: ID! + name: String! + ttl: Int! + dnsClass: String + text: String! + domain: DNSDomain! +} + +type CAARecord implements Node & DNSRecordInterface { + createdAt: DateTime! + updatedAt: DateTime! + deletedAt: DateTime + flags: Int! + tag: DnsmanagerCertificationAuthorityAuthorizationRecordTagChoices! + value: String! + + """The ID of the object""" + id: ID! + name: String! + ttl: Int! + dnsClass: String + text: String! + domain: DNSDomain! +} + +enum DnsmanagerCertificationAuthorityAuthorizationRecordTagChoices { + """issue""" + ISSUE + + """issue wildcard""" + ISSUEWILD + + """Incident object description exchange format""" + IODEF +} + +type DNAMERecord implements Node & DNSRecordInterface { + createdAt: DateTime! + updatedAt: DateTime! + deletedAt: DateTime + + """ + This domain name will alias to the entire subtree of that delegation domain. + """ + dName: String! + + """The ID of the object""" + id: ID! + name: String! + ttl: Int! + dnsClass: String + text: String! + domain: DNSDomain! +} + +type PTRRecord implements Node & DNSRecordInterface { + createdAt: DateTime! + updatedAt: DateTime! + deletedAt: DateTime + ptrdname: String! + + """The ID of the object""" + id: ID! + name: String! + ttl: Int! + dnsClass: String + text: String! + domain: DNSDomain! +} + +type SOARecord implements Node & DNSRecordInterface { + createdAt: DateTime! + updatedAt: DateTime! + deletedAt: DateTime + + """Primary master name server for this zone.""" + mname: String! + + """Email address of the administrator responsible for this zone.""" + rname: String! + + """ + A slave name server will initiate a zone transfer if this serial is incremented. + """ + serial: BigInt! + + """ + Number of seconds after which secondary name servers should query the master to detect zone changes. + """ + refresh: BigInt! + + """ + Number of seconds after which secondary name servers should retry to request the serial number from the master if the master does not respond. + """ + retry: BigInt! + + """ + Number of seconds after which secondary name servers should stop answering request for this zone if the master does not respond. + """ + expire: BigInt! + + """Time to live for purposes of negative caching.""" + minimum: BigInt! + + """The ID of the object""" + id: ID! + name: String! + ttl: Int! + dnsClass: String + text: String! + domain: DNSDomain! +} + +type SRVRecord implements Node & DNSRecordInterface { + createdAt: DateTime! + updatedAt: DateTime! + deletedAt: DateTime + + """The symbolic name of the desired service.""" + service: String! + + """ + The transport protocol of the desired service, usually either TCP or UDP. + """ + protocol: String! + + """The priority of the target host, lower value means more preferred.""" + priority: Int! + + """ + A relative weight for records with the same priority, higher value means higher chance of getting picked. + """ + weight: Int! + port: Int! + + """ + The canonical hostname of the machine providing the service, ending in a dot. + """ + target: String! + + """The ID of the object""" + id: ID! + name: String! + ttl: Int! + dnsClass: String + text: String! + domain: DNSDomain! +} + +type SSHFPRecord implements Node & DNSRecordInterface { + createdAt: DateTime! + updatedAt: DateTime! + deletedAt: DateTime + algorithm: DnsmanagerSshFingerprintRecordAlgorithmChoices! + type: DnsmanagerSshFingerprintRecordTypeChoices! + fingerprint: String! + + """The ID of the object""" + id: ID! + name: String! + ttl: Int! + dnsClass: String + text: String! + domain: DNSDomain! +} + +enum DnsmanagerSshFingerprintRecordAlgorithmChoices { + """RSA""" + A_1 + + """DSA""" + A_2 + + """ECDSA""" + A_3 + + """Ed25519""" + A_4 +} + +enum DnsmanagerSshFingerprintRecordTypeChoices { + """SHA-1""" + A_1 + + """SHA-256""" + A_2 +} + +type APITokenConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [APITokenEdge]! +} + +"""A Relay edge containing a `APIToken` and its cursor.""" +type APITokenEdge { + """The item at the end of the edge""" + node: APIToken + + """A cursor for use in pagination""" + cursor: String! +} + +type APIToken { + id: ID! + user: User! + identifier: String + createdAt: DateTime! + revokedAt: DateTime + lastUsedAt: DateTime + nonceSet(offset: Int, before: String, after: String, first: Int, last: Int): NonceConnection! +} + +type NonceConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [NonceEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `Nonce` and its cursor.""" +type NonceEdge { + """The item at the end of the edge""" + node: Nonce + + """A cursor for use in pagination""" + cursor: String! +} + +type Nonce implements Node { + """The ID of the object""" + id: ID! + name: String! + callbackUrl: String! + createdAt: DateTime! + isValidated: Boolean! + secret: String! + token: String! + expired: Boolean! + authUrl: String! +} + +type UserNotificationConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [UserNotificationEdge]! + hasPendingNotifications: Boolean! + pendingNotificationsCount: Int! +} + +"""A Relay edge containing a `UserNotification` and its cursor.""" +type UserNotificationEdge { + """The item at the end of the edge""" + node: UserNotification + + """A cursor for use in pagination""" + cursor: String! +} + +type UserNotification implements Node { + """The ID of the object""" + id: ID! + icon: String + body: EventBody! + seenState: UserNotificationSeenState! + kind: UserNotificationKind + createdAt: DateTime! +} + +enum UserNotificationSeenState { + UNSEEN + SEEN + SEEN_AND_READ +} + +union UserNotificationKind = UserNotificationKindPublishedPackageVersion | UserNotificationKindIncomingPackageTransfer | UserNotificationKindIncomingPackageInvite | UserNotificationKindIncomingNamespaceInvite | UserNotificationKindValidateEmail + +type UserNotificationKindPublishedPackageVersion { + packageVersion: PackageVersion! +} + +type UserNotificationKindIncomingNamespaceInvite { + namespaceInvite: NamespaceCollaboratorInvite! +} + +type UserNotificationKindValidateEmail { + user: User! +} + +""" + + Enum of ways a user can login. One user can have many login methods + associated with their account. + +""" +enum LoginMethod { + GOOGLE + GITHUB + PASSWORD +} + +type SocialAuth implements Node { + """The ID of the object""" + id: ID! + user: User! + provider: String! + uid: String! + extraData: JSONString! + created: DateTime! + modified: DateTime! + username: String! +} + +type Signature { + id: ID! + publicKey: PublicKey! + data: String! + createdAt: DateTime! +} + +type StripeCustomer { + id: ID! +} + +type Billing { + stripeCustomer: StripeCustomer! + payments: [PaymentIntent]! + paymentMethods: [PaymentMethod]! +} + +type PaymentIntent implements Node { + """The datetime this object was created in stripe.""" + created: DateTime + + """Three-letter ISO currency code""" + currency: String! + + """ + Status of this PaymentIntent, one of requires_payment_method, requires_confirmation, requires_action, processing, requires_capture, canceled, or succeeded. You can read more about PaymentIntent statuses here. + """ + status: DjstripePaymentIntentStatusChoices! + + """The ID of the object""" + id: ID! + amount: String! +} + +enum DjstripePaymentIntentStatusChoices { + """ + Cancellation invalidates the intent for future confirmation and cannot be undone. + """ + CANCELED + + """Required actions have been handled.""" + PROCESSING + + """Payment Method require additional action, such as 3D secure.""" + REQUIRES_ACTION + + """Capture the funds on the cards which have been put on holds.""" + REQUIRES_CAPTURE + + """Intent is ready to be confirmed.""" + REQUIRES_CONFIRMATION + + """Intent created and requires a Payment Method to be attached.""" + REQUIRES_PAYMENT_METHOD + + """The funds are in your account.""" + SUCCEEDED +} + +union PaymentMethod = CardPaymentMethod + +type CardPaymentMethod implements Node { + """The ID of the object""" + id: ID! + brand: CardBrand! + country: String! + expMonth: Int! + expYear: Int! + funding: CardFunding! + last4: String! + isDefault: Boolean! +} + +""" +Card brand. + +Can be amex, diners, discover, jcb, mastercard, unionpay, visa, or unknown. +""" +enum CardBrand { + AMEX + DINERS + DISCOVER + JCB + MASTERCARD + UNIONPAY + VISA + UNKNOWN +} + +""" +Card funding type. + +Can be credit, debit, prepaid, or unknown. +""" +enum CardFunding { + CREDIT + DEBIT + PREPAID + UNKNOWN +} + +type Payment { + id: ID + amount: String + paidOn: DateTime +} + +"""Log entry for deploy app.""" +type Log { + """Timestamp in nanoseconds""" + timestamp: Float! + + """ISO 8601 string in UTC""" + datetime: DateTime! + + """Log message""" + message: String! + + """Log stream""" + stream: LogStream +} + +type UserNotificationKindIncomingPackageTransfer { + packageTransferRequest: PackageTransferRequest! +} + +type UserNotificationKindIncomingPackageInvite { + packageInvite: PackageCollaboratorInvite! +} + +input DeploymentV1 { + name: String! + workload: WorkloadV1! +} + +input WorkloadV1 { + capability: CapabilityMapV1 + name: String = null + runner: WorkloadRunnerV1! +} + +input AppV1Spec { + aliases: [String] = [] + workload: WorkloadV2! +} + +input WorkloadV2 { + source: String! +} + +input CapabilityCpuV1 { + maximumThreads: Int + maximumUsage: Int +} + +input FileSystemPermissionsV1 { + delete: Boolean + read: Boolean + write: Boolean +} + +input FileSystemVolumeMountV1 { + path: String! + permissions: [FileSystemPermissionsV1] +} + +input FileSystemVolumeSourceLocalV1 { + maximumSize: String! +} + +input FileSystemVolumeSourceV1 { + local: FileSystemVolumeSourceLocalV1! +} + +input FileSystemVolumeConfigV1 { + mounts: [FileSystemVolumeMountV1]! + name: String! + source: FileSystemVolumeSourceV1! +} + +input CapabilityFileSystemV1 { + volumes: [FileSystemVolumeConfigV1]! +} + +input CapabilityPersistentMemoryV1 { + volumes: [String] +} + +input CapabilityMemorySwapV1 { + maximumSize: String + memoryId: String +} + +input CapabilityNetworkV1 { + egress: NetworkEgressV1 +} + +input NetworkEgressV1 { + enabled: Boolean +} + +input CapabilityNetworkDnsV1 { + enabled: Boolean + servers: [String] + allowedHosts: NetworkDnsAllowedHostsV1 +} + +input NetworkDnsAllowedHostsV1 { + allowAllHosts: Boolean + hosts: [String] + regexPatterns: [String] + wildcardPatterns: [String] +} + +input CapabilityNetworkGatewayV1 { + domains: [String] + enforceHttps: Boolean +} + +input CapabilityMapV1 { + memorySwap: CapabilityCpuV1 +} + +input WebcSourceV1 { + name: String! + namespace: String! + repository: String! = "https://registry.wasmer.wtf" + tag: String + authToken: String +} + +input WorkloadRunnerV1 { + webProxy: RunnerWebProxyV1 + wcgi: RunnerWCGIV1 +} + +"""Run a webassembly file.""" +input RunnerWCGIV1 { + source: WorkloadRunnerWasmSourceV1! + dialect: String +} + +input RunnerWebProxyV1 { + source: WorkloadRunnerWasmSourceV1! +} + +input WorkloadRunnerWasmSourceV1 { + webc: WebcSourceV1! +} + +type Query { + latestTOS: TermsOfService! + getDeployAppVersion(name: String!, owner: String, version: String): DeployAppVersion + getAllDomains(namespace: String, offset: Int, before: String, after: String, first: Int, last: Int): DNSDomainConnection! + getAllDNSRecords(sortBy: DNSRecordsSortBy, updatedAfter: DateTime, before: String, after: String, first: Int, last: Int): DNSRecordConnection! + getDomain(name: String!): DNSDomain + getDeployApp( + name: String! + + """Owner of the app. Defaults to logged in user.""" + owner: String + ): DeployApp + getAppByGlobalAlias(alias: String!): DeployApp + getDeployApps(sortBy: DeployAppsSortBy, updatedAfter: DateTime, offset: Int, before: String, after: String, first: Int, last: Int): DeployAppConnection! + getAppVersions(sortBy: DeployAppVersionsSortBy, updatedAfter: DateTime, offset: Int, before: String, after: String, first: Int, last: Int): DeployAppVersionConnection! + getAppTemplates(categorySlug: String, offset: Int, before: String, after: String, first: Int, last: Int): AppTemplateConnection + getAppTemplate(slug: String!): AppTemplate + getAppTemplateCategories(offset: Int, before: String, after: String, first: Int, last: Int): AppTemplateCategoryConnection + viewer: User + getUser(username: String!): User + getPasswordResetToken(token: String!): GetPasswordResetToken + getAuthNonce(name: String!): Nonce + + """Can the logged in user create app templates?""" + canDeployAppToGithub: Boolean! + + """Check if a repo exists in the logged in user's github account.""" + checkRepoExists( + """The namespace of the repo to check.""" + namespace: String! + + """The name of the repo to check.""" + name: String! + ): Boolean! + + """Generate a unique repo name in the logged in user's github account.""" + newRepoName( + """The github namespace of the repo to create the repo in.""" + namespace: String! + + """The template to use.""" + templateSlug: String! + ): String! + packages(offset: Int, before: String, after: String, first: Int, last: Int): PackageConnection + recentPackageVersions(curated: Boolean, offset: Int, before: String, after: String, first: Int, last: Int): PackageVersionConnection! + allPackageVersions(sortBy: PackageVersionSortBy, createdAfter: DateTime, updatedAfter: DateTime, offset: Int, before: String, after: String, first: Int, last: Int): PackageVersionConnection! + getWebcImage(hash: String!): WebcImage + getNamespace(name: String!): Namespace + getPackage(name: String!): Package + getPackages(names: [String!]!): [Package]! + getPackageVersion(name: String!, version: String = "latest"): PackageVersion + getPackageVersions(names: [String!]!): [PackageVersion] + getPackageVersionByHash(name: String!, hash: String!): PackageVersion + getInterface(name: String!): Interface + getInterfaces(names: [String!]!): [Interface]! + getInterfaceVersion(name: String!, version: String = "latest"): InterfaceVersion + getContract(name: String!): Interface @deprecated(reason: "Please use getInterface instead") + getContracts(names: [String!]!): [Interface]! @deprecated(reason: "Please use getInterfaces instead") + getContractVersion(name: String!, version: String): InterfaceVersion @deprecated(reason: "Please use getInterfaceVersion instead") + getCommand(name: String!): Command + getCommands(names: [String!]!): [Command] + getCollections(before: String, after: String, first: Int, last: Int): CollectionConnection + getSignedUrlForPackageUpload(name: String!, version: String = "latest", expiresAfterSeconds: Int = 60): SignedUrl + getPackageHash(name: String!, hash: String!): PackageWebc! + categories(offset: Int, before: String, after: String, first: Int, last: Int): CategoryConnection! + blogposts(tags: [String!], before: String, after: String, first: Int, last: Int): BlogPostConnection! + getBlogpost(slug: String, featured: Boolean): BlogPost + allBlogpostTags(offset: Int, before: String, after: String, first: Int, last: Int): BlogPostTagConnection + search(query: String!, packages: PackagesFilter, namespaces: NamespacesFilter, users: UsersFilter, apps: AppFilter, blogposts: BlogPostsFilter, appTemplates: AppTemplateFilter, before: String, after: String, first: Int, last: Int): SearchConnection! + searchAutocomplete(kind: [SearchKind!], query: String!, before: String, after: String, first: Int, last: Int): SearchConnection! + getGlobalObject(slug: String!): GlobalObject + node( + """The ID of the object""" + id: ID! + ): Node + nodes(ids: [ID!]!): [Node] + info: RegistryInfo +} + +type TermsOfService implements Node { + """The ID of the object""" + id: ID! + content: String! + createdAt: DateTime! + viewerHasAccepted: Boolean! +} + +type DNSRecordConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [DNSRecordEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `DNSRecord` and its cursor.""" +type DNSRecordEdge { + """The item at the end of the edge""" + node: DNSRecord + + """A cursor for use in pagination""" + cursor: String! +} + +enum DNSRecordsSortBy { + NEWEST + OLDEST +} + +type AppTemplateCategoryConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [AppTemplateCategoryEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `AppTemplateCategory` and its cursor.""" +type AppTemplateCategoryEdge { + """The item at the end of the edge""" + node: AppTemplateCategory + + """A cursor for use in pagination""" + cursor: String! +} + +type GetPasswordResetToken { + valid: Boolean! + user: User +} + +enum PackageVersionSortBy { + NEWEST + OLDEST +} + +type CollectionConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [CollectionEdge]! +} + +"""A Relay edge containing a `Collection` and its cursor.""" +type CollectionEdge { + """The item at the end of the edge""" + node: Collection + + """A cursor for use in pagination""" + cursor: String! +} + +type SignedUrl { + url: String! +} + +type BlogPostConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [BlogPostEdge]! +} + +"""A Relay edge containing a `BlogPost` and its cursor.""" +type BlogPostEdge { + """The item at the end of the edge""" + node: BlogPost + + """A cursor for use in pagination""" + cursor: String! +} + +type BlogPost implements Node { + """The ID of the object""" + id: ID! + live: Boolean! + + """The page title as you'd like it to be seen by the public""" + title: String! + + """ + The name of the page as it will appear in URLs e.g http://domain.com/blog/[my-slug]/ + """ + slug: String! + owner: User + body: String! + publishDate: DateTime + theme: BlogBlogPostThemeChoices! + url: String! + coverImageUrl: String + opengraphImageUrl: String + tagline: String! + relatedArticles: [BlogPost!] + updatedAt: DateTime! + tags: [BlogPostTag!] + editUrl: String +} + +enum BlogBlogPostThemeChoices { + """Green""" + GREEN + + """Purple""" + PURPLE + + """Orange""" + ORANGE + + """Blue""" + BLUE +} + +type BlogPostTag implements Node { + """The ID of the object""" + id: ID! + name: String! + slug: String! +} + +type BlogPostTagConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [BlogPostTagEdge]! + + """Total number of items in the connection.""" + totalCount: Int +} + +"""A Relay edge containing a `BlogPostTag` and its cursor.""" +type BlogPostTagEdge { + """The item at the end of the edge""" + node: BlogPostTag + + """A cursor for use in pagination""" + cursor: String! +} + +type SearchConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [SearchEdge]! + totalCount: Int +} + +"""A Relay edge containing a `Search` and its cursor.""" +type SearchEdge { + """The item at the end of the edge""" + node: SearchResult + + """A cursor for use in pagination""" + cursor: String! +} + +union SearchResult = PackageVersion | User | Namespace | DeployApp | BlogPost | AppTemplate + +input PackagesFilter { + count: Int = 1000 + sortBy: SearchOrderSort = ASC + + """Filter packages by being curated.""" + curated: Boolean + + """Filter packages by publish date.""" + publishDate: SearchPublishDate + + """Filter packages by having bindings.""" + hasBindings: Boolean = false + + """Filter packages by being standalone.""" + isStandalone: Boolean = false + + """Filter packages by having commands.""" + hasCommands: Boolean = false + + """Filter packages by interface.""" + withInterfaces: [String] + + """Filter packages by deployable status.""" + deployable: Boolean + + """Filter packages by license.""" + license: String + + """Filter packages created after this date.""" + createdAfter: DateTime + + """Filter packages created before this date.""" + createdBefore: DateTime + + """Filter packages with version published after this date.""" + lastPublishedAfter: DateTime + + """Filter packages with version published before this date.""" + lastPublishedBefore: DateTime + + """Filter packages by size.""" + size: CountFilter + + """Filter packages by download count.""" + downloads: CountFilter + + """Filter packages by like count.""" + likes: CountFilter + + """Filter packages by owner.""" + owner: String + + """Filter packages by published by.""" + publishedBy: String + + """Order packages by field.""" + orderBy: PackageOrderBy = PUBLISHED_DATE +} + +enum SearchOrderSort { + ASC + DESC +} + +enum SearchPublishDate { + LAST_DAY + LAST_WEEK + LAST_MONTH + LAST_YEAR +} + +input CountFilter { + count: Int = 0 + comparison: CountComparison = GREATER_THAN_OR_EQUAL +} + +enum CountComparison { + EQUAL + GREATER_THAN + LESS_THAN + GREATER_THAN_OR_EQUAL + LESS_THAN_OR_EQUAL +} + +enum PackageOrderBy { + ALPHABETICALLY + SIZE + TOTAL_DOWNLOADS + PUBLISHED_DATE + CREATED_DATE + TOTAL_LIKES +} + +input NamespacesFilter { + count: Int = 1000 + sortBy: SearchOrderSort = ASC + + """Filter namespaces by package count.""" + packageCount: CountFilter + + """Filter namespaces created after this date.""" + createdAfter: DateTime + + """Filter namespaces created before this date.""" + createdBefore: DateTime + + """Filter namespaces by user count.""" + userCount: CountFilter + + """Filter namespaces by collaborator.""" + collaborator: String + + """Order namespaces by field.""" + orderBy: NamespaceOrderBy = CREATED_DATE +} + +enum NamespaceOrderBy { + PACKAGE_COUNT + COLLABORATOR_COUNT + APP_COUNT + CREATED_DATE +} + +input UsersFilter { + count: Int = 1000 + sortBy: SearchOrderSort = ASC + + """Filter users by package count.""" + packageCount: CountFilter + + """Filter users by namespace count.""" + namespaceCount: CountFilter + + """Filter users joined after this date.""" + joinedAfter: DateTime + + """Filter users joined before this date.""" + joinedBefore: DateTime + + """Order users by field.""" + orderBy: UserOrderBy = CREATED_DATE +} + +enum UserOrderBy { + PACKAGE_COUNT + APP_COUNT + CREATED_DATE +} + +input AppFilter { + count: Int = 1000 + sortBy: SearchOrderSort = ASC + + """Filter apps by deployed by.""" + deployedBy: String + + """Filter apps last deployed after this date.""" + lastDeployedAfter: DateTime + + """Filter apps last deployed before this date.""" + lastDeployedBefore: DateTime + + """Filter apps by owner.""" + owner: String + + """Order apps by field.""" + orderBy: AppOrderBy = CREATED_DATE + + """Filter apps by client name.""" + clientName: String +} + +enum AppOrderBy { + PUBLISHED_DATE + CREATED_DATE +} + +input BlogPostsFilter { + count: Int = 1000 + sortBy: SearchOrderSort = ASC + + """Filter blog posts by tag.""" + tags: [String] +} + +input AppTemplateFilter { + count: Int = 1000 + sortBy: SearchOrderSort = ASC + + """Order app templates by field.""" + orderBy: AppTemplateOrderBy = CREATED_DATE + + """Filter by app template framework""" + framework: String + + """Filter by app template language""" + language: String + + """Filter by one or more of the use-cases for the app template""" + useCases: [String] +} + +enum AppTemplateOrderBy { + CREATED_DATE +} + +enum SearchKind { + PACKAGE + NAMESPACE + USER +} + +union GlobalObject = User | Namespace + +type RegistryInfo { + """Base URL for this registry""" + baseUrl: String! + + """Base URL for the default frontend""" + defaultFrontend: String! + + """URL to the graphql endpoint""" + graphqlUrl: String! + + """URL to the graphql endpoint""" + createBlogpostUrl: String + + """Public metadata about packages""" + packages: PackageInfo! + + """Public metadata about the graphql schema""" + schema: SchemaInfo! +} + +type PackageInfo { + """Number of package versions published this month""" + versionsPublishedThisMonth: Int! + + """Number of new packages published this month""" + newPackagesThisMonth: Int! + + """Number of package downloads this month""" + packageDownloadsThisMonth: Int! +} + +type SchemaInfo { + """Download link for graphql schema""" + downloadUrl: String! + + """SHA256 hash of the schema data""" + SHA256Hash: String! + + """Timestamp when the schema was last updated""" + lastUpdated: DateTime! +} + +type Mutation { + """Viewer accepts the latest ToS.""" + acceptTOS(input: AcceptTOSInput!): AcceptTOSPayload + publishDeployApp(input: PublishDeployAppInput!): PublishDeployAppPayload + deleteApp(input: DeleteAppInput!): DeleteAppPayload + + """Add current user to the waitlist.""" + joinWaitlist(input: JoinWaitlistInput!): JoinWaitlistPayload + + """Add stripe payment to the user""" + addPayment(input: AddPaymentInput!): AddPaymentPayload + + """ + Mutation to change the active version of a DeployApp to another DeployAppVersion. + """ + markAppVersionAsActive(input: MarkAppVersionAsActiveInput!): MarkAppVersionAsActivePayload + + """Set a payment method as default for the user.""" + makePaymentDefault(input: SetDefaultPaymentMethodInput!): SetDefaultPaymentMethodPayload + + """ + Try to detach a payment method from customer. + Fails if trying to detach a default method, + or if it's the only payment method. + """ + detachPaymentMethod(input: DetachPaymentMethodInput!): DetachPaymentMethodPayload + generateDeployConfigToken(input: GenerateDeployConfigTokenInput!): GenerateDeployConfigTokenPayload + renameApp(input: RenameAppInput!): RenameAppPayload + renameAppAlias(input: RenameAppAliasInput!): RenameAppAliasPayload + requestAppTransfer(input: RequestAppTransferInput!): RequestAppTransferPayload + acceptAppTransferRequest(input: AcceptAppTransferRequestInput!): AcceptAppTransferRequestPayload + removeAppTransferRequest(input: RemoveAppTransferRequestInput!): RemoveAppTransferRequestPayload + createRepoForAppTemplate(input: CreateRepoForAppTemplateInput!): CreateRepoForAppTemplatePayload + registerDomain(input: RegisterDomainInput!): RegisterDomainPayload + upsertDNSRecord(input: UpsertDNSRecordInput!): UpsertDNSRecordPayload + deleteDnsRecord(input: DeleteDNSRecordInput!): DeleteDNSRecordPayload + upsertDomainFromZoneFile(input: UpsertDomainFromZoneFileInput!): UpsertDomainFromZoneFilePayload + deleteDomain(input: DeleteDomainInput!): DeleteDomainPayload + tokenAuth(input: ObtainJSONWebTokenInput!): ObtainJSONWebTokenPayload + generateDeployToken(input: GenerateDeployTokenInput!): GenerateDeployTokenPayload + verifyAccessToken(token: String): Verify + refreshAccessToken(refreshToken: String): Refresh + revokeAccessToken(refreshToken: String): Revoke + registerUser(input: RegisterUserInput!): RegisterUserPayload + socialAuth(input: SocialAuthJWTInput!): SocialAuthJWTPayload + validateUserEmail(input: ValidateUserEmailInput!): ValidateUserEmailPayload + requestPasswordReset(input: RequestPasswordResetInput!): RequestPasswordResetPayload + requestValidationEmail(input: RequestValidationEmailInput!): RequestValidationEmailPayload + changeUserPassword(input: ChangeUserPasswordInput!): ChangeUserPasswordPayload + changeUserUsername(input: ChangeUserUsernameInput!): ChangeUserUsernamePayload + changeUserEmail(input: ChangeUserEmailInput!): ChangeUserEmailPayload + updateUserInfo(input: UpdateUserInfoInput!): UpdateUserInfoPayload + validateUserPassword(input: ValidateUserPasswordInput!): ValidateUserPasswordPayload + generateApiToken(input: GenerateAPITokenInput!): GenerateAPITokenPayload + revokeApiToken(input: RevokeAPITokenInput!): RevokeAPITokenPayload + checkUserExists(input: CheckUserExistsInput!): CheckUserExistsPayload + readNotification(input: ReadNotificationInput!): ReadNotificationPayload + seePendingNotifications(input: SeePendingNotificationsInput!): SeePendingNotificationsPayload + newNonce(input: NewNonceInput!): NewNoncePayload + validateNonce(input: ValidateNonceInput!): ValidateNoncePayload + mfa2totpGetToken(input: MFATOTPGetTokenInput!): MFATOTPTokenType + mfa2totpVerify(input: MFATOTPVerifyInput!): MFATOTPVerifyPayload + mfa2totpAuth(input: MFATOTPAuthInput!): MFAAuthResponse + mfa2RecoveryGetToken(input: MFAGenerateRecoveryTokenInput!): MFARecoveryCodes + mfa2RecoveryAuth(input: MFARecoveryAuthInput!): MFAAuthResponse + mfa2EmailAuth(input: MFAEmailAuthInput!): MFAAuthResponse + mfa2EmailGetToken(input: MFAGenerateEmailOTPInput!): MFAEmailGenerationResponse + publishPublicKey(input: PublishPublicKeyInput!): PublishPublicKeyPayload + publishPackage(input: PublishPackageInput!): PublishPackagePayload + updatePackage(input: UpdatePackageInput!): UpdatePackagePayload + likePackage(input: LikePackageInput!): LikePackagePayload + unlikePackage(input: UnlikePackageInput!): UnlikePackagePayload + watchPackage(input: WatchPackageInput!): WatchPackagePayload + unwatchPackage(input: UnwatchPackageInput!): UnwatchPackagePayload + archivePackage(input: ArchivePackageInput!): ArchivePackagePayload + renamePackage(input: RenamePackageInput!): RenamePackagePayload + changePackageVersionArchivedStatus(input: ChangePackageVersionArchivedStatusInput!): ChangePackageVersionArchivedStatusPayload + createNamespace(input: CreateNamespaceInput!): CreateNamespacePayload + updateNamespace(input: UpdateNamespaceInput!): UpdateNamespacePayload + deleteNamespace(input: DeleteNamespaceInput!): DeleteNamespacePayload + inviteNamespaceCollaborator(input: InviteNamespaceCollaboratorInput!): InviteNamespaceCollaboratorPayload + acceptNamespaceCollaboratorInvite(input: AcceptNamespaceCollaboratorInviteInput!): AcceptNamespaceCollaboratorInvitePayload + removeNamespaceCollaboratorInvite(input: RemoveNamespaceCollaboratorInviteInput!): RemoveNamespaceCollaboratorInvitePayload + removeNamespaceCollaborator(input: RemoveNamespaceCollaboratorInput!): RemoveNamespaceCollaboratorPayload + updateNamespaceCollaboratorRole(input: UpdateNamespaceCollaboratorRoleInput!): UpdateNamespaceCollaboratorRolePayload + updateNamespaceCollaboratorInviteRole(input: UpdateNamespaceCollaboratorInviteRoleInput!): UpdateNamespaceCollaboratorInviteRolePayload + invitePackageCollaborator(input: InvitePackageCollaboratorInput!): InvitePackageCollaboratorPayload + acceptPackageCollaboratorInvite(input: AcceptPackageCollaboratorInviteInput!): AcceptPackageCollaboratorInvitePayload + removePackageCollaboratorInvite(input: RemovePackageCollaboratorInviteInput!): RemovePackageCollaboratorInvitePayload + updatePackageCollaboratorRole(input: UpdatePackageCollaboratorRoleInput!): UpdatePackageCollaboratorRolePayload + updatePackageCollaboratorInviteRole(input: UpdatePackageCollaboratorInviteRoleInput!): UpdatePackageCollaboratorInviteRolePayload + removePackageCollaborator(input: RemovePackageCollaboratorInput!): RemovePackageCollaboratorPayload + requestPackageTransfer(input: RequestPackageTransferInput!): RequestPackageTransferPayload + acceptPackageTransferRequest(input: AcceptPackageTransferRequestInput!): AcceptPackageTransferRequestPayload + removePackageTransferRequest(input: RemovePackageTransferRequestInput!): RemovePackageTransferRequestPayload + generateBindingsForAllPackages(input: GenerateBindingsForAllPackagesInput!): GenerateBindingsForAllPackagesPayload + makePackagePublic(input: MakePackagePublicInput!): MakePackagePublicPayload +} + +"""Viewer accepts the latest ToS.""" +type AcceptTOSPayload { + TOS: TermsOfService! + clientMutationId: String +} + +input AcceptTOSInput { + clientMutationId: String +} + +type PublishDeployAppPayload { + deployAppVersion: DeployAppVersion! + clientMutationId: String +} + +input PublishDeployAppInput { + """The configuration of the app.""" + config: Configuration! + + """The name of the app.""" + name: ID + + """The owner of the app.""" + owner: ID + + """The description of the app.""" + description: String + + """If true, the new version will be set as the default version.""" + makeDefault: Boolean = true + + """ + If true, Publishing will fail if the source package does not have a valid webc. + """ + strict: Boolean = false + clientMutationId: String +} + +input Configuration { + deployment: AppV0 + yamlConfig: String +} + +input AppV0 { + kind: String = "wasmer.io/App.v0" + appId: ID + name: String! + description: String + package: String! +} + +type DeleteAppPayload { + success: Boolean! + clientMutationId: String +} + +input DeleteAppInput { + """App ID to delete.""" + id: ID! + clientMutationId: String +} + +"""Add current user to the waitlist.""" +type JoinWaitlistPayload { + waitlistMember: WaitlistMember! + clientMutationId: String +} + +input JoinWaitlistInput { + name: String! + clientMutationId: String +} + +"""Add stripe payment to the user""" +type AddPaymentPayload { + customerSecret: String! + clientMutationId: String +} + +input AddPaymentInput { + clientMutationId: String +} + +""" +Mutation to change the active version of a DeployApp to another DeployAppVersion. +""" +type MarkAppVersionAsActivePayload { + app: DeployApp! + clientMutationId: String +} + +input MarkAppVersionAsActiveInput { + """The ID of the DeployAppVersion to set as the new active version.""" + appVersion: ID! + clientMutationId: String +} + +"""Set a payment method as default for the user.""" +type SetDefaultPaymentMethodPayload { + success: Boolean! + billing: Billing! + clientMutationId: String +} + +input SetDefaultPaymentMethodInput { + paymentMethod: ID! + clientMutationId: String +} + +""" +Try to detach a payment method from customer. +Fails if trying to detach a default method, +or if it's the only payment method. +""" +type DetachPaymentMethodPayload { + success: Boolean! + billing: Billing! + clientMutationId: String +} + +input DetachPaymentMethodInput { + paymentMethod: ID! + clientMutationId: String +} + +type GenerateDeployConfigTokenPayload { + token: String! + config: String! + clientMutationId: String +} + +input GenerateDeployConfigTokenInput { + config: String! + clientMutationId: String +} + +type RenameAppPayload { + success: Boolean! + app: DeployApp! + clientMutationId: String +} + +input RenameAppInput { + """App ID to delete.""" + id: ID! + + """New name for the app.""" + name: String! + clientMutationId: String +} + +type RenameAppAliasPayload { + success: Boolean! + alias: AppAlias! + clientMutationId: String +} + +input RenameAppAliasInput { + """App alias ID to delete.""" + id: ID! + + """New name for the alias.""" + name: String! + clientMutationId: String +} + +type RequestAppTransferPayload { + appTransferRequest: AppTransferRequest + wasInstantlyTransferred: Boolean! + clientMutationId: String +} + +type AppTransferRequest implements Node { + """The ID of the object""" + id: ID! + requestedBy: User! + previousOwnerObjectId: Int! + newOwnerObjectId: Int! + app: DeployApp! + approvedBy: User + declinedBy: User + createdAt: DateTime! + expiresAt: DateTime! + closedAt: DateTime + previousOwner: Owner! + newOwner: Owner! +} + +input RequestAppTransferInput { + appId: ID! + newOwnerId: ID! + clientMutationId: String +} + +type AcceptAppTransferRequestPayload { + app: DeployApp! + appTransferRequest: AppTransferRequest! + clientMutationId: String +} + +input AcceptAppTransferRequestInput { + appTransferRequestId: ID! + clientMutationId: String +} + +type RemoveAppTransferRequestPayload { + app: DeployApp! + clientMutationId: String +} + +input RemoveAppTransferRequestInput { + appTransferRequestId: ID! + clientMutationId: String +} + +type CreateRepoForAppTemplatePayload { + success: Boolean! + repoId: ID! + clientMutationId: String +} + +input CreateRepoForAppTemplateInput { + templateId: ID! + name: String! + namespace: String! + private: Boolean = false + clientMutationId: String +} + +type RegisterDomainPayload { + success: Boolean! + domain: DNSDomain + clientMutationId: String +} + +input RegisterDomainInput { + name: String! + namespace: String + importRecords: Boolean = true + clientMutationId: String +} + +type UpsertDNSRecordPayload { + success: Boolean! + record: DNSRecord! + clientMutationId: String +} + +input UpsertDNSRecordInput { + kind: RecordKind! + domainId: String! + name: String! + value: String! + ttl: Int + recordId: String + mx: DNSMXExtraInput + clientMutationId: String +} + +enum RecordKind { + A + AAAA + CNAME + MX + NS + TXT + DNAME + PTR + SOA + SRV + CAA + SSHFP +} + +input DNSMXExtraInput { + preference: Int! +} + +type DeleteDNSRecordPayload { + success: Boolean! + clientMutationId: String +} + +input DeleteDNSRecordInput { + recordId: ID! + clientMutationId: String +} + +type UpsertDomainFromZoneFilePayload { + success: Boolean! + domain: DNSDomain! + clientMutationId: String +} + +input UpsertDomainFromZoneFileInput { + zoneFile: String! + deleteMissingRecords: Boolean + clientMutationId: String +} + +type DeleteDomainPayload { + success: Boolean! + clientMutationId: String +} + +input DeleteDomainInput { + domainId: ID! + clientMutationId: String +} + +type ObtainJSONWebTokenPayload { + payload: GenericScalar! + refreshExpiresIn: Int! + username: CaseInsensitiveString! + clientMutationId: String + token: String! + refreshToken: String! +} + +""" +The `GenericScalar` scalar type represents a generic +GraphQL scalar value that could be: +String, Boolean, Int, Float, List or Object. +""" +scalar GenericScalar + +""" +The `CaseInsensitiveString` scalar type represents textual data, represented as UTF-8 +character sequences. The String type is most often used by GraphQL to +represent free-form human-readable text. +""" +scalar CaseInsensitiveString + +input ObtainJSONWebTokenInput { + clientMutationId: String + username: String! + password: String! +} + +type GenerateDeployTokenPayload { + token: String! + deployConfigVersion: DeployAppVersion! + clientMutationId: String +} + +input GenerateDeployTokenInput { + deployConfigVersionId: String! + clientMutationId: String +} + +type Verify { + payload: GenericScalar! +} + +type Refresh { + payload: GenericScalar! + refreshExpiresIn: Int! + token: String! + refreshToken: String! +} + +type Revoke { + revoked: Int! +} + +type RegisterUserPayload { + token: String + clientMutationId: String +} + +input RegisterUserInput { + fullName: String! + email: String! + username: CaseInsensitiveString! + password: String! + acceptedTos: Boolean + clientMutationId: String +} + +type SocialAuthJWTPayload { + social: SocialAuth + token: String + clientMutationId: String +} + +input SocialAuthJWTInput { + provider: String! + accessToken: String! + register: Boolean = false + clientMutationId: String +} + +type ValidateUserEmailPayload { + user: User + clientMutationId: String +} + +input ValidateUserEmailInput { + """The user id""" + userId: ID + challenge: String! + clientMutationId: String +} + +type RequestPasswordResetPayload { + email: String! + errors: [ErrorType] + clientMutationId: String +} + +type ErrorType { + field: String! + messages: [String!]! +} + +input RequestPasswordResetInput { + email: String! + clientMutationId: String +} + +type RequestValidationEmailPayload { + user: User + success: Boolean! + clientMutationId: String +} + +input RequestValidationEmailInput { + """The user id""" + userId: ID + clientMutationId: String +} + +type ChangeUserPasswordPayload { + token: String + clientMutationId: String +} + +input ChangeUserPasswordInput { + """ + The token associated to change the password. If not existing it will use the request user by default + """ + token: String + oldPassword: String + password: String! + clientMutationId: String +} + +type ChangeUserUsernamePayload { + user: User + token: String + clientMutationId: String +} + +input ChangeUserUsernameInput { + """The new user username""" + username: CaseInsensitiveString! + clientMutationId: String +} + +type ChangeUserEmailPayload { + user: User! + clientMutationId: String +} + +input ChangeUserEmailInput { + newEmail: String! + clientMutationId: String +} + +type UpdateUserInfoPayload { + user: User + clientMutationId: String +} + +input UpdateUserInfoInput { + """The user id""" + userId: ID + + """The user full name""" + fullName: String + + """The user bio""" + bio: String + + """The user avatar""" + avatar: String + + """ + The user Twitter (it can be the url, or the handle with or without the @) + """ + twitter: String + + """ + The user Github (it can be the url, or the handle with or without the @) + """ + github: String + + """The user website (it must be a valid url)""" + websiteUrl: String + + """The user location""" + location: String + clientMutationId: String +} + +type ValidateUserPasswordPayload { + success: Boolean + clientMutationId: String +} + +input ValidateUserPasswordInput { + password: String! + clientMutationId: String +} + +type GenerateAPITokenPayload { + token: APIToken + tokenRaw: String + user: User + clientMutationId: String +} + +input GenerateAPITokenInput { + identifier: String + clientMutationId: String +} + +type RevokeAPITokenPayload { + token: APIToken + success: Boolean + clientMutationId: String +} + +input RevokeAPITokenInput { + """The API token ID""" + tokenId: ID! + clientMutationId: String +} + +type CheckUserExistsPayload { + exists: Boolean! + + """The user is only returned if the user input was the username""" + user: User + clientMutationId: String +} + +input CheckUserExistsInput { + """The user""" + user: String! + clientMutationId: String +} + +type ReadNotificationPayload { + notification: UserNotification + clientMutationId: String +} + +input ReadNotificationInput { + notificationId: ID! + clientMutationId: String +} + +type SeePendingNotificationsPayload { + success: Boolean + clientMutationId: String +} + +input SeePendingNotificationsInput { + clientMutationId: String +} + +type NewNoncePayload { + nonce: Nonce! + clientMutationId: String +} + +input NewNonceInput { + name: String! + callbackUrl: String! + clientMutationId: String +} + +type ValidateNoncePayload { + nonce: Nonce! + clientMutationId: String +} + +input ValidateNonceInput { + id: ID! + secret: String! + clientMutationId: String +} + +type MFATOTPTokenType { + qr: String + secretKey: String +} + +input MFATOTPGetTokenInput { + clientMutationId: String +} + +type MFATOTPVerifyPayload { + status: MFATOTPVerifyStatus + clientMutationId: String +} + +enum MFATOTPVerifyStatus { + SUCCESS + RECOVERY +} + +input MFATOTPVerifyInput { + answer: String! + secretKey: String! + clientMutationId: String +} + +"""Response object for MFAAuth mutation.""" +type MFAAuthResponse { + success: Boolean! + token: String + refreshToken: String + username: String + refreshTokenExpiresIn: Int +} + +input MFATOTPAuthInput { + username: String! + otp: String! + clientMutationId: String +} + +type MFARecoveryCodes { + codes: [String]! +} + +input MFAGenerateRecoveryTokenInput { + clientMutationId: String +} + +input MFARecoveryAuthInput { + username: String! + otp: String! + clientMutationId: String +} + +input MFAEmailAuthInput { + username: String! + otp: String! + clientMutationId: String +} + +type MFAEmailGenerationResponse { + success: Boolean! +} + +input MFAGenerateEmailOTPInput { + clientMutationId: String +} + +type PublishPublicKeyPayload { + success: Boolean! + publicKey: PublicKey! + clientMutationId: String +} + +input PublishPublicKeyInput { + keyId: String! + key: String! + verifyingSignatureId: String + clientMutationId: String +} + +type PublishPackagePayload { + success: Boolean! + packageVersion: PackageVersion! + clientMutationId: String +} + +input PublishPackageInput { + name: String! + version: String! + description: String! + manifest: String! + license: String + licenseFile: String + readme: String + repository: String + homepage: String + file: String + signedUrl: String + signature: InputSignature + + """The package icon""" + icon: String + + """Whether the package is private""" + private: Boolean = false + + """The upload format of the package""" + uploadFormat: UploadFormat = targz + + """Whether to wait for webc generation to finish""" + wait: Boolean = false + clientMutationId: String +} + +input InputSignature { + publicKeyKeyId: String! + data: String! +} + +enum UploadFormat { + targz + webcv2 +} + +type UpdatePackagePayload { + package: Package! + clientMutationId: String +} + +input UpdatePackageInput { + packageId: ID! + + """The package icon""" + icon: String + clientMutationId: String +} + +type LikePackagePayload { + package: Package! + clientMutationId: String +} + +input LikePackageInput { + packageId: ID! + clientMutationId: String +} + +type UnlikePackagePayload { + package: Package! + clientMutationId: String +} + +input UnlikePackageInput { + packageId: ID! + clientMutationId: String +} + +type WatchPackagePayload { + package: Package! + clientMutationId: String +} + +input WatchPackageInput { + packageId: ID! + clientMutationId: String +} + +type UnwatchPackagePayload { + package: Package! + clientMutationId: String +} + +input UnwatchPackageInput { + packageId: ID! + clientMutationId: String +} + +type ArchivePackagePayload { + package: Package! + clientMutationId: String +} + +input ArchivePackageInput { + packageId: ID! + clientMutationId: String +} + +type RenamePackagePayload { + package: Package! + clientMutationId: String +} + +input RenamePackageInput { + packageId: ID! + newName: String! + clientMutationId: String +} + +type ChangePackageVersionArchivedStatusPayload { + packageVersion: PackageVersion! + clientMutationId: String +} + +input ChangePackageVersionArchivedStatusInput { + packageVersionId: ID! + isArchived: Boolean + clientMutationId: String +} + +type CreateNamespacePayload { + namespace: Namespace! + user: User! + clientMutationId: String +} + +input CreateNamespaceInput { + name: String! + + """The namespace display name""" + displayName: String + + """The namespace description""" + description: String + + """The namespace avatar""" + avatar: String + clientMutationId: String +} + +type UpdateNamespacePayload { + namespace: Namespace! + clientMutationId: String +} + +input UpdateNamespaceInput { + namespaceId: ID! + + """The namespace slug name""" + name: String + + """The namespace display name""" + displayName: String + + """The namespace description""" + description: String + + """The namespace avatar""" + avatar: String + + """ + The user Twitter (it can be the url, or the handle with or without the @) + """ + twitter: String + + """ + The user Github (it can be the url, or the handle with or without the @) + """ + github: String + + """The user website (it must be a valid url)""" + websiteUrl: String + clientMutationId: String +} + +type DeleteNamespacePayload { + success: Boolean! + clientMutationId: String +} + +input DeleteNamespaceInput { + namespaceId: ID! + clientMutationId: String +} + +type InviteNamespaceCollaboratorPayload { + invite: NamespaceCollaboratorInvite! + namespace: Namespace! + clientMutationId: String +} + +input InviteNamespaceCollaboratorInput { + namespaceId: ID! + role: GrapheneRole! + username: String + email: String + clientMutationId: String +} + +type AcceptNamespaceCollaboratorInvitePayload { + namespaceCollaboratorInvite: NamespaceCollaboratorInvite! + clientMutationId: String +} + +input AcceptNamespaceCollaboratorInviteInput { + inviteId: ID! + clientMutationId: String +} + +type RemoveNamespaceCollaboratorInvitePayload { + namespace: Namespace! + clientMutationId: String +} + +input RemoveNamespaceCollaboratorInviteInput { + inviteId: ID! + clientMutationId: String +} + +type RemoveNamespaceCollaboratorPayload { + namespace: Namespace! + clientMutationId: String +} + +input RemoveNamespaceCollaboratorInput { + namespaceCollaboratorId: ID! + clientMutationId: String +} + +type UpdateNamespaceCollaboratorRolePayload { + collaborator: NamespaceCollaborator! + clientMutationId: String +} + +input UpdateNamespaceCollaboratorRoleInput { + namespaceCollaboratorId: ID! + role: GrapheneRole! + clientMutationId: String +} + +type UpdateNamespaceCollaboratorInviteRolePayload { + collaboratorInvite: NamespaceCollaboratorInvite! + clientMutationId: String +} + +input UpdateNamespaceCollaboratorInviteRoleInput { + namespaceCollaboratorInviteId: ID! + role: GrapheneRole! + clientMutationId: String +} + +type InvitePackageCollaboratorPayload { + invite: PackageCollaboratorInvite! + package: Package! + clientMutationId: String +} + +input InvitePackageCollaboratorInput { + packageName: String! + role: GrapheneRole! + username: String + email: String + clientMutationId: String +} + +type AcceptPackageCollaboratorInvitePayload { + packageCollaboratorInvite: PackageCollaboratorInvite! + clientMutationId: String +} + +input AcceptPackageCollaboratorInviteInput { + inviteId: ID! + clientMutationId: String +} + +type RemovePackageCollaboratorInvitePayload { + package: Package! + clientMutationId: String +} + +input RemovePackageCollaboratorInviteInput { + inviteId: ID! + clientMutationId: String +} + +type UpdatePackageCollaboratorRolePayload { + collaborator: PackageCollaborator! + clientMutationId: String +} + +input UpdatePackageCollaboratorRoleInput { + packageCollaboratorId: ID! + role: GrapheneRole! + clientMutationId: String +} + +type UpdatePackageCollaboratorInviteRolePayload { + collaboratorInvite: PackageCollaboratorInvite! + clientMutationId: String +} + +input UpdatePackageCollaboratorInviteRoleInput { + packageCollaboratorInviteId: ID! + role: GrapheneRole! + clientMutationId: String +} + +type RemovePackageCollaboratorPayload { + package: Package! + clientMutationId: String +} + +input RemovePackageCollaboratorInput { + packageCollaboratorId: ID! + clientMutationId: String +} + +type RequestPackageTransferPayload { + package: Package! + wasInstantlyTransferred: Boolean! + packageTransferRequest: PackageTransferRequest + clientMutationId: String +} + +input RequestPackageTransferInput { + packageId: ID! + newOwnerId: ID! + clientMutationId: String +} + +type AcceptPackageTransferRequestPayload { + package: Package! + packageTransferRequest: PackageTransferRequest! + clientMutationId: String +} + +input AcceptPackageTransferRequestInput { + packageTransferRequestId: ID! + clientMutationId: String +} + +type RemovePackageTransferRequestPayload { + package: Package! + clientMutationId: String +} + +input RemovePackageTransferRequestInput { + packageTransferRequestId: ID! + clientMutationId: String +} + +type GenerateBindingsForAllPackagesPayload { + message: String! + clientMutationId: String +} + +input GenerateBindingsForAllPackagesInput { + bindingsGeneratorId: ID + bindingsGeneratorCommand: String + clientMutationId: String +} + +type MakePackagePublicPayload { + package: Package! + clientMutationId: String +} + +input MakePackagePublicInput { + """The ID of the package to make public""" + id: ID! + clientMutationId: String +} + +type Subscription { + streamLogs( + appVersionId: ID! + + """ + Get logs starting from this timestamp. Takes ISO timestamp in UTC timezone. + """ + startingFromISO: DateTime + + """ + Fetch logs until this timestamp. Takes ISO timestamp in UTC timezone. If specified, the subscription will at this time. + """ + untilISO: DateTime + + """Filter logs by stream""" + streams: [LogStream] + + """Filter logs by instance ids""" + instanceIds: [String] + + """Search logs for this term""" + searchTerm: String + ): Log! + waitOnRepoCreation(repoId: ID!): Boolean! + appIsPublishedFromRepo(repoId: ID!): DeployAppVersion! + packageVersionCreated(publishedBy: ID, ownerId: ID): PackageVersion! + + """Subscribe to package version ready""" + packageVersionReady(packageVersionId: ID!): PackageVersionReadyResponse! + userNotificationCreated(userId: ID!): UserNotificationCreated! +} + +type PackageVersionReadyResponse { + state: PackageVersionState! + packageVersion: PackageVersion! + success: Boolean! +} + +enum PackageVersionState { + WEBC_GENERATED + BINDINGS_GENERATED + NATIVE_EXES_GENERATED +} + +type UserNotificationCreated { + notification: UserNotification + notificationDeletedId: ID +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/client.rs b/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/client.rs new file mode 100644 index 0000000..66270ed --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/client.rs @@ -0,0 +1,209 @@ +use std::time::Duration; + +use anyhow::{bail, Context as _}; +use cynic::{http::CynicReqwestError, GraphQlResponse, Operation}; +use url::Url; + +use crate::GraphQLApiFailure; + +/// API client for the Wasmer API. +/// +/// Use the queries in [`crate::queries`] to interact with the API. +#[derive(Clone, Debug)] +pub struct WasmerClient { + auth_token: Option, + graphql_endpoint: Url, + + pub(crate) client: reqwest::Client, + pub(crate) user_agent: reqwest::header::HeaderValue, + #[allow(unused)] + extra_debugging: bool, +} + +impl WasmerClient { + pub fn graphql_endpoint(&self) -> &Url { + &self.graphql_endpoint + } + + pub fn auth_token(&self) -> Option<&str> { + self.auth_token.as_deref() + } + + fn parse_user_agent(user_agent: &str) -> Result { + if user_agent.is_empty() { + bail!("user agent must not be empty"); + } + user_agent + .parse() + .with_context(|| format!("invalid user agent: '{}'", user_agent)) + } + + pub fn new_with_client( + client: reqwest::Client, + graphql_endpoint: Url, + user_agent: &str, + ) -> Result { + Ok(Self { + client, + auth_token: None, + user_agent: Self::parse_user_agent(user_agent)?, + graphql_endpoint, + extra_debugging: false, + }) + } + + pub fn new(graphql_endpoint: Url, user_agent: &str) -> Result { + let client = reqwest::Client::builder() + .connect_timeout(Duration::from_secs(10)) + .timeout(Duration::from_secs(90)) + .build() + .context("could not construct http client")?; + Self::new_with_client(client, graphql_endpoint, user_agent) + } + + pub fn with_auth_token(mut self, auth_token: String) -> Self { + self.auth_token = Some(auth_token); + self + } + + pub(crate) async fn run_graphql_raw( + &self, + operation: Operation, + ) -> Result, anyhow::Error> + where + Vars: serde::Serialize + std::fmt::Debug, + ResponseData: serde::de::DeserializeOwned + std::fmt::Debug + 'static, + { + let req = self + .client + .post(self.graphql_endpoint.as_str()) + .header(reqwest::header::USER_AGENT, &self.user_agent); + let req = if let Some(token) = &self.auth_token { + req.bearer_auth(token) + } else { + req + }; + + if self.extra_debugging { + tracing::trace!( + query=%operation.query, + vars=?operation.variables, + "running GraphQL query" + ); + } + let query = operation.query.clone(); + + tracing::trace!( + endpoint=%self.graphql_endpoint, + query=serde_json::to_string(&operation).unwrap_or_default(), + "sending graphql query" + ); + + let res = req.json(&operation).send().await; + + let res = match res { + Ok(response) => { + let status = response.status(); + if !status.is_success() { + let body_string = match response.text().await { + Ok(b) => b, + Err(err) => { + tracing::error!("could not load response body: {err}"); + "".to_string() + } + }; + + match serde_json::from_str::>(&body_string) { + Ok(response) => Ok(response), + Err(_) => Err(CynicReqwestError::ErrorResponse(status, body_string)), + } + } else { + let body = response.bytes().await?; + + let jd = &mut serde_json::Deserializer::from_slice(&body); + let data: Result, _> = + serde_path_to_error::deserialize(jd).map_err(|err| { + let body_txt = String::from_utf8_lossy(&body); + CynicReqwestError::ErrorResponse( + reqwest::StatusCode::INTERNAL_SERVER_ERROR, + format!("Could not decode JSON response: {err} -- '{body_txt}'"), + ) + }); + + data + } + } + Err(e) => Err(CynicReqwestError::ReqwestError(e)), + }; + let res = match res { + Ok(res) => { + tracing::trace!(?res, "GraphQL query succeeded"); + res + } + Err(err) => { + tracing::error!(?err, "GraphQL query failed"); + return Err(err.into()); + } + }; + + if let Some(errors) = &res.errors { + if !errors.is_empty() { + tracing::warn!( + ?errors, + data=?res.data, + %query, + endpoint=%self.graphql_endpoint, + "GraphQL query succeeded, but returned errors", + ); + } + } + + Ok(res) + } + + pub(crate) async fn run_graphql( + &self, + operation: Operation, + ) -> Result + where + Vars: serde::Serialize + std::fmt::Debug, + ResponseData: serde::de::DeserializeOwned + std::fmt::Debug + 'static, + { + let res = self.run_graphql_raw(operation).await?; + + if let Some(data) = res.data { + Ok(data) + } else if let Some(errs) = res.errors { + let errs = GraphQLApiFailure { errors: errs }; + Err(errs).context("GraphQL query failed") + } else { + Err(anyhow::anyhow!("Query did not return any data")) + } + } + + /// Run a GraphQL query, but fail (return an Error) if any error is returned + /// in the response. + pub(crate) async fn run_graphql_strict( + &self, + operation: Operation, + ) -> Result + where + Vars: serde::Serialize + std::fmt::Debug, + ResponseData: serde::de::DeserializeOwned + std::fmt::Debug + 'static, + { + let res = self.run_graphql_raw(operation).await?; + + if let Some(errs) = res.errors { + if !errs.is_empty() { + let errs = GraphQLApiFailure { errors: errs }; + return Err(errs).context("GraphQL query failed"); + } + } + + if let Some(data) = res.data { + Ok(data) + } else { + Err(anyhow::anyhow!("Query did not return any data")) + } + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/error.rs b/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/error.rs new file mode 100644 index 0000000..76f5fa9 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/error.rs @@ -0,0 +1,36 @@ +/// One or multiple errors returned by the GraphQL API. +// Mainly exists to implement [`std::error::Error`]. +#[derive(Debug)] +pub struct GraphQLApiFailure { + pub errors: Vec, +} + +impl GraphQLApiFailure { + pub fn from_errors( + msg: impl Into, + errors: Option>, + ) -> anyhow::Error { + let msg = msg.into(); + if let Some(errs) = errors { + if !errs.is_empty() { + let err = GraphQLApiFailure { errors: errs }; + return anyhow::Error::new(err).context(msg); + } + } + anyhow::anyhow!("{msg} - query did not return any data") + } +} + +impl std::fmt::Display for GraphQLApiFailure { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let errs = self + .errors + .iter() + .map(|err| err.to_string()) + .collect::>() + .join(", "); + write!(f, "GraphQL API failure: {}", errs) + } +} + +impl std::error::Error for GraphQLApiFailure {} diff --git a/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/global_id.rs b/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/global_id.rs new file mode 100644 index 0000000..e7481cd --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/global_id.rs @@ -0,0 +1,483 @@ +//! [`GlobalId`]s are used by the backend to identify a specific object. +//! +//! This module provides a parser/encoder and related type defintions +//! for global ids. + +use std::fmt::Display; + +#[repr(u16)] +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum NodeKind { + User = 0, + SocialAuth = 1, + Namespace = 2, + Package = 3, + PackageVersion = 4, + PackageCollaborator = 5, + PackageCollaboratorInvite = 6, + NativeExecutable = 7, + PackageVersionNPMBinding = 8, + PackageVersionPythonBinding = 9, + PackageTransferRequest = 10, + Interface = 11, + InterfaceVersion = 12, + PublicKey = 13, + UserNotification = 14, + ActivityEvent = 15, + NamespaceCollaborator = 16, + NamespaceCollaboratorInvite = 17, + BindingsGenerator = 18, + DeployConfigVersion = 19, + DeployConfigInfo = 20, + DeployApp = 21, + DeployAppVersion = 22, + Waitlist = 23, + WaitlistMember = 24, + CardPaymentMethod = 25, + PaymentIntent = 26, + AppAlias = 27, + Nonce = 28, + TermsOfService = 29, +} + +impl NodeKind { + pub fn from_num(x: u64) -> Option { + match x { + 0 => Some(Self::User), + 1 => Some(Self::SocialAuth), + 2 => Some(Self::Namespace), + 3 => Some(Self::Package), + 4 => Some(Self::PackageVersion), + 5 => Some(Self::PackageCollaborator), + 6 => Some(Self::PackageCollaboratorInvite), + 7 => Some(Self::NativeExecutable), + 8 => Some(Self::PackageVersionNPMBinding), + 9 => Some(Self::PackageVersionPythonBinding), + 10 => Some(Self::PackageTransferRequest), + 11 => Some(Self::Interface), + 12 => Some(Self::InterfaceVersion), + 13 => Some(Self::PublicKey), + 14 => Some(Self::UserNotification), + 15 => Some(Self::ActivityEvent), + 16 => Some(Self::NamespaceCollaborator), + 17 => Some(Self::NamespaceCollaboratorInvite), + 18 => Some(Self::BindingsGenerator), + 19 => Some(Self::DeployConfigVersion), + 20 => Some(Self::DeployConfigInfo), + 21 => Some(Self::DeployApp), + 22 => Some(Self::DeployAppVersion), + 23 => Some(Self::Waitlist), + 24 => Some(Self::WaitlistMember), + 25 => Some(Self::CardPaymentMethod), + 26 => Some(Self::PaymentIntent), + 27 => Some(Self::AppAlias), + 28 => Some(Self::Nonce), + 29 => Some(Self::TermsOfService), + _ => None, + } + } + + pub fn parse_prefix(s: &str) -> Option { + match s { + "u" => Some(Self::User), + "su" => Some(Self::SocialAuth), + "ns" => Some(Self::Namespace), + "pk" => Some(Self::Package), + "pkv" => Some(Self::PackageVersion), + "pc" => Some(Self::PackageCollaborator), + "pci" => Some(Self::PackageCollaboratorInvite), + "ne" => Some(Self::NativeExecutable), + "pkvbjs" => Some(Self::PackageVersionNPMBinding), + "pkvbpy" => Some(Self::PackageVersionPythonBinding), + "pt" => Some(Self::PackageTransferRequest), + "in" => Some(Self::Interface), + "inv" => Some(Self::InterfaceVersion), + "pub" => Some(Self::PublicKey), + "nt" => Some(Self::UserNotification), + "ae" => Some(Self::ActivityEvent), + "nsc" => Some(Self::NamespaceCollaborator), + "nsci" => Some(Self::NamespaceCollaboratorInvite), + "bg" => Some(Self::BindingsGenerator), + "dcv" => Some(Self::DeployConfigVersion), + "dci" => Some(Self::DeployConfigInfo), + "da" => Some(Self::DeployApp), + "dav" => Some(Self::DeployAppVersion), + "wl" => Some(Self::Waitlist), + "wlm" => Some(Self::WaitlistMember), + "cpm" => Some(Self::CardPaymentMethod), + "pi" => Some(Self::PaymentIntent), + "daa" => Some(Self::AppAlias), + "nnc" => Some(Self::Nonce), + "tos" => Some(Self::TermsOfService), + _ => None, + } + } + + fn as_prefix(&self) -> &'static str { + match self { + Self::User => "u", + Self::SocialAuth => "su", + Self::Namespace => "ns", + Self::Package => "pk", + Self::PackageVersion => "pkv", + Self::PackageCollaborator => "pc", + Self::PackageCollaboratorInvite => "pci", + Self::NativeExecutable => "ne", + Self::PackageVersionNPMBinding => "pkvbjs", + Self::PackageVersionPythonBinding => "pkvbpy", + Self::PackageTransferRequest => "pt", + Self::Interface => "in", + Self::InterfaceVersion => "inv", + Self::PublicKey => "pub", + Self::UserNotification => "nt", + Self::ActivityEvent => "ae", + Self::NamespaceCollaborator => "nsc", + Self::NamespaceCollaboratorInvite => "nsci", + Self::BindingsGenerator => "bg", + Self::DeployConfigVersion => "dcv", + Self::DeployConfigInfo => "dci", + Self::DeployApp => "da", + Self::DeployAppVersion => "dav", + Self::Waitlist => "wl", + Self::WaitlistMember => "wlm", + Self::CardPaymentMethod => "cpm", + Self::PaymentIntent => "pi", + Self::AppAlias => "daa", + Self::Nonce => "nnc", + Self::TermsOfService => "tos", + } + } +} + +impl Display for NodeKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let name = match self { + Self::User => "User", + Self::SocialAuth => "SocialAuth", + Self::Namespace => "Namespace", + Self::Package => "Package", + Self::PackageVersion => "PackageVersion", + Self::PackageCollaborator => "PackageCollaborator", + Self::PackageCollaboratorInvite => "PackageCollaboratorInvite", + Self::NativeExecutable => "NativeExecutable", + Self::PackageVersionNPMBinding => "PackageVersionNPMBinding", + Self::PackageVersionPythonBinding => "PackageVersionPythonBinding", + Self::PackageTransferRequest => "PackageTransferRequest", + Self::Interface => "Interface", + Self::InterfaceVersion => "InterfaceVersion", + Self::PublicKey => "PublicKey", + Self::UserNotification => "UserNotification", + Self::ActivityEvent => "ActivityEvent", + Self::NamespaceCollaborator => "NamespaceCollaborator", + Self::NamespaceCollaboratorInvite => "NamespaceCollaboratorInvite", + Self::BindingsGenerator => "BindingsGenerator", + Self::DeployConfigVersion => "DeployConfigVersion", + Self::DeployConfigInfo => "DeployConfigInfo", + Self::DeployApp => "DeployApp", + Self::DeployAppVersion => "DeployAppVersion", + Self::Waitlist => "Waitlist", + Self::WaitlistMember => "WaitlistMember", + Self::CardPaymentMethod => "CardPaymentMethod", + Self::PaymentIntent => "PaymentIntent", + Self::AppAlias => "AppAlias", + Self::Nonce => "Nonce", + Self::TermsOfService => "TermsOfService", + }; + write!(f, "{name}") + } +} + +/// Global id of backend nodes. +/// +/// IDs are encoded using the "hashid" scheme, which uses a given alphabet and +/// a salt to encode u64 numbers into a string hash. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct GlobalId { + /// The node type of the ID. + kind: NodeKind, + /// The database ID of the node. + database_id: u64, +} + +impl GlobalId { + /// Salt used by the backend to encode hashes. + const SALT: &'static str = "wasmer salt hashid"; + /// Minimum length of the encoded hashes. + const MIN_LENGTH: usize = 12; + + /// Hash alphabet used for the prefix id variant. + const ALPHABET_PREFIXED: &'static str = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + + /// Hash alphabet used for the non-prefixed id variant. + const ALPHABET_URL: &'static str = "abcdefghijklmnopqrstuvwxyz0123456789"; + + pub fn new(kind: NodeKind, database_id: u64) -> Self { + Self { kind, database_id } + } + + fn build_harsh(alphabet: &str, salt: &[u8]) -> harsh::Harsh { + harsh::HarshBuilder::new() + .alphabet(alphabet.as_bytes()) + .salt(salt) + .length(GlobalId::MIN_LENGTH) + .build() + .unwrap() + } + + fn build_harsh_prefixed() -> harsh::Harsh { + Self::build_harsh(Self::ALPHABET_PREFIXED, Self::SALT.as_bytes()) + } + + fn build_harsh_url() -> harsh::Harsh { + Self::build_harsh(Self::ALPHABET_URL, Self::SALT.as_bytes()) + } + + pub fn kind(&self) -> NodeKind { + self.kind + } + + pub fn database_id(&self) -> u64 { + self.database_id + } + + /// Encode a prefixed global id. + pub fn encode_prefixed(&self) -> String { + let hash = Self::build_harsh_prefixed().encode(&[ + // scope + 1, + // version + 2, + self.kind as u64, + self.database_id, + ]); + + format!("{}_{}", self.kind.as_prefix(), hash) + } + + fn parse_values(values: &[u64]) -> Result { + let scope = values.first().cloned().ok_or(ErrorKind::MissingScope)?; + + if scope != 1 { + return Err(ErrorKind::UnknownScope(scope)); + } + + let version = values.get(1).cloned().ok_or(ErrorKind::MissingVersion)?; + if version != 2 { + return Err(ErrorKind::UnknownVersion(version)); + } + + let ty_raw = values.get(2).cloned().ok_or(ErrorKind::MissingNodeType)?; + let ty_parsed = NodeKind::from_num(ty_raw).ok_or(ErrorKind::UnknownNodeType(ty_raw))?; + + let db_id = values.get(3).cloned().ok_or(ErrorKind::MissingDatabaseId)?; + + Ok(Self { + kind: ty_parsed, + database_id: db_id, + }) + } + + /// Parse a prefixed global id. + pub fn parse_prefixed(hash: &str) -> Result { + let (prefix, value) = hash + .split_once('_') + .ok_or_else(|| GlobalIdParseError::new(hash, ErrorKind::MissingPrefix))?; + + if prefix.is_empty() { + return Err(GlobalIdParseError::new(hash, ErrorKind::MissingPrefix)); + } + + let ty_prefix = NodeKind::parse_prefix(prefix).ok_or_else(|| { + GlobalIdParseError::new(hash, ErrorKind::UnknownPrefix(prefix.to_string())) + })?; + + let values = Self::build_harsh_prefixed() + .decode(value) + .map_err(|err| GlobalIdParseError::new(hash, ErrorKind::Decode(err.to_string())))?; + + let s = Self::parse_values(&values).map_err(|kind| GlobalIdParseError::new(hash, kind))?; + + if ty_prefix != s.kind { + return Err(GlobalIdParseError::new(hash, ErrorKind::PrefixTypeMismatch)); + } + + Ok(s) + } + + /// Encode a non-prefixed global id. + /// + /// Note: URL ids use a different alphabet than prefixed ids. + pub fn encode_url(&self) -> String { + Self::build_harsh_url().encode(&[ + // scope + 1, + // version + 2, + self.kind as u64, + self.database_id, + ]) + } + + /// Parse a non-prefixed URL global id variant. + /// + /// Note: URL ids use a different alphabet than prefixed ids. + pub fn parse_url(hash: &str) -> Result { + let values = Self::build_harsh_url() + .decode(hash) + .map_err(|err| GlobalIdParseError::new(hash, ErrorKind::Decode(err.to_string())))?; + + Self::parse_values(&values).map_err(|kind| GlobalIdParseError::new(hash, kind)) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GlobalIdParseError { + id: String, + kind: ErrorKind, +} + +impl GlobalIdParseError { + fn new(id: impl Into, kind: ErrorKind) -> Self { + Self { + id: id.into(), + kind, + } + } +} + +/// Error type for parsing of [`GlobalId`]s. +// Note: kept private on purpose, not useful to export. +#[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] +enum ErrorKind { + MissingPrefix, + UnknownPrefix(String), + PrefixTypeMismatch, + MissingScope, + UnknownScope(u64), + MissingVersion, + UnknownVersion(u64), + MissingNodeType, + UnknownNodeType(u64), + MissingDatabaseId, + Decode(String), +} + +impl std::fmt::Display for GlobalIdParseError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "could not parse global id '{}': ", self.id)?; + + match &self.kind { + ErrorKind::UnknownPrefix(p) => { + write!(f, "unknown type prefix '{}'", p) + } + ErrorKind::Decode(s) => { + write!(f, "decode error: {}", s) + } + ErrorKind::MissingScope => { + write!(f, "missing scope value") + } + ErrorKind::UnknownScope(x) => { + write!(f, "unknown scope value {}", x) + } + ErrorKind::MissingVersion => { + write!(f, "missing version value") + } + ErrorKind::UnknownVersion(v) => { + write!(f, "unknown version value {}", v) + } + ErrorKind::UnknownNodeType(t) => { + write!(f, "unknown node type '{}'", t) + } + ErrorKind::MissingPrefix => write!(f, "missing prefix"), + ErrorKind::PrefixTypeMismatch => write!(f, "prefix type mismatch"), + ErrorKind::MissingNodeType => write!(f, "missing node type"), + ErrorKind::MissingDatabaseId => write!(f, "missing database id"), + } + } +} + +impl std::error::Error for GlobalIdParseError {} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_global_id() { + // Roundtrip. + let x1 = GlobalId { + kind: NodeKind::DeployApp, + database_id: 123, + }; + assert_eq!(Ok(x1), GlobalId::parse_prefixed(&x1.encode_prefixed()),); + assert_eq!(Ok(x1), GlobalId::parse_url(&x1.encode_url())); + + assert_eq!( + GlobalId::parse_prefixed("da_MRrWI0t5U582"), + Ok(GlobalId { + kind: NodeKind::DeployApp, + database_id: 273, + }) + ); + + // Error conditions. + assert_eq!( + GlobalId::parse_prefixed("oOtQIDI7q").err().unwrap().kind, + ErrorKind::MissingPrefix, + ); + assert_eq!( + GlobalId::parse_prefixed("oOtQIDI7q").err().unwrap().kind, + ErrorKind::MissingPrefix, + ); + assert_eq!( + GlobalId::parse_prefixed("_oOtQIDI7q").err().unwrap().kind, + ErrorKind::MissingPrefix, + ); + assert_eq!( + GlobalId::parse_prefixed("lala_oOtQIDI7q") + .err() + .unwrap() + .kind, + ErrorKind::UnknownPrefix("lala".to_string()), + ); + + let kind = GlobalId::parse_prefixed("da_xxx").err().unwrap().kind; + assert!(matches!(kind, ErrorKind::Decode(_))); + } + + #[test] + fn test_global_id_parse_values() { + assert_eq!(GlobalId::parse_values(&[]), Err(ErrorKind::MissingScope),); + assert_eq!( + GlobalId::parse_values(&[2]), + Err(ErrorKind::UnknownScope(2)), + ); + assert_eq!(GlobalId::parse_values(&[1]), Err(ErrorKind::MissingVersion),); + assert_eq!( + GlobalId::parse_values(&[1, 999]), + Err(ErrorKind::UnknownVersion(999)), + ); + assert_eq!( + GlobalId::parse_values(&[1, 2]), + Err(ErrorKind::MissingNodeType), + ); + assert_eq!( + GlobalId::parse_values(&[1, 2, 99999]), + Err(ErrorKind::UnknownNodeType(99999)), + ); + assert_eq!( + GlobalId::parse_values(&[1, 2, 1]), + Err(ErrorKind::MissingDatabaseId), + ); + assert_eq!( + GlobalId::parse_values(&[1, 2, 1, 1]), + Ok(GlobalId { + kind: NodeKind::SocialAuth, + database_id: 1, + }), + ); + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/gql.rs b/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/gql.rs new file mode 100644 index 0000000..d9657ff --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/gql.rs @@ -0,0 +1,2 @@ + +types diff --git a/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/lib.rs b/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/lib.rs new file mode 100644 index 0000000..ff6c2d1 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/lib.rs @@ -0,0 +1,29 @@ +// Allowed because it makes code more readable. +#![allow(clippy::bool_comparison, clippy::match_like_matches_macro)] + +mod client; +mod error; + +pub mod global_id; +pub mod query; +pub mod stream; +pub mod types; + +use url::Url; + +pub use self::{client::WasmerClient, error::GraphQLApiFailure}; + +/// Api endpoint for the dev environment. +pub const ENDPOINT_DEV: &str = "https://registry.wasmer.wtf/graphql"; +/// Api endpoint for the prod environment. +pub const ENDPOINT_PROD: &str = "https://registry.wasmer.io/graphql"; + +/// API endpoint for the dev environment. +pub fn endpoint_dev() -> Url { + Url::parse(ENDPOINT_DEV).unwrap() +} + +/// API endpoint for the prod environment. +pub fn endpoint_prod() -> Url { + Url::parse(ENDPOINT_PROD).unwrap() +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/query.rs b/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/query.rs new file mode 100644 index 0000000..5e804e7 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/query.rs @@ -0,0 +1,990 @@ +use std::{collections::HashSet, pin::Pin, time::Duration}; + +use anyhow::{bail, Context}; +use cynic::{MutationBuilder, QueryBuilder}; +use edge_schema::schema::{NetworkTokenV1, WebcIdent}; +use futures::{Stream, StreamExt}; +use time::OffsetDateTime; +use tracing::Instrument; +use url::Url; + +use crate::{ + types::{ + self, CreateNamespaceVars, DeployApp, DeployAppConnection, DeployAppVersion, + DeployAppVersionConnection, DnsDomain, GetCurrentUserWithAppsVars, GetDeployAppAndVersion, + GetDeployAppVersionsVars, GetNamespaceAppsVars, Log, LogStream, PackageVersionConnection, + PublishDeployAppVars, UpsertDomainFromZoneFileVars, + }, + GraphQLApiFailure, WasmerClient, +}; + +/// Load a webc package from the registry. +/// +/// NOTE: this uses the public URL instead of the download URL available through +/// the API, and should not be used where possible. +pub async fn fetch_webc_package( + client: &WasmerClient, + ident: &WebcIdent, + default_registry: &Url, +) -> Result { + let url = ident.build_download_url_with_default_registry(default_registry); + let data = client + .client + .get(url) + .header(reqwest::header::USER_AGENT, &client.user_agent) + .header(reqwest::header::ACCEPT, "application/webc") + .send() + .await? + .error_for_status()? + .bytes() + .await?; + + webc::compat::Container::from_bytes(data).context("failed to parse webc package") +} + +/// Get the currently logged in used, together with all accessible namespaces. +/// +/// You can optionally filter the namespaces by the user role. +pub async fn current_user_with_namespaces( + client: &WasmerClient, + namespace_role: Option, +) -> Result { + client + .run_graphql(types::GetCurrentUser::build(types::GetCurrentUserVars { + namespace_role, + })) + .await? + .viewer + .context("not logged in") +} + +/// Retrieve an app. +pub async fn get_app( + client: &WasmerClient, + owner: String, + name: String, +) -> Result, anyhow::Error> { + client + .run_graphql(types::GetDeployApp::build(types::GetDeployAppVars { + name, + owner, + })) + .await + .map(|x| x.get_deploy_app) +} + +/// Retrieve an app by its global alias. +pub async fn get_app_by_alias( + client: &WasmerClient, + alias: String, +) -> Result, anyhow::Error> { + client + .run_graphql(types::GetDeployAppByAlias::build( + types::GetDeployAppByAliasVars { alias }, + )) + .await + .map(|x| x.get_app_by_global_alias) +} + +/// Retrieve an app version. +pub async fn get_app_version( + client: &WasmerClient, + owner: String, + name: String, + version: String, +) -> Result, anyhow::Error> { + client + .run_graphql(types::GetDeployAppVersion::build( + types::GetDeployAppVersionVars { + name, + owner, + version, + }, + )) + .await + .map(|x| x.get_deploy_app_version) +} + +/// Retrieve an app together with a specific version. +pub async fn get_app_with_version( + client: &WasmerClient, + owner: String, + name: String, + version: String, +) -> Result { + client + .run_graphql(types::GetDeployAppAndVersion::build( + types::GetDeployAppAndVersionVars { + name, + owner, + version, + }, + )) + .await +} + +/// Retrieve an app together with a specific version. +pub async fn get_app_and_package_by_name( + client: &WasmerClient, + vars: types::GetPackageAndAppVars, +) -> Result<(Option, Option), anyhow::Error> { + let res = client + .run_graphql(types::GetPackageAndApp::build(vars)) + .await?; + Ok((res.get_package, res.get_deploy_app)) +} + +/// Retrieve apps. +pub async fn get_deploy_apps( + client: &WasmerClient, + vars: types::GetDeployAppsVars, +) -> Result { + let res = client + .run_graphql(types::GetDeployApps::build(vars)) + .await?; + res.get_deploy_apps.context("no apps returned") +} + +/// Retrieve apps as a stream that will automatically paginate. +pub fn get_deploy_apps_stream( + client: &WasmerClient, + vars: types::GetDeployAppsVars, +) -> impl futures::Stream, anyhow::Error>> + '_ { + futures::stream::try_unfold( + Some(vars), + move |vars: Option| async move { + let vars = match vars { + Some(vars) => vars, + None => return Ok(None), + }; + + let page = get_deploy_apps(client, vars.clone()).await?; + + let end_cursor = page.page_info.end_cursor; + + let items = page + .edges + .into_iter() + .filter_map(|x| x.and_then(|x| x.node)) + .collect::>(); + + let new_vars = end_cursor.map(|c| types::GetDeployAppsVars { + after: Some(c), + ..vars + }); + + Ok(Some((items, new_vars))) + }, + ) +} + +/// Retrieve versions for an app. +pub async fn get_deploy_app_versions( + client: &WasmerClient, + vars: GetDeployAppVersionsVars, +) -> Result { + let res = client + .run_graphql_strict(types::GetDeployAppVersions::build(vars)) + .await?; + let versions = res.get_deploy_app.context("app not found")?.versions; + Ok(versions) +} + +/// Load all versions of an app. +/// +/// Will paginate through all versions and return them in a single list. +pub async fn all_app_versions( + client: &WasmerClient, + owner: String, + name: String, +) -> Result, anyhow::Error> { + let mut vars = GetDeployAppVersionsVars { + owner, + name, + offset: None, + before: None, + after: None, + first: Some(10), + last: None, + sort_by: None, + }; + + let mut all_versions = Vec::::new(); + + loop { + let page = get_deploy_app_versions(client, vars.clone()).await?; + if page.edges.is_empty() { + break; + } + + for edge in page.edges { + let edge = match edge { + Some(edge) => edge, + None => continue, + }; + let version = match edge.node { + Some(item) => item, + None => continue, + }; + + // Sanity check to avoid duplication. + if all_versions.iter().any(|v| v.id == version.id) == false { + all_versions.push(version); + } + + // Update pagination. + vars.after = Some(edge.cursor); + } + } + + Ok(all_versions) +} + +/// Activate a particular version of an app. +pub async fn app_version_activate( + client: &WasmerClient, + version: String, +) -> Result { + let res = client + .run_graphql_strict(types::MarkAppVersionAsActive::build( + types::MarkAppVersionAsActiveVars { + input: types::MarkAppVersionAsActiveInput { + app_version: version.into(), + }, + }, + )) + .await?; + res.mark_app_version_as_active + .context("app not found") + .map(|x| x.app) +} + +/// Retrieve a node based on its global id. +pub async fn get_node( + client: &WasmerClient, + id: String, +) -> Result, anyhow::Error> { + client + .run_graphql(types::GetNode::build(types::GetNodeVars { id: id.into() })) + .await + .map(|x| x.node) +} + +/// Retrieve an app by its global id. +pub async fn get_app_by_id( + client: &WasmerClient, + app_id: String, +) -> Result { + get_app_by_id_opt(client, app_id) + .await? + .context("app not found") +} + +/// Retrieve an app by its global id. +pub async fn get_app_by_id_opt( + client: &WasmerClient, + app_id: String, +) -> Result, anyhow::Error> { + let app_opt = client + .run_graphql(types::GetDeployAppById::build( + types::GetDeployAppByIdVars { + app_id: app_id.into(), + }, + )) + .await? + .app; + + if let Some(app) = app_opt { + let app = app.into_deploy_app().context("app conversion failed")?; + Ok(Some(app)) + } else { + Ok(None) + } +} + +/// Retrieve an app together with a specific version. +pub async fn get_app_with_version_by_id( + client: &WasmerClient, + app_id: String, + version_id: String, +) -> Result<(DeployApp, DeployAppVersion), anyhow::Error> { + let res = client + .run_graphql(types::GetDeployAppAndVersionById::build( + types::GetDeployAppAndVersionByIdVars { + app_id: app_id.into(), + version_id: version_id.into(), + }, + )) + .await?; + + let app = res + .app + .context("app not found")? + .into_deploy_app() + .context("app conversion failed")?; + let version = res + .version + .context("version not found")? + .into_deploy_app_version() + .context("version conversion failed")?; + + Ok((app, version)) +} + +/// Retrieve an app version by its global id. +pub async fn get_app_version_by_id( + client: &WasmerClient, + version_id: String, +) -> Result { + client + .run_graphql(types::GetDeployAppVersionById::build( + types::GetDeployAppVersionByIdVars { + version_id: version_id.into(), + }, + )) + .await? + .version + .context("app not found")? + .into_deploy_app_version() + .context("app version conversion failed") +} + +pub async fn get_app_version_by_id_with_app( + client: &WasmerClient, + version_id: String, +) -> Result<(DeployApp, DeployAppVersion), anyhow::Error> { + let version = client + .run_graphql(types::GetDeployAppVersionById::build( + types::GetDeployAppVersionByIdVars { + version_id: version_id.into(), + }, + )) + .await? + .version + .context("app not found")? + .into_deploy_app_version() + .context("app version conversion failed")?; + + let app_id = version + .app + .as_ref() + .context("could not load app for version")? + .id + .clone(); + + let app = get_app_by_id(client, app_id.into_inner()).await?; + + Ok((app, version)) +} + +/// List all apps that are accessible by the current user. +/// +/// NOTE: this will only include the first pages and does not provide pagination. +pub async fn user_apps( + client: &WasmerClient, +) -> impl futures::Stream, anyhow::Error>> + '_ { + futures::stream::try_unfold(None, move |cursor| async move { + let user = client + .run_graphql(types::GetCurrentUserWithApps::build( + GetCurrentUserWithAppsVars { after: cursor }, + )) + .await? + .viewer + .context("not logged in")?; + + let apps: Vec<_> = user + .apps + .edges + .into_iter() + .flatten() + .filter_map(|x| x.node) + .collect(); + + let cursor = user.apps.page_info.end_cursor; + + if apps.is_empty() { + Ok(None) + } else { + Ok(Some((apps, cursor))) + } + }) +} + +/// List all apps that are accessible by the current user. +pub async fn user_accessible_apps( + client: &WasmerClient, +) -> Result< + impl futures::Stream, anyhow::Error>> + '_, + anyhow::Error, +> { + let apps: Pin, anyhow::Error>> + Send + Sync>> = + Box::pin(user_apps(client).await); + + // Get all aps in user-accessible namespaces. + let namespace_res = client + .run_graphql(types::GetCurrentUser::build(types::GetCurrentUserVars { + namespace_role: None, + })) + .await?; + let active_user = namespace_res.viewer.context("not logged in")?; + let namespace_names = active_user + .namespaces + .edges + .iter() + .filter_map(|edge| edge.as_ref()) + .filter_map(|edge| edge.node.as_ref()) + .map(|node| node.name.clone()) + .collect::>(); + + let mut all_apps = vec![apps]; + for ns in namespace_names { + let apps: Pin, anyhow::Error>> + Send + Sync>> = + Box::pin(namespace_apps(client, ns).await); + + all_apps.push(apps); + } + + let apps = futures::stream::select_all(all_apps); + + Ok(apps) +} + +/// Get apps for a specific namespace. +/// +/// NOTE: only retrieves the first page and does not do pagination. +pub async fn namespace_apps( + client: &WasmerClient, + namespace: String, +) -> impl futures::Stream, anyhow::Error>> + '_ { + let namespace = namespace.clone(); + + futures::stream::try_unfold((None, namespace), move |(cursor, namespace)| async move { + let res = client + .run_graphql(types::GetNamespaceApps::build(GetNamespaceAppsVars { + name: namespace.to_string(), + after: cursor, + })) + .await?; + + let ns = res + .get_namespace + .with_context(|| format!("failed to get namespace '{}'", namespace))?; + + let apps: Vec<_> = ns + .apps + .edges + .into_iter() + .flatten() + .filter_map(|x| x.node) + .collect(); + + let cursor = ns.apps.page_info.end_cursor; + + if apps.is_empty() { + Ok(None) + } else { + Ok(Some((apps, (cursor, namespace)))) + } + }) +} + +/// Publish a new app (version). +pub async fn publish_deploy_app( + client: &WasmerClient, + vars: PublishDeployAppVars, +) -> Result { + let res = client + .run_graphql_raw(types::PublishDeployApp::build(vars)) + .await?; + + if let Some(app) = res + .data + .and_then(|d| d.publish_deploy_app) + .map(|d| d.deploy_app_version) + { + Ok(app) + } else { + Err(GraphQLApiFailure::from_errors( + "could not publish app", + res.errors, + )) + } +} + +/// Delete an app. +pub async fn delete_app(client: &WasmerClient, app_id: String) -> Result<(), anyhow::Error> { + let res = client + .run_graphql_strict(types::DeleteApp::build(types::DeleteAppVars { + app_id: app_id.into(), + })) + .await? + .delete_app + .context("API did not return data for the delete_app mutation")?; + + if !res.success { + bail!("App deletion failed for an unknown reason"); + } + + Ok(()) +} + +/// Get all namespaces accessible by the current user. +pub async fn user_namespaces( + client: &WasmerClient, +) -> Result, anyhow::Error> { + let user = client + .run_graphql(types::GetCurrentUser::build(types::GetCurrentUserVars { + namespace_role: None, + })) + .await? + .viewer + .context("not logged in")?; + + let ns = user + .namespaces + .edges + .into_iter() + .flatten() + // .filter_map(|x| x) + .filter_map(|x| x.node) + .collect(); + + Ok(ns) +} + +/// Retrieve a namespace by its name. +pub async fn get_namespace( + client: &WasmerClient, + name: String, +) -> Result, anyhow::Error> { + client + .run_graphql(types::GetNamespace::build(types::GetNamespaceVars { name })) + .await + .map(|x| x.get_namespace) +} + +/// Create a new namespace. +pub async fn create_namespace( + client: &WasmerClient, + vars: CreateNamespaceVars, +) -> Result { + client + .run_graphql(types::CreateNamespace::build(vars)) + .await? + .create_namespace + .map(|x| x.namespace) + .context("no namespace returned") +} + +/// Retrieve a package by its name. +pub async fn get_package( + client: &WasmerClient, + name: String, +) -> Result, anyhow::Error> { + client + .run_graphql_strict(types::GetPackage::build(types::GetPackageVars { name })) + .await + .map(|x| x.get_package) +} + +/// Retrieve a package version by its name. +pub async fn get_package_version( + client: &WasmerClient, + name: String, + version: String, +) -> Result, anyhow::Error> { + client + .run_graphql_strict(types::GetPackageVersion::build( + types::GetPackageVersionVars { name, version }, + )) + .await + .map(|x| x.get_package_version) +} + +/// Retrieve package versions for an app. +pub async fn get_package_versions( + client: &WasmerClient, + vars: types::AllPackageVersionsVars, +) -> Result { + let res = client + .run_graphql(types::GetAllPackageVersions::build(vars)) + .await?; + Ok(res.all_package_versions) +} + +/// Retrieve all versions of a package as a stream that auto-paginates. +pub fn get_package_versions_stream( + client: &WasmerClient, + vars: types::AllPackageVersionsVars, +) -> impl futures::Stream, anyhow::Error>> + '_ +{ + futures::stream::try_unfold( + Some(vars), + move |vars: Option| async move { + let vars = match vars { + Some(vars) => vars, + None => return Ok(None), + }; + + let page = get_package_versions(client, vars.clone()).await?; + + let end_cursor = page.page_info.end_cursor; + + let items = page + .edges + .into_iter() + .filter_map(|x| x.and_then(|x| x.node)) + .collect::>(); + + let new_vars = end_cursor.map(|cursor| types::AllPackageVersionsVars { + after: Some(cursor), + ..vars + }); + + Ok(Some((items, new_vars))) + }, + ) +} + +/// Generate a new Edge token. +pub async fn generate_deploy_token_raw( + client: &WasmerClient, + app_version_id: String, +) -> Result { + let res = client + .run_graphql(types::GenerateDeployToken::build( + types::GenerateDeployTokenVars { app_version_id }, + )) + .await?; + + res.generate_deploy_token + .map(|x| x.token) + .context("no token returned") +} + +#[derive(Debug, PartialEq)] +pub enum GenerateTokenBy { + Id(NetworkTokenV1), +} + +#[derive(Debug, PartialEq)] +pub enum TokenKind { + SSH, + Network(GenerateTokenBy), +} + +pub async fn generate_deploy_config_token_raw( + client: &WasmerClient, + token_kind: TokenKind, +) -> Result { + let res = client + .run_graphql(types::GenerateDeployConfigToken::build( + types::GenerateDeployConfigTokenVars { + input: match token_kind { + TokenKind::SSH => "{}".to_string(), + TokenKind::Network(by) => match by { + GenerateTokenBy::Id(token) => serde_json::to_string(&token)?, + }, + }, + }, + )) + .await?; + + res.generate_deploy_config_token + .map(|x| x.token) + .context("no token returned") +} + +/// Get pages of logs associated with an application that lie within the +/// specified date range. +// NOTE: this is not public due to severe usability issues. +// The stream can loop forever due to re-fetching the same logs over and over. +#[tracing::instrument(skip_all, level = "debug")] +#[allow(clippy::let_with_type_underscore)] +#[allow(clippy::too_many_arguments)] +fn get_app_logs( + client: &WasmerClient, + name: String, + owner: String, + tag: Option, + start: OffsetDateTime, + end: Option, + watch: bool, + streams: Option>, +) -> impl futures::Stream, anyhow::Error>> + '_ { + // Note: the backend will limit responses to a certain number of log + // messages, so we use try_unfold() to keep calling it until we stop getting + // new log messages. + let span = tracing::Span::current(); + + futures::stream::try_unfold(start, move |start| { + let variables = types::GetDeployAppLogsVars { + name: name.clone(), + owner: owner.clone(), + version: tag.clone(), + first: Some(100), + starting_from: unix_timestamp(start), + until: end.map(unix_timestamp), + streams: streams.clone(), + }; + + let fut = async move { + loop { + let deploy_app_version = client + .run_graphql(types::GetDeployAppLogs::build(variables.clone())) + .await? + .get_deploy_app_version + .context("unknown package version")?; + + let page: Vec<_> = deploy_app_version + .logs + .edges + .into_iter() + .flatten() + .filter_map(|edge| edge.node) + .collect(); + + if page.is_empty() { + if watch { + /* + TODO: the resolution of watch should be configurable + TODO: should this be async? + */ + tokio::time::sleep(Duration::from_secs(1)).await; + continue; + } + + break Ok(None); + } else { + let last_message = page.last().expect("The page is non-empty"); + let timestamp = last_message.timestamp; + // NOTE: adding 1 microsecond to the timestamp to avoid fetching + // the last message again. + let timestamp = OffsetDateTime::from_unix_timestamp_nanos(timestamp as i128) + .with_context(|| { + format!("Unable to interpret {timestamp} as a unix timestamp") + })?; + + // FIXME: We need a better way to tell the backend "give me the + // next set of logs". Adding 1 nanosecond could theoretically + // mean we miss messages if multiple log messages arrived at + // the same nanosecond and the page ended midway. + + let next_timestamp = timestamp + Duration::from_nanos(1_000); + + break Ok(Some((page, next_timestamp))); + } + } + }; + + fut.instrument(span.clone()) + }) +} + +/// Get pages of logs associated with an application that lie within the +/// specified date range. +/// +/// In contrast to [`get_app_logs`], this function collects the stream into a +/// final vector. +#[tracing::instrument(skip_all, level = "debug")] +#[allow(clippy::let_with_type_underscore)] +#[allow(clippy::too_many_arguments)] +pub async fn get_app_logs_paginated( + client: &WasmerClient, + name: String, + owner: String, + tag: Option, + start: OffsetDateTime, + end: Option, + watch: bool, + streams: Option>, +) -> impl futures::Stream, anyhow::Error>> + '_ { + let stream = get_app_logs(client, name, owner, tag, start, end, watch, streams); + + stream.map(|res| { + let mut logs = Vec::new(); + let mut hasher = HashSet::new(); + let mut page = res?; + + // Prevent duplicates. + // TODO: don't clone the message, just hash it. + page.retain(|log| hasher.insert((log.message.clone(), log.timestamp.round() as i128))); + + logs.extend(page); + + Ok(logs) + }) +} + +/// Retrieve a domain by its name. +/// +/// Specify with_records to also retrieve all records for the domain. +pub async fn get_domain( + client: &WasmerClient, + domain: String, +) -> Result, anyhow::Error> { + let vars = types::GetDomainVars { domain }; + + let opt = client + .run_graphql(types::GetDomain::build(vars)) + .await + .map_err(anyhow::Error::from)? + .get_domain; + Ok(opt) +} + +/// Retrieve a domain by its name. +/// +/// Specify with_records to also retrieve all records for the domain. +pub async fn get_domain_zone_file( + client: &WasmerClient, + domain: String, +) -> Result, anyhow::Error> { + let vars = types::GetDomainVars { domain }; + + let opt = client + .run_graphql(types::GetDomainWithZoneFile::build(vars)) + .await + .map_err(anyhow::Error::from)? + .get_domain; + Ok(opt) +} + +/// Retrieve a domain by its name, along with all it's records. +pub async fn get_domain_with_records( + client: &WasmerClient, + domain: String, +) -> Result, anyhow::Error> { + let vars = types::GetDomainVars { domain }; + + let opt = client + .run_graphql(types::GetDomainWithRecords::build(vars)) + .await + .map_err(anyhow::Error::from)? + .get_domain; + Ok(opt) +} + +/// Register a new domain +pub async fn register_domain( + client: &WasmerClient, + name: String, + namespace: Option, + import_records: Option, +) -> Result { + let vars = types::RegisterDomainVars { + name, + namespace, + import_records, + }; + let opt = client + .run_graphql_strict(types::RegisterDomain::build(vars)) + .await + .map_err(anyhow::Error::from)? + .register_domain + .context("Domain registration failed")? + .domain + .context("Domain registration failed, no associatede domain found.")?; + Ok(opt) +} + +/// Retrieve all DNS records. +/// +/// NOTE: this is a privileged operation that requires extra permissions. +pub async fn get_all_dns_records( + client: &WasmerClient, + vars: types::GetAllDnsRecordsVariables, +) -> Result { + client + .run_graphql_strict(types::GetAllDnsRecords::build(vars)) + .await + .map_err(anyhow::Error::from) + .map(|x| x.get_all_dnsrecords) +} + +/// Retrieve all DNS domains. +pub async fn get_all_domains( + client: &WasmerClient, + vars: types::GetAllDomainsVariables, +) -> Result, anyhow::Error> { + let connection = client + .run_graphql_strict(types::GetAllDomains::build(vars)) + .await + .map_err(anyhow::Error::from) + .map(|x| x.get_all_domains) + .context("no domains returned")?; + Ok(connection + .edges + .into_iter() + .flatten() + .filter_map(|x| x.node) + .collect()) +} + +/// Retrieve a domain by its name. +/// +/// Specify with_records to also retrieve all records for the domain. +pub fn get_all_dns_records_stream( + client: &WasmerClient, + vars: types::GetAllDnsRecordsVariables, +) -> impl futures::Stream, anyhow::Error>> + '_ { + futures::stream::try_unfold( + Some(vars), + move |vars: Option| async move { + let vars = match vars { + Some(vars) => vars, + None => return Ok(None), + }; + + let page = get_all_dns_records(client, vars.clone()).await?; + + let end_cursor = page.page_info.end_cursor; + + let items = page + .edges + .into_iter() + .filter_map(|x| x.and_then(|x| x.node)) + .collect::>(); + + let new_vars = end_cursor.map(|c| types::GetAllDnsRecordsVariables { + after: Some(c), + ..vars + }); + + Ok(Some((items, new_vars))) + }, + ) +} + +/// Convert a [`OffsetDateTime`] to a unix timestamp that the WAPM backend +/// understands. +fn unix_timestamp(ts: OffsetDateTime) -> f64 { + let nanos_per_second = 1_000_000_000; + let timestamp = ts.unix_timestamp_nanos(); + let nanos = timestamp % nanos_per_second; + let secs = timestamp / nanos_per_second; + + (secs as f64) + (nanos as f64 / nanos_per_second as f64) +} + +/// Publish a new app (version). +pub async fn upsert_domain_from_zone_file( + client: &WasmerClient, + zone_file_contents: String, + delete_missing_records: bool, +) -> Result { + let vars = UpsertDomainFromZoneFileVars { + zone_file: zone_file_contents, + delete_missing_records: Some(delete_missing_records), + }; + let res = client + .run_graphql_strict(types::UpsertDomainFromZoneFile::build(vars)) + .await?; + + let domain = res + .upsert_domain_from_zone_file + .context("Upserting domain from zonefile failed")? + .domain; + + Ok(domain) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/stream.rs b/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/stream.rs new file mode 100644 index 0000000..c2a8f8e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/stream.rs @@ -0,0 +1,119 @@ +use std::{collections::VecDeque, task::Poll}; + +use futures::{ + future::{BoxFuture, OptionFuture}, + Future, +}; + +use super::WasmerClient; + +type PaginationFuture = BoxFuture<'static, Result<(Vec, Option

), anyhow::Error>>; + +pub trait PaginatedQuery { + type Vars; + type Paginator; + type Item; + + fn query( + &self, + client: WasmerClient, + paginator: Option, + ) -> PaginationFuture; +} + +pin_project_lite::pin_project! { + pub struct QueryStream { + query: Q, + + client: WasmerClient, + page: usize, + paginator: Option, + finished: bool, + items: VecDeque, + + #[pin] + fut: OptionFuture>, + } +} + +impl QueryStream { + pub fn new(query: Q, client: WasmerClient) -> Self { + Self { + query, + client, + page: 0, + finished: false, + paginator: None, + items: VecDeque::new(), + fut: None.into(), + } + } +} + +impl futures::Stream for QueryStream { + type Item = Result; + + fn poll_next( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll> { + let mut this = self.project(); + + if let Some(item) = this.items.pop_front() { + return Poll::Ready(Some(Ok(item))); + } + + match this.fut.as_mut().poll(cx) { + Poll::Ready(None) => {} + Poll::Ready(Some(Ok((items, paginator)))) => { + *this.paginator = paginator; + *this.page += 1; + // *this.fut = None.into(); + this.items.extend(items); + this.fut.set(None.into()); + + if let Some(item) = this.items.pop_front() { + return Poll::Ready(Some(Ok(item))); + } + } + Poll::Ready(Some(Err(err))) => { + return Poll::Ready(Some(Err(err))); + } + Poll::Pending => { + return Poll::Pending; + } + }; + + let pager = match this.paginator.take() { + Some(p) => Some(p), + None if *this.page == 0 => None, + None => { + return Poll::Ready(None); + } + }; + + let f = this.query.query(this.client.clone(), pager); + this.fut.set(Some(f).into()); + + match this.fut.as_mut().poll(cx) { + Poll::Ready(None) => { + unreachable!() + } + Poll::Ready(Some(Ok((items, paginator)))) => { + *this.paginator = paginator; + *this.page += 1; + // *this.fut = None.into(); + this.items.extend(items); + this.fut.set(None.into()); + + if let Some(item) = this.items.pop_front() { + Poll::Ready(Some(Ok(item))) + } else { + Poll::Ready(None) + } + } + Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(err))), + Poll::Pending => Poll::Pending, + } + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/types.rs b/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/types.rs new file mode 100644 index 0000000..bd481e8 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/backend-api/src/types.rs @@ -0,0 +1,1342 @@ +pub use queries::*; + +pub use cynic::Id; + +#[cynic::schema_for_derives(file = r#"schema.graphql"#, module = "schema")] +mod queries { + use serde::Serialize; + use time::OffsetDateTime; + + use super::schema; + + #[derive(cynic::Scalar, Debug, Clone)] + pub struct DateTime(pub String); + + impl TryFrom for DateTime { + type Error = time::error::Format; + + fn try_from(value: OffsetDateTime) -> Result { + value + .format(&time::format_description::well_known::Rfc3339) + .map(Self) + } + } + + impl TryFrom for OffsetDateTime { + type Error = time::error::Parse; + + fn try_from(value: DateTime) -> Result { + OffsetDateTime::parse(&value.0, &time::format_description::well_known::Rfc3339) + } + } + + #[derive(cynic::Scalar, Debug, Clone)] + pub struct JSONString(pub String); + + #[derive(cynic::Enum, Clone, Copy, Debug)] + pub enum GrapheneRole { + Owner, + Admin, + Editor, + Viewer, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GetCurrentUserVars { + pub namespace_role: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetCurrentUserVars")] + pub struct GetCurrentUser { + pub viewer: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + pub struct User { + pub id: cynic::Id, + pub username: String, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + pub struct Package { + pub id: cynic::Id, + pub package_name: String, + pub namespace: Option, + pub last_version: Option, + pub private: bool, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + pub struct PackageDistribution { + pub pirita_sha256_hash: Option, + pub pirita_download_url: Option, + pub download_url: Option, + pub size: Option, + pub pirita_size: Option, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + pub struct PackageVersion { + pub id: cynic::Id, + pub version: String, + pub created_at: DateTime, + pub distribution: PackageDistribution, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "PackageVersion")] + pub struct PackageVersionWithPackage { + pub id: cynic::Id, + pub version: String, + pub created_at: DateTime, + pub pirita_manifest: Option, + pub distribution: PackageDistribution, + + pub package: Package, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GetPackageVars { + pub name: String, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetPackageVars")] + pub struct GetPackage { + #[arguments(name: $name)] + pub get_package: Option, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GetPackageVersionVars { + pub name: String, + pub version: String, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetPackageVersionVars")] + pub struct GetPackageVersion { + #[arguments(name: $name, version: $version)] + pub get_package_version: Option, + } + + #[derive(cynic::Enum, Clone, Copy, Debug)] + pub enum PackageVersionSortBy { + Newest, + Oldest, + } + + #[derive(cynic::QueryVariables, Debug, Clone, Default)] + pub struct AllPackageVersionsVars { + pub offset: Option, + pub before: Option, + pub after: Option, + pub first: Option, + pub last: Option, + + pub created_after: Option, + pub updated_after: Option, + pub sort_by: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "AllPackageVersionsVars")] + pub struct GetAllPackageVersions { + #[arguments( + first: $first, + last: $last, + after: $after, + before: $before, + offset: $offset, + updatedAfter: $updated_after, + createdAfter: $created_after, + sortBy: $sort_by, + )] + pub all_package_versions: PackageVersionConnection, + } + + #[derive(cynic::QueryFragment, Debug)] + pub struct PackageVersionConnection { + pub page_info: PageInfo, + pub edges: Vec>, + } + + #[derive(cynic::QueryFragment, Debug)] + pub struct PackageVersionEdge { + pub node: Option, + pub cursor: String, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GetPackageAndAppVars { + pub package: String, + pub app_owner: String, + pub app_name: String, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetPackageAndAppVars")] + pub struct GetPackageAndApp { + #[arguments(name: $package)] + pub get_package: Option, + #[arguments(owner: $app_owner, name: $app_name)] + pub get_deploy_app: Option, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GetCurrentUserWithAppsVars { + pub after: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetCurrentUserWithAppsVars")] + pub struct GetCurrentUserWithApps { + pub viewer: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "User")] + #[cynic(variables = "GetCurrentUserWithAppsVars")] + pub struct UserWithApps { + pub id: cynic::Id, + pub username: String, + #[arguments(after: $after)] + pub apps: DeployAppConnection, + } + + #[derive(cynic::QueryFragment, Serialize, Debug, Clone)] + pub struct Owner { + pub global_name: String, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "User", variables = "GetCurrentUserVars")] + pub struct UserWithNamespaces { + pub id: cynic::Id, + pub username: String, + #[arguments(role: $namespace_role)] + pub namespaces: NamespaceConnection, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GetUserAppsVars { + pub username: String, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetUserAppsVars")] + pub struct GetUserApps { + #[arguments(username: $username)] + pub get_user: Option, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GetDeployAppVars { + pub name: String, + pub owner: String, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetDeployAppVars")] + pub struct GetDeployApp { + #[arguments(owner: $owner, name: $name)] + pub get_deploy_app: Option, + } + + #[derive(cynic::QueryVariables, Debug, Clone)] + pub struct PaginationVars { + pub offset: Option, + pub before: Option, + pub after: Option, + pub first: Option, + pub last: Option, + } + + #[derive(cynic::Enum, Clone, Copy, Debug)] + pub enum DeployAppsSortBy { + Newest, + Oldest, + MostActive, + } + + #[derive(cynic::QueryVariables, Debug, Clone, Default)] + pub struct GetDeployAppsVars { + pub offset: Option, + pub before: Option, + pub after: Option, + pub first: Option, + pub last: Option, + + pub updated_after: Option, + pub sort_by: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetDeployAppsVars")] + pub struct GetDeployApps { + #[arguments( + first: $first, + last: $last, + after: $after, + before: $before, + offset: $offset, + updatedAfter: $updated_after, + sortBy: $sort_by, + )] + pub get_deploy_apps: Option, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GetDeployAppByAliasVars { + pub alias: String, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetDeployAppByAliasVars")] + pub struct GetDeployAppByAlias { + #[arguments(alias: $alias)] + pub get_app_by_global_alias: Option, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GetDeployAppAndVersionVars { + pub name: String, + pub owner: String, + pub version: String, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetDeployAppAndVersionVars")] + pub struct GetDeployAppAndVersion { + #[arguments(owner: $owner, name: $name)] + pub get_deploy_app: Option, + #[arguments(owner: $owner, name: $name, version: $version)] + pub get_deploy_app_version: Option, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GetDeployAppVersionVars { + pub name: String, + pub owner: String, + pub version: String, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetDeployAppVersionVars")] + pub struct GetDeployAppVersion { + #[arguments(owner: $owner, name: $name, version: $version)] + pub get_deploy_app_version: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + pub struct RegisterDomainPayload { + pub success: bool, + pub domain: Option, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct RegisterDomainVars { + pub name: String, + pub namespace: Option, + pub import_records: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Mutation", variables = "RegisterDomainVars")] + pub struct RegisterDomain { + #[arguments(input: {name: $name, importRecords: $import_records, namespace: $namespace})] + pub register_domain: Option, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct UpsertDomainFromZoneFileVars { + pub zone_file: String, + pub delete_missing_records: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Mutation", variables = "UpsertDomainFromZoneFileVars")] + pub struct UpsertDomainFromZoneFile { + #[arguments(input: {zoneFile: $zone_file, deleteMissingRecords: $delete_missing_records})] + pub upsert_domain_from_zone_file: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + pub struct UpsertDomainFromZoneFilePayload { + pub success: bool, + pub domain: DnsDomain, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct CreateNamespaceVars { + pub name: String, + pub description: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Mutation", variables = "CreateNamespaceVars")] + pub struct CreateNamespace { + #[arguments(input: {name: $name, description: $description})] + pub create_namespace: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + pub struct CreateNamespacePayload { + pub namespace: Namespace, + } + + #[derive(cynic::InputObject, Debug)] + pub struct CreateNamespaceInput { + pub name: String, + pub display_name: Option, + pub description: Option, + pub avatar: Option, + pub client_mutation_id: Option, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + pub struct NamespaceEdge { + pub node: Option, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + pub struct NamespaceConnection { + pub edges: Vec>, + } + + #[derive(cynic::QueryFragment, Serialize, Debug, Clone)] + pub struct Namespace { + pub id: cynic::Id, + pub name: String, + pub global_name: String, + } + + #[derive(cynic::QueryFragment, Serialize, Debug, Clone)] + pub struct DeployApp { + pub id: cynic::Id, + pub name: String, + pub created_at: DateTime, + pub description: Option, + pub active_version: DeployAppVersion, + pub admin_url: String, + pub owner: Owner, + pub url: String, + pub deleted: bool, + pub aliases: AppAliasConnection, + } + + #[derive(cynic::QueryFragment, Serialize, Debug, Clone)] + pub struct AppAliasConnection { + pub page_info: PageInfo, + pub edges: Vec>, + } + + #[derive(cynic::QueryFragment, Serialize, Debug, Clone)] + pub struct AppAliasEdge { + pub node: Option, + } + + #[derive(cynic::QueryFragment, Serialize, Debug, Clone)] + pub struct AppAlias { + pub name: String, + } + + #[derive(cynic::QueryVariables, Debug, Clone)] + pub struct DeleteAppVars { + pub app_id: cynic::Id, + } + + #[derive(cynic::QueryFragment, Serialize, Debug, Clone)] + pub struct DeleteAppPayload { + pub success: bool, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Mutation", variables = "DeleteAppVars")] + pub struct DeleteApp { + #[arguments(input: { id: $app_id })] + pub delete_app: Option, + } + + #[derive(cynic::Enum, Clone, Copy, Debug)] + pub enum DeployAppVersionsSortBy { + Newest, + Oldest, + } + + #[derive(cynic::QueryVariables, Debug, Clone)] + pub struct GetDeployAppVersionsVars { + pub owner: String, + pub name: String, + + pub offset: Option, + pub before: Option, + pub after: Option, + pub first: Option, + pub last: Option, + pub sort_by: Option, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "Query", variables = "GetDeployAppVersionsVars")] + pub struct GetDeployAppVersions { + #[arguments(owner: $owner, name: $name)] + pub get_deploy_app: Option, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "DeployApp", variables = "GetDeployAppVersionsVars")] + pub struct DeployAppVersions { + #[arguments( + first: $first, + last: $last, + before: $before, + after: $after, + offset: $offset, + sortBy: $sort_by + )] + pub versions: DeployAppVersionConnection, + } + + #[derive(cynic::QueryFragment, Serialize, Debug, Clone)] + #[cynic(graphql_type = "DeployApp")] + pub struct SparseDeployApp { + pub id: cynic::Id, + } + + #[derive(cynic::QueryFragment, Serialize, Debug, Clone)] + pub struct DeployAppVersion { + pub id: cynic::Id, + pub created_at: DateTime, + pub version: String, + pub description: Option, + pub yaml_config: String, + pub user_yaml_config: String, + pub config: String, + pub json_config: String, + pub url: String, + + pub app: Option, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + pub struct DeployAppVersionConnection { + pub page_info: PageInfo, + pub edges: Vec>, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + pub struct DeployAppVersionEdge { + pub node: Option, + pub cursor: String, + } + + #[derive(cynic::QueryFragment, Debug)] + pub struct DeployAppConnection { + pub page_info: PageInfo, + pub edges: Vec>, + } + + #[derive(cynic::QueryFragment, Debug)] + pub struct DeployAppEdge { + pub node: Option, + pub cursor: String, + } + + #[derive(cynic::QueryFragment, Serialize, Debug, Clone)] + pub struct PageInfo { + pub has_next_page: bool, + pub end_cursor: Option, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GetNamespaceVars { + pub name: String, + } + + #[derive(cynic::QueryFragment, Serialize, Debug, Clone)] + pub struct MarkAppVersionAsActivePayload { + pub app: DeployApp, + } + + #[derive(cynic::InputObject, Debug)] + pub struct MarkAppVersionAsActiveInput { + pub app_version: cynic::Id, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct MarkAppVersionAsActiveVars { + pub input: MarkAppVersionAsActiveInput, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Mutation", variables = "MarkAppVersionAsActiveVars")] + pub struct MarkAppVersionAsActive { + #[arguments(input: $input)] + pub mark_app_version_as_active: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetNamespaceVars")] + pub struct GetNamespace { + #[arguments(name: $name)] + pub get_namespace: Option, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GetNamespaceAppsVars { + pub name: String, + pub after: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetNamespaceAppsVars")] + pub struct GetNamespaceApps { + #[arguments(name: $name)] + pub get_namespace: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Namespace")] + #[cynic(variables = "GetNamespaceAppsVars")] + pub struct NamespaceWithApps { + pub id: cynic::Id, + pub name: String, + #[arguments(after: $after)] + pub apps: DeployAppConnection, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct PublishDeployAppVars { + pub config: String, + pub name: cynic::Id, + pub owner: Option, + pub make_default: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Mutation", variables = "PublishDeployAppVars")] + pub struct PublishDeployApp { + #[arguments(input: { config: { yamlConfig: $config }, name: $name, owner: $owner, makeDefault: $make_default })] + pub publish_deploy_app: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + pub struct PublishDeployAppPayload { + pub deploy_app_version: DeployAppVersion, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GenerateDeployTokenVars { + pub app_version_id: String, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Mutation", variables = "GenerateDeployTokenVars")] + pub struct GenerateDeployToken { + #[arguments(input: { deployConfigVersionId: $app_version_id })] + pub generate_deploy_token: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + pub struct GenerateDeployTokenPayload { + pub token: String, + } + + #[derive(cynic::Enum, Clone, Copy, Debug)] + pub enum LogStream { + Stdout, + Stderr, + Runtime, + } + + #[derive(cynic::QueryVariables, Debug, Clone)] + pub struct GetDeployAppLogsVars { + pub name: String, + pub owner: String, + /// The tag associated with a particular app version. Uses the active + /// version if not provided. + pub version: Option, + /// The lower bound for log messages, in nanoseconds since the Unix + /// epoch. + pub starting_from: f64, + /// The upper bound for log messages, in nanoseconds since the Unix + /// epoch. + pub until: Option, + pub first: Option, + + pub streams: Option>, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetDeployAppLogsVars")] + pub struct GetDeployAppLogs { + #[arguments(name: $name, owner: $owner, version: $version)] + pub get_deploy_app_version: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "DeployAppVersion", variables = "GetDeployAppLogsVars")] + pub struct DeployAppVersionLogs { + #[arguments(startingFrom: $starting_from, until: $until, first: $first)] + pub logs: LogConnection, + } + + #[derive(cynic::QueryFragment, Debug)] + pub struct LogConnection { + pub edges: Vec>, + } + + #[derive(cynic::QueryFragment, Debug)] + pub struct LogEdge { + pub node: Option, + } + + #[derive(cynic::QueryFragment, Debug, serde::Serialize, PartialEq)] + pub struct Log { + pub message: String, + /// When the message was recorded, in nanoseconds since the Unix epoch. + pub timestamp: f64, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GenerateDeployConfigTokenVars { + pub input: String, + } + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Mutation", variables = "GenerateDeployConfigTokenVars")] + pub struct GenerateDeployConfigToken { + #[arguments(input: { config: $input })] + pub generate_deploy_config_token: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + pub struct GenerateDeployConfigTokenPayload { + pub token: String, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GetNodeVars { + pub id: cynic::Id, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetNodeVars")] + pub struct GetNode { + #[arguments(id: $id)] + pub node: Option, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GetDeployAppByIdVars { + pub app_id: cynic::Id, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetDeployAppByIdVars")] + pub struct GetDeployAppById { + #[arguments(id: $app_id)] + #[cynic(rename = "node")] + pub app: Option, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GetDeployAppAndVersionByIdVars { + pub app_id: cynic::Id, + pub version_id: cynic::Id, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetDeployAppAndVersionByIdVars")] + pub struct GetDeployAppAndVersionById { + #[arguments(id: $app_id)] + #[cynic(rename = "node")] + pub app: Option, + #[arguments(id: $version_id)] + #[cynic(rename = "node")] + pub version: Option, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GetDeployAppVersionByIdVars { + pub version_id: cynic::Id, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetDeployAppVersionByIdVars")] + pub struct GetDeployAppVersionById { + #[arguments(id: $version_id)] + #[cynic(rename = "node")] + pub version: Option, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "TXTRecord")] + pub struct TxtRecord { + pub id: cynic::Id, + pub created_at: DateTime, + pub updated_at: DateTime, + pub deleted_at: Option, + pub name: Option, + pub text: String, + pub ttl: Option, + pub data: String, + + pub domain: DnsDomain, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "SSHFPRecord")] + pub struct SshfpRecord { + pub id: cynic::Id, + pub created_at: DateTime, + pub updated_at: DateTime, + pub deleted_at: Option, + pub name: Option, + pub text: String, + pub ttl: Option, + #[cynic(rename = "type")] + pub type_: DnsmanagerSshFingerprintRecordTypeChoices, + pub algorithm: DnsmanagerSshFingerprintRecordAlgorithmChoices, + pub fingerprint: String, + + pub domain: DnsDomain, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "SRVRecord")] + pub struct SrvRecord { + pub id: cynic::Id, + pub created_at: DateTime, + pub updated_at: DateTime, + pub deleted_at: Option, + pub name: Option, + pub text: String, + pub ttl: Option, + pub service: String, + pub protocol: String, + pub priority: i32, + pub weight: i32, + pub port: i32, + pub target: String, + + pub domain: DnsDomain, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "SOARecord")] + pub struct SoaRecord { + pub id: cynic::Id, + pub created_at: DateTime, + pub updated_at: DateTime, + pub deleted_at: Option, + pub name: Option, + pub text: String, + pub ttl: Option, + pub mname: String, + pub rname: String, + pub serial: BigInt, + pub refresh: BigInt, + pub retry: BigInt, + pub expire: BigInt, + pub minimum: BigInt, + + pub domain: DnsDomain, + } + + #[derive(cynic::Enum, Debug, Clone, Copy)] + pub enum DNSRecordsSortBy { + Newest, + Oldest, + } + + #[derive(cynic::QueryVariables, Debug, Clone)] + pub struct GetAllDnsRecordsVariables { + pub after: Option, + pub updated_after: Option, + pub sort_by: Option, + pub first: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetAllDnsRecordsVariables")] + pub struct GetAllDnsRecords { + #[arguments( + first: $first, + after: $after, + updatedAfter: $updated_after, + sortBy: $sort_by + )] + #[cynic(rename = "getAllDNSRecords")] + pub get_all_dnsrecords: DnsRecordConnection, + } + + #[derive(cynic::QueryVariables, Debug, Clone)] + pub struct GetAllDomainsVariables { + pub after: Option, + pub first: Option, + pub namespace: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetAllDomainsVariables")] + pub struct GetAllDomains { + #[arguments( + first: $first, + after: $after, + namespace: $namespace, + )] + #[cynic(rename = "getAllDomains")] + pub get_all_domains: DnsDomainConnection, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "PTRRecord")] + pub struct PtrRecord { + pub id: cynic::Id, + pub created_at: DateTime, + pub updated_at: DateTime, + pub deleted_at: Option, + pub name: Option, + pub text: String, + pub ttl: Option, + pub ptrdname: String, + + pub domain: DnsDomain, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "NSRecord")] + pub struct NsRecord { + pub id: cynic::Id, + pub created_at: DateTime, + pub updated_at: DateTime, + pub deleted_at: Option, + pub name: Option, + pub text: String, + pub ttl: Option, + pub nsdname: String, + + pub domain: DnsDomain, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "MXRecord")] + pub struct MxRecord { + pub id: cynic::Id, + pub created_at: DateTime, + pub updated_at: DateTime, + pub deleted_at: Option, + pub name: Option, + pub text: String, + pub ttl: Option, + pub preference: i32, + pub exchange: String, + + pub domain: DnsDomain, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "DNSRecordConnection")] + pub struct DnsRecordConnection { + pub page_info: PageInfo, + pub edges: Vec>, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "DNSRecordEdge")] + pub struct DnsRecordEdge { + pub node: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "DNSDomainConnection")] + pub struct DnsDomainConnection { + pub page_info: PageInfo, + pub edges: Vec>, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "DNSDomainEdge")] + pub struct DnsDomainEdge { + pub node: Option, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "DNAMERecord")] + pub struct DNameRecord { + pub id: cynic::Id, + pub created_at: DateTime, + pub updated_at: DateTime, + pub deleted_at: Option, + pub name: Option, + pub text: String, + pub ttl: Option, + pub d_name: String, + + pub domain: DnsDomain, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "CNAMERecord")] + pub struct CNameRecord { + pub id: cynic::Id, + pub created_at: DateTime, + pub updated_at: DateTime, + pub deleted_at: Option, + pub name: Option, + pub text: String, + pub ttl: Option, + pub c_name: String, + + pub domain: DnsDomain, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "CAARecord")] + pub struct CaaRecord { + pub id: cynic::Id, + pub created_at: DateTime, + pub updated_at: DateTime, + pub deleted_at: Option, + pub name: Option, + pub text: String, + pub ttl: Option, + pub value: String, + pub flags: i32, + pub tag: DnsmanagerCertificationAuthorityAuthorizationRecordTagChoices, + + pub domain: DnsDomain, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "ARecord")] + pub struct ARecord { + pub id: cynic::Id, + pub created_at: DateTime, + pub updated_at: DateTime, + pub deleted_at: Option, + pub name: Option, + pub text: String, + pub ttl: Option, + pub address: String, + pub domain: DnsDomain, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "AAAARecord")] + pub struct AaaaRecord { + pub id: cynic::Id, + pub created_at: DateTime, + pub updated_at: DateTime, + pub deleted_at: Option, + pub name: Option, + pub text: String, + pub ttl: Option, + pub address: String, + pub domain: DnsDomain, + } + + #[derive(cynic::InlineFragments, Debug, Clone, Serialize)] + #[cynic(graphql_type = "DNSRecord")] + pub enum DnsRecord { + A(ARecord), + AAAA(AaaaRecord), + CName(CNameRecord), + Txt(TxtRecord), + Mx(MxRecord), + Ns(NsRecord), + CAA(CaaRecord), + DName(DNameRecord), + Ptr(PtrRecord), + Soa(SoaRecord), + Srv(SrvRecord), + Sshfp(SshfpRecord), + #[cynic(fallback)] + Unknown, + } + + impl DnsRecord { + pub fn id(&self) -> &str { + match self { + DnsRecord::A(record) => record.id.inner(), + DnsRecord::AAAA(record) => record.id.inner(), + DnsRecord::CName(record) => record.id.inner(), + DnsRecord::Txt(record) => record.id.inner(), + DnsRecord::Mx(record) => record.id.inner(), + DnsRecord::Ns(record) => record.id.inner(), + DnsRecord::CAA(record) => record.id.inner(), + DnsRecord::DName(record) => record.id.inner(), + DnsRecord::Ptr(record) => record.id.inner(), + DnsRecord::Soa(record) => record.id.inner(), + DnsRecord::Srv(record) => record.id.inner(), + DnsRecord::Sshfp(record) => record.id.inner(), + DnsRecord::Unknown => "", + } + } + pub fn name(&self) -> Option<&str> { + match self { + DnsRecord::A(record) => record.name.as_deref(), + DnsRecord::AAAA(record) => record.name.as_deref(), + DnsRecord::CName(record) => record.name.as_deref(), + DnsRecord::Txt(record) => record.name.as_deref(), + DnsRecord::Mx(record) => record.name.as_deref(), + DnsRecord::Ns(record) => record.name.as_deref(), + DnsRecord::CAA(record) => record.name.as_deref(), + DnsRecord::DName(record) => record.name.as_deref(), + DnsRecord::Ptr(record) => record.name.as_deref(), + DnsRecord::Soa(record) => record.name.as_deref(), + DnsRecord::Srv(record) => record.name.as_deref(), + DnsRecord::Sshfp(record) => record.name.as_deref(), + DnsRecord::Unknown => None, + } + } + pub fn ttl(&self) -> Option { + match self { + DnsRecord::A(record) => record.ttl, + DnsRecord::AAAA(record) => record.ttl, + DnsRecord::CName(record) => record.ttl, + DnsRecord::Txt(record) => record.ttl, + DnsRecord::Mx(record) => record.ttl, + DnsRecord::Ns(record) => record.ttl, + DnsRecord::CAA(record) => record.ttl, + DnsRecord::DName(record) => record.ttl, + DnsRecord::Ptr(record) => record.ttl, + DnsRecord::Soa(record) => record.ttl, + DnsRecord::Srv(record) => record.ttl, + DnsRecord::Sshfp(record) => record.ttl, + DnsRecord::Unknown => None, + } + } + + pub fn text(&self) -> &str { + match self { + DnsRecord::A(record) => record.text.as_str(), + DnsRecord::AAAA(record) => record.text.as_str(), + DnsRecord::CName(record) => record.text.as_str(), + DnsRecord::Txt(record) => record.text.as_str(), + DnsRecord::Mx(record) => record.text.as_str(), + DnsRecord::Ns(record) => record.text.as_str(), + DnsRecord::CAA(record) => record.text.as_str(), + DnsRecord::DName(record) => record.text.as_str(), + DnsRecord::Ptr(record) => record.text.as_str(), + DnsRecord::Soa(record) => record.text.as_str(), + DnsRecord::Srv(record) => record.text.as_str(), + DnsRecord::Sshfp(record) => record.text.as_str(), + DnsRecord::Unknown => "", + } + } + pub fn record_type(&self) -> &str { + match self { + DnsRecord::A(_) => "A", + DnsRecord::AAAA(_) => "AAAA", + DnsRecord::CName(_) => "CNAME", + DnsRecord::Txt(_) => "TXT", + DnsRecord::Mx(_) => "MX", + DnsRecord::Ns(_) => "NS", + DnsRecord::CAA(_) => "CAA", + DnsRecord::DName(_) => "DNAME", + DnsRecord::Ptr(_) => "PTR", + DnsRecord::Soa(_) => "SOA", + DnsRecord::Srv(_) => "SRV", + DnsRecord::Sshfp(_) => "SSHFP", + DnsRecord::Unknown => "", + } + } + + pub fn domain(&self) -> Option<&DnsDomain> { + match self { + DnsRecord::A(record) => Some(&record.domain), + DnsRecord::AAAA(record) => Some(&record.domain), + DnsRecord::CName(record) => Some(&record.domain), + DnsRecord::Txt(record) => Some(&record.domain), + DnsRecord::Mx(record) => Some(&record.domain), + DnsRecord::Ns(record) => Some(&record.domain), + DnsRecord::CAA(record) => Some(&record.domain), + DnsRecord::DName(record) => Some(&record.domain), + DnsRecord::Ptr(record) => Some(&record.domain), + DnsRecord::Soa(record) => Some(&record.domain), + DnsRecord::Srv(record) => Some(&record.domain), + DnsRecord::Sshfp(record) => Some(&record.domain), + DnsRecord::Unknown => None, + } + } + + pub fn created_at(&self) -> Option<&DateTime> { + match self { + DnsRecord::A(record) => Some(&record.created_at), + DnsRecord::AAAA(record) => Some(&record.created_at), + DnsRecord::CName(record) => Some(&record.created_at), + DnsRecord::Txt(record) => Some(&record.created_at), + DnsRecord::Mx(record) => Some(&record.created_at), + DnsRecord::Ns(record) => Some(&record.created_at), + DnsRecord::CAA(record) => Some(&record.created_at), + DnsRecord::DName(record) => Some(&record.created_at), + DnsRecord::Ptr(record) => Some(&record.created_at), + DnsRecord::Soa(record) => Some(&record.created_at), + DnsRecord::Srv(record) => Some(&record.created_at), + DnsRecord::Sshfp(record) => Some(&record.created_at), + DnsRecord::Unknown => None, + } + } + + pub fn updated_at(&self) -> Option<&DateTime> { + match self { + Self::A(record) => Some(&record.updated_at), + Self::AAAA(record) => Some(&record.updated_at), + Self::CName(record) => Some(&record.updated_at), + Self::Txt(record) => Some(&record.updated_at), + Self::Mx(record) => Some(&record.updated_at), + Self::Ns(record) => Some(&record.updated_at), + Self::CAA(record) => Some(&record.updated_at), + Self::DName(record) => Some(&record.updated_at), + Self::Ptr(record) => Some(&record.updated_at), + Self::Soa(record) => Some(&record.updated_at), + Self::Srv(record) => Some(&record.updated_at), + Self::Sshfp(record) => Some(&record.updated_at), + Self::Unknown => None, + } + } + + pub fn deleted_at(&self) -> Option<&DateTime> { + match self { + Self::A(record) => record.deleted_at.as_ref(), + Self::AAAA(record) => record.deleted_at.as_ref(), + Self::CName(record) => record.deleted_at.as_ref(), + Self::Txt(record) => record.deleted_at.as_ref(), + Self::Mx(record) => record.deleted_at.as_ref(), + Self::Ns(record) => record.deleted_at.as_ref(), + Self::CAA(record) => record.deleted_at.as_ref(), + Self::DName(record) => record.deleted_at.as_ref(), + Self::Ptr(record) => record.deleted_at.as_ref(), + Self::Soa(record) => record.deleted_at.as_ref(), + Self::Srv(record) => record.deleted_at.as_ref(), + Self::Sshfp(record) => record.deleted_at.as_ref(), + Self::Unknown => None, + } + } + } + + #[derive(cynic::Enum, Clone, Copy, Debug)] + pub enum DnsmanagerCertificationAuthorityAuthorizationRecordTagChoices { + Issue, + Issuewild, + Iodef, + } + + impl DnsmanagerCertificationAuthorityAuthorizationRecordTagChoices { + pub fn as_str(self) -> &'static str { + match self { + Self::Issue => "issue", + Self::Issuewild => "issuewild", + Self::Iodef => "iodef", + } + } + } + + #[derive(cynic::Enum, Clone, Copy, Debug)] + pub enum DnsmanagerSshFingerprintRecordAlgorithmChoices { + #[cynic(rename = "A_1")] + A1, + #[cynic(rename = "A_2")] + A2, + #[cynic(rename = "A_3")] + A3, + #[cynic(rename = "A_4")] + A4, + } + + #[derive(cynic::Enum, Clone, Copy, Debug)] + pub enum DnsmanagerSshFingerprintRecordTypeChoices { + #[cynic(rename = "A_1")] + A1, + #[cynic(rename = "A_2")] + A2, + } + + #[derive(cynic::QueryVariables, Debug)] + pub struct GetDomainVars { + pub domain: String, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetDomainVars")] + pub struct GetDomain { + #[arguments(name: $domain)] + pub get_domain: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetDomainVars")] + pub struct GetDomainWithZoneFile { + #[arguments(name: $domain)] + pub get_domain: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "GetDomainVars")] + pub struct GetDomainWithRecords { + #[arguments(name: $domain)] + pub get_domain: Option, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "DNSDomain")] + pub struct DnsDomain { + pub id: cynic::Id, + pub name: String, + pub slug: String, + pub owner: Owner, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "DNSDomain")] + pub struct DnsDomainWithZoneFile { + pub id: cynic::Id, + pub name: String, + pub slug: String, + pub zone_file: String, + } + + #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] + #[cynic(graphql_type = "DNSDomain")] + pub struct DnsDomainWithRecords { + pub id: cynic::Id, + pub name: String, + pub slug: String, + pub records: Option>>, + } + + #[derive(cynic::Scalar, Debug, Clone)] + pub struct BigInt(pub i64); + + #[derive(cynic::InlineFragments, Debug)] + pub enum Node { + DeployApp(Box), + DeployAppVersion(Box), + #[cynic(fallback)] + Unknown, + } + + impl Node { + pub fn into_deploy_app(self) -> Option { + match self { + Node::DeployApp(app) => Some(*app), + _ => None, + } + } + + pub fn into_deploy_app_version(self) -> Option { + match self { + Node::DeployAppVersion(version) => Some(*version), + _ => None, + } + } + } +} + +#[allow(non_snake_case, non_camel_case_types)] +mod schema { + cynic::use_schema!(r#"schema.graphql"#); +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/.gitignore b/arbitrator/tools/module_roots/wasmer/lib/c-api/.gitignore new file mode 100644 index 0000000..66de34f --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/.gitignore @@ -0,0 +1 @@ +doc/deprecated/html/ \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/CHANGELOG.md b/arbitrator/tools/module_roots/wasmer/lib/c-api/CHANGELOG.md new file mode 100644 index 0000000..6602d4a --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/CHANGELOG.md @@ -0,0 +1,48 @@ +# C API Changelog + +*The format is based on [Keep a Changelog].* + +[Keep a Changelog]: http://keepachangelog.com/en/1.0.0/ + +Looking for changes to the Wasmer CLI and the Rust API? See our [Primary Changelog](../../CHANGELOG.md) + +## **[Unreleased]** + +## 2.1.0 - 2021/11/30 + +### Added +- [#2521](https://github.com/wasmerio/wasmer/pull/2521) Create `OrderedResolver` from a parallel iterator, which improves performances of `wasm_new_instance` when a large set of imports is given. +- [#2449](https://github.com/wasmerio/wasmer/pull/2449) Configure `soname`, `install_name`, `out-implib`, etc. + +### Changed +- [#2478](https://github.com/wasmerio/wasmer/pull/2478) Rename `traps` input to `wasm_instance_new()` to `trap`. + +### Fixed +- [#2485](https://github.com/wasmerio/wasmer/pull/2493) Document wasm_limits_t’s members publicly. +- [#2444](https://github.com/wasmerio/wasmer/pull/2444) Trap's messages are always null terminated. +- [#2683](https://github.com/wasmerio/wasmer/pull/2683) Fix memory leaks in the C API. + +## 2.0.0 - 2020/06/16 + +## 2.0.0-rc1 - 2020/06/02 + +### Added +- [#2346](https://github.com/wasmerio/wasmer/pull/2346) Add missing `wasm_func_copy` function. +- [#2208](https://github.com/wasmerio/wasmer/pull/2208) Add a new CHANGELOG.md specific to our C API to make it easier for users primarily consuming our C API to keep up to date with changes that affect them. +- [#2103](https://github.com/wasmerio/wasmer/pull/2103) Add middleware (incl. metering) API. +- [#2153](https://github.com/wasmerio/wasmer/pull/2153) Add a `wasmer_features_t` unstable C API to define features for the engine and the compiler in the Wasm C API. +- [#2118](https://github.com/wasmerio/wasmer/pull/2118) Add an unstable non-standard C API to query available engines and compilers. + +### Changed +- [#2375](https://github.com/wasmerio/wasmer/pull/2375) Rename `wasmer_wasm.h` to `wasmer.h` (old behavior still continues to work). +- [#2370](https://github.com/wasmerio/wasmer/pull/2370) Remove the deprecated C API. + +### Fixed +- [#2208](https://github.com/wasmerio/wasmer/pull/2208) Fix ownership of `wasm_extern_as_func`, `wasm_extern_as_memory`, `wasm_extern_as_table`, `wasm_extern_as_global`, `wasm_func_as_extern`, `wasm_memory_as_extern`, `wasm_table_as_extern`, and `wasm_global_as_extern`. These functions no longer allocate memory and thus their results should not be freed. This is a breaking change to align more closely with the Wasm C API's stated ownership. +- [#2210](https://github.com/wasmerio/wasmer/pull/2210) Fix a memory leak in the strings used to identify imports and exports coming from user code. +- [#2117](https://github.com/wasmerio/wasmer/pull/2117) Formalize API prefixes. Only unstable functions have been renamed. +- [#2097](https://github.com/wasmerio/wasmer/pull/2097) Fix how string's length is computed in `wasm_cpu_features_add`. + +## Changes before 2020-04-06 + +See the [Primary Changelog](../../CHANGELOG.md). diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/Cargo.toml b/arbitrator/tools/module_roots/wasmer/lib/c-api/Cargo.toml new file mode 100644 index 0000000..716f429 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/Cargo.toml @@ -0,0 +1,109 @@ +[package] +name = "wasmer-c-api" +description = "Wasmer C API library" +categories = ["wasm", "api-bindings"] +keywords = ["wasm", "webassembly", "runtime"] +documentation = "https://wasmerio.github.io/wasmer/c-api/" +readme = "README.md" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[lib] +# The library name is `wasmer` so that we generate dylib like +# `libwasmer.so`, `libwasmer.dylib`, `wasmer.dll` etc. But it creates +# a conflict with the existing `wasmer` crate, see below. +name = "wasmer" # ##lib.name## + # ^ DO NOT REMOVE, it's used the `Makefile`, see `build-docs-capi`. +crate-type = ["staticlib", "cdylib"] #"cdylib", "rlib", "staticlib"] + +[dependencies] +# We rename `wasmer` to `wasmer-api` to avoid the conflict with this +# library name (see `[lib]`). +wasmer-api = { version = "=4.2.8", path = "../api", default-features = false, package = "wasmer" } +wasmer-compiler = { version = "=4.2.8", path = "../compiler", optional = true } +wasmer-compiler-cranelift = { version = "=4.2.8", path = "../compiler-cranelift", optional = true } +wasmer-compiler-llvm = { version = "=4.2.8", path = "../compiler-llvm", optional = true } +wasmer-compiler-singlepass = { version = "=4.2.8", path = "../compiler-singlepass", optional = true } +wasmer-emscripten = { version = "=4.2.8", path = "../emscripten", optional = true } +wasmer-middlewares = { version = "=4.2.8", path = "../middlewares", optional = true } +wasmer-types = { version = "=4.2.8", path = "../types" } +wasmer-wasix = { version = "0.18.3", path = "../wasix", features = ["host-fs", "host-vnet"], optional = true } +webc = { version = "5.0", optional = true } +virtual-fs = { version = "0.11.2", path = "../virtual-fs", optional = true, default-features = false, features = ["static-fs"] } +enumset.workspace = true +cfg-if = "1.0" +lazy_static = "1.4" +libc = { version = "^0.2", default-features = false } +thiserror = "1" +typetag = { version = "0.1", optional = true } +paste = "1.0" +tokio = { version = "1", features = [ "rt", "rt-multi-thread", "io-util", "sync", "macros"], default_features = false } + +[dev-dependencies] +field-offset = "0.3.3" + +[target.'cfg(target_os = "windows")'.dev-dependencies] +wasmer-inline-c = "0.1.1" + +[target.'cfg(not(target_os = "windows"))'.dev-dependencies] +inline-c = "0.1.7" + +[features] +default = [ + "wat", + "cranelift", + "compiler", + "wasi", + "middlewares", +] +sys = [] +jsc = ["wasmer-api/jsc", "wasmer-api/std"] +wat = ["wasmer-api/wat"] +wasi = ["wasmer-wasix"] +middlewares = [ + "compiler", + "wasmer-middlewares", +] +compiler = [ + "wasmer-compiler", + "wasmer-api/compiler", + "wasmer-compiler/translator", + "wasmer-compiler/compiler", +] +compiler-headless = [ + "wasmer-artifact-load", + "static-artifact-load", + "wasmer-api/compiler", + "wasmer-compiler/translator", + "wasmer-compiler/compiler", +] +singlepass = [ + "wasmer-compiler-singlepass", + "compiler", +] +cranelift = [ + "wasmer-compiler-cranelift", + "compiler", +] +llvm = [ + "wasmer-compiler-llvm", + "compiler", +] +wasmer-artifact-load = ["wasmer-compiler/wasmer-artifact-load"] +wasmer-artifact-create = ["wasmer-compiler/wasmer-artifact-create"] +static-artifact-load = ["wasmer-compiler/static-artifact-load"] +static-artifact-create = ["wasmer-compiler/static-artifact-create"] +webc_runner = ["virtual-fs", "webc"] +# Deprecated features. +jit = ["compiler"] + +# TODO: Port this feature. +#emscripten = ["wasmer-emscripten"] + +[build-dependencies] +cbindgen = { version = "0.24", default-features = false } diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/README.md b/arbitrator/tools/module_roots/wasmer/lib/c-api/README.md new file mode 100644 index 0000000..2651ddb --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/README.md @@ -0,0 +1,206 @@ +# `wasmer-c-api` [![Build Status](https://github.com/wasmerio/wasmer/workflows/build/badge.svg?style=flat-square)](https://github.com/wasmerio/wasmer/actions?query=workflow%3Abuild) [![Join Wasmer Slack](https://img.shields.io/static/v1?label=Slack&message=join%20chat&color=brighgreen&style=flat-square)](https://slack.wasmer.io) [![MIT License](https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square)](https://github.com/wasmerio/wasmer/blob/master/LICENSE) + +This crate exposes a C and a C++ API for the Wasmer runtime. It also fully supports the [wasm-c-api common API](https://github.com/WebAssembly/wasm-c-api). + +## Usage + +Once you [install Wasmer in your system](https://github.com/wasmerio/wasmer-install), the *shared object files* and the *headers* are available **inside the Wasmer installed path**. + +``` +$WASMER_DIR/ + lib/ + libwasmer.{so,dylib,dll} + include/ + wasm.h + wasmer.h + wasmer.hh + wasmer.h +``` + +Wasmer binary also ships with [`wasmer-config`](#wasmer-config) +an utility tool that outputs config information needed to compile programs which use Wasmer. + +[The full C API documentation can be found here](https://wasmerio.github.io/wasmer/crates/doc/wasmer_c_api/). + +Here is a simple example to use the C API: + +```c +#include +#include "wasmer.h" + +int main(int argc, const char* argv[]) { + const char *wat_string = + "(module\n" + " (type $sum_t (func (param i32 i32) (result i32)))\n" + " (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)\n" + " local.get $x\n" + " local.get $y\n" + " i32.add)\n" + " (export \"sum\" (func $sum_f)))"; + + wasm_byte_vec_t wat; + wasm_byte_vec_new(&wat, strlen(wat_string), wat_string); + wasm_byte_vec_t wasm_bytes; + wat2wasm(&wat, &wasm_bytes); + wasm_byte_vec_delete(&wat); + + printf("Creating the store...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + printf("Compiling module...\n"); + wasm_module_t* module = wasm_module_new(store, &wasm_bytes); + + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&wasm_bytes); + + printf("Creating imports...\n"); + wasm_extern_vec_t import_object = WASM_EMPTY_VEC; + + printf("Instantiating module...\n"); + wasm_instance_t* instance = wasm_instance_new(store, module, &import_object, NULL); + + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + printf("Retrieving exports...\n"); + wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return 1; + } + + printf("Retrieving the `sum` function...\n"); + wasm_func_t* sum_func = wasm_extern_as_func(exports.data[0]); + + if (sum_func == NULL) { + printf("> Failed to get the `sum` function!\n"); + return 1; + } + + printf("Calling `sum` function...\n"); + wasm_val_t args_val[2] = { WASM_I32_VAL(3), WASM_I32_VAL(4) }; + wasm_val_t results_val[1] = { WASM_INIT_VAL }; + wasm_val_vec_t args = WASM_ARRAY_VEC(args_val); + wasm_val_vec_t results = WASM_ARRAY_VEC(results_val); + + if (wasm_func_call(sum_func, &args, &results)) { + printf("> Error calling the `sum` function!\n"); + + return 1; + } + + printf("Results of `sum`: %d\n", results_val[0].of.i32); + + wasm_module_delete(module); + wasm_extern_vec_delete(&exports); + wasm_instance_delete(instance); + wasm_store_delete(store); + wasm_engine_delete(engine); +} +``` + +## Building + +You can compile Wasmer shared library from source: + +```text +make build-capi +``` + +This will generate the shared library \(depending on your system\): + +* Windows: `target/release/libwasmer_c_api.dll` +* macOS: `target/release/libwasmer_runtime_c_api.dylib` +* Linux: `target/release/libwasmer_runtime_c_api.so` + +If you want to generate the library and headers in a friendly format as shown in [Usage](#usage), you can execute the following in Wasmer root: + +```bash +make package-capi +``` + +This command will generate a `package` directory, that you can then use easily in the [Wasmer C API examples](https://docs.wasmer.io/integrations/examples). + + +## Testing + +Tests are run using the release build of the library. If you make +changes or compile with non-default features, please ensure you +rebuild in release mode for the tests to see the changes. + +To run all the full suite of tests, enter Wasmer root directory +and run the following commands: + +```sh +$ make test-capi +``` + +## `wasmer config` + +`wasmer config` output various configuration information needed to compile programs which use Wasmer. + +### `wasmer config --pkg-config` + +It outputs the necessary details for compiling and linking a program to Wasmer, +using the `pkg-config` format: + +```bash +$ wasmer config --pkg-config > $PKG_CONFIG_PATH/wasmer.pc +``` + +### `wasmer config --includedir` + +Directory containing Wasmer headers: + +```bash +$ wasmer config --includedir +/users/myuser/.wasmer/include +``` + +### `wasmer config --libdir` + +Directory containing Wasmer libraries: + +```bash +$ wasmer config --libdir +/users/myuser/.wasmer/lib +``` + +### `wasmer config --libs` + +Libraries needed to link against Wasmer components: + +```bash +$ wasmer config --libs +-L/Users/myuser/.wasmer/lib -lwasmer +``` + +### `wasmer config --cflags` + +Headers needed to build against Wasmer components: + +```bash +$ wasmer config --cflags +-I/Users/myuser/.wasmer/include/wasmer +``` + +## License + +Wasmer is primarily distributed under the terms of the [MIT +license][mit-license] ([LICENSE][license]). + + +[wasmer_h]: ./wasmer.h +[wasmer_hh]: ./wasmer.hh +[mit-license]: http://opensource.org/licenses/MIT +[license]: https://github.com/wasmerio/wasmer/blob/master/LICENSE +[Wasmer release page]: https://github.com/wasmerio/wasmer/releases diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/build.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/build.rs new file mode 100644 index 0000000..0ae35c7 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/build.rs @@ -0,0 +1,364 @@ +//! This build script aims at: +//! +//! * generating the C header files for the C API, +//! * setting `wasmer-inline-c` up. + +use cbindgen::{Builder, Language}; +use std::{ + env, + ffi::OsStr, + fs, + path::{Path, PathBuf}, +}; + +const PRE_HEADER: &str = r#" +// Define the `ARCH_X86_X64` constant. +#if defined(MSVC) && defined(_M_AMD64) +# define ARCH_X86_64 +#elif (defined(GCC) || defined(__GNUC__) || defined(__clang__)) && defined(__x86_64__) +# define ARCH_X86_64 +#endif + +// Compatibility with non-Clang compilers. +#if !defined(__has_attribute) +# define __has_attribute(x) 0 +#endif + +// Compatibility with non-Clang compilers. +#if !defined(__has_declspec_attribute) +# define __has_declspec_attribute(x) 0 +#endif + +// Define the `DEPRECATED` macro. +#if defined(GCC) || defined(__GNUC__) || __has_attribute(deprecated) +# define DEPRECATED(message) __attribute__((deprecated(message))) +#elif defined(MSVC) || __has_declspec_attribute(deprecated) +# define DEPRECATED(message) __declspec(deprecated(message)) +#endif +"#; + +#[allow(unused)] +const UNIVERSAL_FEATURE_AS_C_DEFINE: &str = "WASMER_UNIVERSAL_ENABLED"; + +#[allow(unused)] +const COMPILER_FEATURE_AS_C_DEFINE: &str = "WASMER_COMPILER_ENABLED"; + +#[allow(unused)] +const WASI_FEATURE_AS_C_DEFINE: &str = "WASMER_WASI_ENABLED"; + +#[allow(unused)] +const MIDDLEWARES_FEATURE_AS_C_DEFINE: &str = "WASMER_MIDDLEWARES_ENABLED"; + +#[allow(unused)] +const EMSCRIPTEN_FEATURE_AS_C_DEFINE: &str = "WASMER_EMSCRIPTEN_ENABLED"; + +#[allow(unused)] +const JSC_FEATURE_AS_C_DEFINE: &str = "WASMER_JSC_BACKEND"; + +macro_rules! map_feature_as_c_define { + ($feature:expr, $c_define:ident, $accumulator:ident) => { + #[cfg(feature = $feature)] + { + use std::fmt::Write; + let _ = write!( + $accumulator, + r#" +// The `{feature}` feature has been enabled for this build. +#define {define} +"#, + feature = $feature, + define = $c_define, + ); + } + }; +} + +fn main() { + if !running_self() { + return; + } + + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let out_dir = env::var("OUT_DIR").unwrap(); + + build_wasm_c_api_headers(&crate_dir, &out_dir); + build_inline_c_env_vars(); + build_cdylib_link_arg(); +} + +/// Check whether we should build the C API headers or set `wasmer-inline-c` up. +fn running_self() -> bool { + env::var("DOCS_RS").is_err() + && env::var("_CBINDGEN_IS_RUNNING").is_err() + && env::var("WASMER_PUBLISH_SCRIPT_IS_RUNNING").is_err() +} + +/// Build the header files for the `wasm_c_api` API. +fn build_wasm_c_api_headers(crate_dir: &str, out_dir: &str) { + let mut crate_header_file = PathBuf::from(crate_dir); + crate_header_file.push("wasmer"); + + let mut out_header_file = PathBuf::from(out_dir); + out_header_file.push("wasmer"); + + let mut pre_header = format!( + r#"// The Wasmer C/C++ header file compatible with the [`wasm-c-api`] +// standard API, as `wasm.h` (included here). +// +// This file is automatically generated by `lib/c-api/build.rs` of the +// [`wasmer-c-api`] Rust crate. +// +// # Stability +// +// The [`wasm-c-api`] standard API is a _living_ standard. There is no +// commitment for stability yet. We (Wasmer) will try our best to keep +// backward compatibility as much as possible. Nonetheless, some +// necessary API aren't yet standardized, and as such, we provide a +// custom API, e.g. `wasi_*` types and functions. +// +// The documentation makes it clear whether a function is unstable. +// +// When a type or a function will be deprecated, it will be marked as +// such with the appropriated compiler warning, and will be removed at +// the next release round. +// +// # Documentation +// +// At the time of writing, the [`wasm-c-api`] standard has no +// documentation. This file also does not include inline +// documentation. However, we have made (and we continue to make) an +// important effort to document everything. [See the documentation +// online][documentation]. Please refer to this page for the real +// canonical documentation. It also contains numerous examples. +// +// To generate the documentation locally, run `cargo doc --open` from +// within the [`wasmer-c-api`] Rust crate. +// +// [`wasm-c-api`]: https://github.com/WebAssembly/wasm-c-api +// [`wasmer-c-api`]: https://github.com/wasmerio/wasmer/tree/master/lib/c-api +// [documentation]: https://wasmerio.github.io/wasmer/crates/wasmer_c_api/ + +#if !defined(WASMER_H_PRELUDE) + +#define WASMER_H_PRELUDE +{pre_header}"#, + pre_header = PRE_HEADER + ); + + map_feature_as_c_define!("jsc", JSC_FEATURE_AS_C_DEFINE, pre_header); + map_feature_as_c_define!("compiler", UNIVERSAL_FEATURE_AS_C_DEFINE, pre_header); + map_feature_as_c_define!("compiler", COMPILER_FEATURE_AS_C_DEFINE, pre_header); + map_feature_as_c_define!("wasi", WASI_FEATURE_AS_C_DEFINE, pre_header); + map_feature_as_c_define!("middlewares", MIDDLEWARES_FEATURE_AS_C_DEFINE, pre_header); + map_feature_as_c_define!("emscripten", EMSCRIPTEN_FEATURE_AS_C_DEFINE, pre_header); + + add_wasmer_version(&mut pre_header); + + // Close pre header. + pre_header.push_str( + r#" +#endif // WASMER_H_PRELUDE + + +// +// OK, here we go. The code below is automatically generated. +// +"#, + ); + + let guard = "WASMER_H"; + + // C bindings. + { + // Generate the bindings in the `OUT_DIR`. + out_header_file.set_extension("h"); + + // Build and generate the header file. + new_builder(Language::C, crate_dir, guard, &pre_header) + .with_include("wasm.h") + .generate() + .expect("Unable to generate C bindings") + .write_to_file(out_header_file.as_path()); + + // Copy the generated bindings from `OUT_DIR` to + // `CARGO_MANIFEST_DIR`. + crate_header_file.set_extension("h"); + + fs::copy(out_header_file.as_path(), crate_header_file.as_path()) + .expect("Unable to copy the generated C bindings"); + } +} + +fn add_wasmer_version(pre_header: &mut String) { + use std::fmt::Write; + let _ = write!( + pre_header, + r#" +// This file corresponds to the following Wasmer version. +#define WASMER_VERSION "{full}" +#define WASMER_VERSION_MAJOR {major} +#define WASMER_VERSION_MINOR {minor} +#define WASMER_VERSION_PATCH {patch} +#define WASMER_VERSION_PRE "{pre}" +"#, + full = env!("CARGO_PKG_VERSION"), + major = env!("CARGO_PKG_VERSION_MAJOR"), + minor = env!("CARGO_PKG_VERSION_MINOR"), + patch = env!("CARGO_PKG_VERSION_PATCH"), + pre = env!("CARGO_PKG_VERSION_PRE"), + ); +} + +/// Create a fresh new `Builder`, already pre-configured. +fn new_builder(language: Language, crate_dir: &str, include_guard: &str, header: &str) -> Builder { + Builder::new() + .with_config(cbindgen::Config { + sort_by: cbindgen::SortKey::Name, + cpp_compat: true, + ..cbindgen::Config::default() + }) + .with_language(language) + .with_crate(crate_dir) + .with_include_guard(include_guard) + .with_header(header) + .with_documentation(false) + .with_define("target_family", "windows", "_WIN32") + .with_define("target_arch", "x86_64", "ARCH_X86_64") + .with_define("feature", "universal", UNIVERSAL_FEATURE_AS_C_DEFINE) + .with_define("feature", "compiler", COMPILER_FEATURE_AS_C_DEFINE) + .with_define("feature", "wasi", WASI_FEATURE_AS_C_DEFINE) + .with_define("feature", "emscripten", EMSCRIPTEN_FEATURE_AS_C_DEFINE) +} + +fn build_inline_c_env_vars() { + let shared_object_dir = shared_object_dir(); + let shared_object_dir = shared_object_dir.as_path().to_string_lossy(); + let include_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + + // The following options mean: + // + // * `-I`, add `include_dir` to include search path, + // * `-L`, add `shared_object_dir` to library search path, + // * `-D_DEBUG`, enable debug mode to enable `assert.h`. + // * `-D_CRT_SECURE_NO_WARNINGS`, disable security features in the + // Windows C runtime, which allows to use `getenv` without any + // warnings. + println!( + "cargo:rustc-env=INLINE_C_RS_CFLAGS=-I{I} -L{L} -D_DEBUG -D_CRT_SECURE_NO_WARNINGS", + I = include_dir, + L = shared_object_dir.clone(), + ); + + if let Ok(compiler_engine) = env::var("TEST") { + println!( + "cargo:rustc-env=INLINE_C_RS_TEST={test}", + test = compiler_engine + ); + } + + println!( + "cargo:rustc-env=INLINE_C_RS_LDFLAGS=-rpath,{shared_object_dir} {shared_object_dir}/{lib}", + shared_object_dir = shared_object_dir, + lib = if cfg!(target_os = "windows") { + "wasmer.dll".to_string() + } else if cfg!(target_vendor = "apple") { + "libwasmer.dylib".to_string() + } else { + let path = format!( + "{shared_object_dir}/{lib}", + shared_object_dir = shared_object_dir, + lib = "libwasmer.so" + ); + + if Path::new(path.as_str()).exists() { + "libwasmer.so".to_string() + } else { + "libwasmer.a".to_string() + } + } + ); +} + +fn build_cdylib_link_arg() { + // Code inspired by the `cdylib-link-lines` crate. + let mut lines = Vec::new(); + let os = env::var("CARGO_CFG_TARGET_OS").unwrap(); + let env = env::var("CARGO_CFG_TARGET_ENV").unwrap(); + let version_major = env::var("CARGO_PKG_VERSION_MAJOR").unwrap(); + let version_minor = env::var("CARGO_PKG_VERSION_MINOR").unwrap(); + let version_patch = env::var("CARGO_PKG_VERSION_PATCH").unwrap(); + let shared_object_dir = shared_object_dir(); + + match (os.as_str(), env.as_str()) { + ("android", _) => { + lines.push("-Wl,-soname,libwasmer.so".to_string()); + } + + ("linux", _) | ("freebsd", _) | ("dragonfly", _) | ("netbsd", _) if env != "musl" => { + lines.push("-Wl,-soname,libwasmer.so".to_string()); + } + + ("macos", _) | ("ios", _) => { + lines.push(format!( + "-Wl,-install_name,@rpath/libwasmer.dylib,-current_version,{x}.{y}.{z},-compatibility_version,{x}", + x = version_major, + y = version_minor, + z = version_patch, + )); + } + + ("windows", "gnu") => { + // This is only set up to work on GNU toolchain versions of Rust + lines.push(format!( + "-Wl,--out-implib,{}", + shared_object_dir.join("wasmer.dll.a").display() + )); + lines.push(format!( + "-Wl,--output-def,{}", + shared_object_dir.join("wasmer.def").display() + )); + } + + _ => {} + } + + for line in lines { + println!("cargo:rustc-cdylib-link-arg={}", line); + } +} + +fn shared_object_dir() -> PathBuf { + // We start from `OUT_DIR` because `cargo publish` uses a different directory + // so traversing from `CARGO_MANIFEST_DIR` is less reliable. + let mut shared_object_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + + assert_eq!(shared_object_dir.file_name(), Some(OsStr::new("out"))); + shared_object_dir.pop(); + + assert!(shared_object_dir + .file_name() + .as_ref() + .unwrap() + .to_string_lossy() + .to_string() + .starts_with("wasmer-c-api")); + shared_object_dir.pop(); + + assert_eq!(shared_object_dir.file_name(), Some(OsStr::new("build"))); + shared_object_dir.pop(); + shared_object_dir.pop(); // "debug" or "release" + + // We either find `target` or the target triple if cross-compiling. + if shared_object_dir.file_name() != Some(OsStr::new("target")) { + let target = env::var("TARGET").unwrap(); + if shared_object_dir.file_name() != Some(OsStr::new("llvm-cov-target")) { + assert_eq!(shared_object_dir.file_name(), Some(OsStr::new(&target))); + } else { + shared_object_dir.set_file_name(&target); + } + } + + shared_object_dir.push(env::var("PROFILE").unwrap()); + + shared_object_dir +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/doc/.empty b/arbitrator/tools/module_roots/wasmer/lib/c-api/doc/.empty new file mode 100644 index 0000000..e69de29 diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/Makefile b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/Makefile new file mode 100644 index 0000000..e2c919c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/Makefile @@ -0,0 +1,44 @@ +WASMER_DIR:=$(realpath $(WASMER_DIR)) + +$(info Using provided WASMER_DIR=$(WASMER_DIR)) + +ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) +ROOT_DIR_PARENT:=$(shell dirname $(ROOT_DIR)) + +MSVC_CFLAGS:="" +MSVC_LDFLAGS:="" +MSVC_LDLIBS:="" + +ifeq (,$(wildcard $(WASMER_DIR)/bin/wasmer)) + CFLAGS = -g -I $(ROOT_DIR)/ -I $(WASMER_DIR)/include + LDFLAGS = -Wl,-rpath,$(WASMER_DIR)/lib + LDLIBS = -L $(WASMER_DIR)/lib -lwasmer + + MSVC_CFLAGS:= /DEBUG /I $(ROOT_DIR)/ /I $(WASMER_DIR)/include + MSVC_LDFLAGS:= "" + MSVC_LDLIBS:= /LIBPATH:$(WASMER_DIR)/lib wasmer.dll.lib +else + CFLAGS = -g -I $(ROOT_DIR)/ -I $(shell $(WASMER_DIR)/bin/wasmer config --includedir) + LDFLAGS = -Wl,-rpath,$(shell $(WASMER_DIR)/bin/wasmer config --libdir) + LDLIBS = $(shell $(WASMER_DIR)/bin/wasmer config --libs) + + MSVC_CFLAGS:= /DEBUG /I $(ROOT_DIR)/ /I $(shell $(WASMER_DIR)/bin/wasmer config --includedir) + MSVC_LDFLAGS:= "" + MSVC_LDLIBS:= /LIBPATH:$(shell $(WASMER_DIR)/bin/wasmer config --libs) wasmer.dll.lib +endif + +$(info * CFLAGS: $(CFLAGS)) +$(info * LDFLAGS: $(LDFLAGS)) +$(info * LDLIBS: $(LDLIBS)) + +ALL = deprecated-header early-exit instance imports-exports exports-function exports-global memory memory2 features wasi + +.PHONY: run +.SILENT: run +run: + WASMER_DIR="$(WASMER_DIR)" ROOT_DIR="$(ROOT_DIR)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" LDLIBS="$(LDLIBS)" cargo test --manifest-path="./wasmer-capi-examples-runner/Cargo.toml" -- --nocapture 2>&1 + +.SILENT: clean +.PHONY: clean +clean: + $(foreach file,$(ALL),rm -f $(file).o $(file)) \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/call_trap.wasm b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/call_trap.wasm new file mode 100644 index 0000000..44cbb27 Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/call_trap.wasm differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/call_trap.wat b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/call_trap.wat new file mode 100644 index 0000000..febd5f8 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/call_trap.wat @@ -0,0 +1,11 @@ +(module + (type $run_t (func (param i32 i32) (result i32))) + (type $early_exit_t (func (param) (result))) + (import "env" "early_exit" (func $early_exit (type $early_exit_t))) + (func $run (type $run_t) (param $x i32) (param $y i32) (result i32) + (call $early_exit) + (i32.add + (local.get $x) + (local.get $y))) + (export "run" (func $run))) + diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/python-0.1.0.wasmer b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/python-0.1.0.wasmer new file mode 100644 index 0000000..cf6fb44 Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/python-0.1.0.wasmer differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/qjs-wasmer.toml b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/qjs-wasmer.toml new file mode 100644 index 0000000..53751a2 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/qjs-wasmer.toml @@ -0,0 +1,13 @@ +[package] +name = "adamz/qjs" +version = "0.0.1" +description = "https://github.com/bellard/quickjs" +license = "MIT" + +[[command]] +name = "qjs" +module = "qjs" + +[[module]] +name = "qjs" +source = "qjs.wasm" diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/qjs.wasm b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/qjs.wasm new file mode 100755 index 0000000..91f73f1 Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/qjs.wasm differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/staticserver.webc b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/staticserver.webc new file mode 100644 index 0000000..3cd4135 Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/staticserver.webc differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/wabt-1.0.37.wasmer b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/wabt-1.0.37.wasmer new file mode 100644 index 0000000..aa2859a Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/assets/wabt-1.0.37.wasmer differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/deprecated-header.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/deprecated-header.c new file mode 100644 index 0000000..fca3e2e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/deprecated-header.c @@ -0,0 +1,137 @@ +#include +#include +#include +#include + +// This is now deprecated, but it should work regardless +#include "wasmer_wasm.h" + +#define own + +// Use the last_error API to retrieve error messages +own char* get_wasmer_error() { + int error_len = wasmer_last_error_length(); + printf("Error len: `%d`\n", error_len); + char *error_str = malloc(error_len); + wasmer_last_error_message(error_str, error_len); + return error_str; +} + +int main(int argc, const char *argv[]) { + printf("Initializing...\n"); + own wasm_engine_t* engine = wasm_engine_new(); + own wasm_store_t* store = wasm_store_new(engine); + + // ===================== + wasm_limits_t limits1 = { + .min = 0, + .max = 0x7FFFFFFF, + }; + own wasm_memorytype_t* memtype1 = wasm_memorytype_new(&limits1); + own wasm_memory_t* memory1 = wasm_memory_new(store, memtype1); + assert(memory1 == NULL); + char* error = get_wasmer_error(); + printf("Found error string: %s\n", error); + // We can't validate the exact error message because it's not universal for the engines + // assert(0 == strcmp("The maximum requested memory (2147483647 pages) is greater than the maximum allowed memory (65536 pages)", error)); + free(error); + + wasm_memorytype_delete(memtype1); + + // ===================== + wasm_limits_t limits2 = { + .min = 15, + .max = 25, + }; + own wasm_memorytype_t* memtype2 = wasm_memorytype_new(&limits2); + own wasm_memory_t* memory2 = wasm_memory_new(store, memtype2); + assert(memory2 != NULL); + + wasm_memorytype_delete(memtype2); + wasm_memory_delete(memory2); + + // ===================== + wasm_limits_t limits3 = { + .min = 15, + .max = wasm_limits_max_default, + }; + own wasm_memorytype_t* memtype3 = wasm_memorytype_new(&limits3); + own wasm_memory_t* memory3 = wasm_memory_new(store, memtype3); + assert(memory3 != NULL); + int size = wasm_memory_size(memory3); + printf("memory size: %d\n", size); + + wasm_memorytype_delete(memtype3); + wasm_memory_delete(memory3); + + // ===================== + wasm_limits_t limits4 = { + .min = 0x7FFFFFFF, + .max = 0x7FFFFFFF, + }; + own wasm_memorytype_t* memtype4 = wasm_memorytype_new(&limits4); + own wasm_memory_t* memory4 = wasm_memory_new(store, memtype4); + assert(memory4 == NULL); + error = get_wasmer_error(); + printf("Found error string: %s\n", error); + // We can't validate the exact error message because it's not universal for the engines + // assert(0 == strcmp("The minimum requested (2147483647 pages) memory is greater than the maximum allowed memory (65536 pages)", error)); + free(error); + + wasm_memorytype_delete(memtype4); + + // ===================== + wasm_limits_t limits5 = { + .min = 0x7FFFFFFF, + .max = 0x0FFFFFFF, + }; + own wasm_memorytype_t* memtype5 = wasm_memorytype_new(&limits5); + own wasm_memory_t* memory5 = wasm_memory_new(store, memtype5); + assert(memory5 == NULL); + error = get_wasmer_error(); + printf("Found error string: %s\n", error); + // We can't validate the exact error message because it's not universal for the engines + // assert(0 == strcmp("The minimum requested (2147483647 pages) memory is greater than the maximum allowed memory (65536 pages)", error)); + free(error); + + wasm_memorytype_delete(memtype5); + + // ===================== + wasm_limits_t limits6 = { + .min = 15, + .max = 10, + }; + own wasm_memorytype_t* memtype6 = wasm_memorytype_new(&limits6); + own wasm_memory_t* memory6 = wasm_memory_new(store, memtype6); + assert(memory6 == NULL); + error = get_wasmer_error(); + printf("Found error string: %s\n", error); + // We can't validate the exact error message because it's not universal for the engines + // assert(0 == strcmp("The memory is invalid because the maximum (10 pages) is less than the minimum (15 pages)", error)); + free(error); + + wasm_memorytype_delete(memtype6); + + // ===================== + wasm_limits_t limits7 = { + .min = 0x7FFFFFFF, + .max = 10, + }; + own wasm_memorytype_t* memtype7 = wasm_memorytype_new(&limits7); + own wasm_memory_t* memory7 = wasm_memory_new(store, memtype7); + assert(memory7 == NULL); + error = get_wasmer_error(); + printf("Found error string: %s\n", error); + // We can't validate the exact error message because it's not universal for the engines + // assert(0 == strcmp("The minimum requested (2147483647 pages) memory is greater than the maximum allowed memory (65536 pages)", error)); + free(error); + + wasm_memorytype_delete(memtype7); + + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + printf("Done.\n"); + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/early-exit.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/early-exit.c new file mode 100644 index 0000000..6c4bb08 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/early-exit.c @@ -0,0 +1,168 @@ +#include +#include +#include +#include + +#include "wasmer.h" + +#define own + +// Use the last_error API to retrieve error messages +void print_wasmer_error() { + int error_len = wasmer_last_error_length(); + if (error_len > 0) { + printf("Error len: `%d`\n", error_len); + char *error_str = malloc(error_len); + wasmer_last_error_message(error_str, error_len); + printf("Error str: `%s`\n", error_str); + } +} + +void print_frame(wasm_frame_t* frame) { + printf("> %p @ 0x%zx = %"PRIu32".0x%zx\n", + wasm_frame_instance(frame), + wasm_frame_module_offset(frame), + wasm_frame_func_index(frame), + wasm_frame_func_offset(frame) + ); +} + +wasm_store_t *store = NULL; + +own wasm_trap_t* early_exit(const wasm_val_vec_t* args, wasm_val_vec_t* results) { + own wasm_message_t trap_message; + wasm_name_new_from_string_nt(&trap_message, "trapping from a host import"); + own wasm_trap_t *trap = wasm_trap_new(store, &trap_message); + wasm_name_delete(&trap_message); + return trap; +} + +int main(int argc, const char *argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t *engine = wasm_engine_new(); + store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); + FILE *file = fopen("assets/call_trap.wasm", "rb"); + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t *module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Instantiate. + printf("Instantiating module...\n"); + + wasm_functype_t *host_func_type = wasm_functype_new_0_0(); + wasm_func_t *host_func = wasm_func_new(store, host_func_type, early_exit); + + wasm_functype_delete(host_func_type); + + wasm_extern_vec_t imports; + wasm_extern_vec_new_uninitialized(&imports, 1); + imports.data[0] = wasm_func_as_extern(host_func); + + own wasm_instance_t *instance = + wasm_instance_new(store, module, &imports, NULL); + + if (!instance) { + printf("> Error instantiating module!\n"); + print_wasmer_error(); + return 1; + } + + // Extract export. + printf("Extracting export...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return 1; + } + fprintf(stderr, "found %zu exports\n", exports.size); + + wasm_module_delete(module); + wasm_instance_delete(instance); + + wasm_func_t *run_func = wasm_extern_as_func(exports.data[0]); + if (run_func == NULL) { + printf("> Error accessing export!\n"); + print_wasmer_error(); + return 1; + } + + // Call. + printf("Calling export...\n"); + wasm_val_t values[2] = { WASM_I32_VAL(1), WASM_I32_VAL(7) }; + own wasm_val_vec_t args = WASM_ARRAY_VEC(values); + wasm_val_t result = WASM_INIT_VAL; + own wasm_val_vec_t rets = { 1, &result }; + own wasm_trap_t *trap = wasm_func_call(run_func, &args, &rets); + + if (!trap) { + printf("> Error calling function: expected trap!\n"); + return 1; + } + + printf("Printing message...\n"); + own wasm_name_t message; + wasm_trap_message(trap, &message); + printf("> %s\n", message.data); + + printf("Printing origin...\n"); + own wasm_frame_t* frame = wasm_trap_origin(trap); + if (frame) { + print_frame(frame); + wasm_frame_delete(frame); + } else { + printf("> Empty origin.\n"); + } + + printf("Printing trace...\n"); + own wasm_frame_vec_t trace; + wasm_trap_trace(trap, &trace); + if (trace.size > 0) { + for (size_t i = 0; i < trace.size; ++i) { + print_frame(trace.data[i]); + } + } else { + printf("> Empty trace.\n"); + } + + wasm_frame_vec_delete(&trace); + wasm_trap_delete(trap); + wasm_name_delete(&message); + + wasm_extern_vec_delete(&exports); + wasm_extern_vec_delete(&imports); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/exports-function.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/exports-function.c new file mode 100644 index 0000000..8c64463 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/exports-function.c @@ -0,0 +1,85 @@ +#include +#include "wasmer.h" + +int main(int argc, const char* argv[]) { + const char *wat_string = + "(module\n" + " (type $sum_t (func (param i32 i32) (result i32)))\n" + " (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)\n" + " local.get $x\n" + " local.get $y\n" + " i32.add)\n" + " (export \"sum\" (func $sum_f)))"; + + wasm_byte_vec_t wat; + wasm_byte_vec_new(&wat, strlen(wat_string), wat_string); + wasm_byte_vec_t wasm_bytes; + wat2wasm(&wat, &wasm_bytes); + wasm_byte_vec_delete(&wat); + + printf("Creating the store...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + printf("Compiling module...\n"); + wasm_module_t* module = wasm_module_new(store, &wasm_bytes); + + if (!module) { + printf("> Error compiling module!\n"); + + return 1; + } + + wasm_byte_vec_delete(&wasm_bytes); + + printf("Creating imports...\n"); + wasm_extern_vec_t import_object = WASM_EMPTY_VEC; + + printf("Instantiating module...\n"); + wasm_instance_t* instance = wasm_instance_new(store, module, &import_object, NULL); + + if (!instance) { + printf("> Error instantiating module!\n"); + + return 1; + } + + printf("Retrieving exports...\n"); + wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + + return 1; + } + + printf("Retrieving the `sum` function...\n"); + wasm_func_t* sum_func = wasm_extern_as_func(exports.data[0]); + + if (sum_func == NULL) { + printf("> Failed to get the `sum` function!\n"); + + return 1; + } + + printf("Calling `sum` function...\n"); + wasm_val_t args_val[2] = { WASM_I32_VAL(3), WASM_I32_VAL(4) }; + wasm_val_t results_val[1] = { WASM_INIT_VAL }; + wasm_val_vec_t args = WASM_ARRAY_VEC(args_val); + wasm_val_vec_t results = WASM_ARRAY_VEC(results_val); + + if (wasm_func_call(sum_func, &args, &results)) { + printf("> Error calling the `sum` function!\n"); + + return 1; + } + + printf("Results of `sum`: %d\n", results_val[0].of.i32); + + wasm_module_delete(module); + wasm_instance_delete(instance); + wasm_extern_vec_delete(&exports); + wasm_store_delete(store); + wasm_engine_delete(engine); +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/exports-global.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/exports-global.c new file mode 100644 index 0000000..ff20dfd --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/exports-global.c @@ -0,0 +1,120 @@ +#include +#include "wasmer.h" + +int main(int argc, const char* argv[]) { + const char *wat_string = + "(module\n" + " (global $one (export \"one\") f32 (f32.const 1))\n" + " (global $some (export \"some\") (mut f32) (f32.const 0))\n" + " (func (export \"get_one\") (result f32) (global.get $one))\n" + " (func (export \"get_some\") (result f32) (global.get $some))\n" + " (func (export \"set_some\") (param f32) (global.set $some (local.get 0))))"; + + wasm_byte_vec_t wat; + wasm_byte_vec_new(&wat, strlen(wat_string), wat_string); + wasm_byte_vec_t wasm_bytes; + wat2wasm(&wat, &wasm_bytes); + wasm_byte_vec_delete(&wat); + + printf("Creating the store...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + printf("Compiling module...\n"); + wasm_module_t* module = wasm_module_new(store, &wasm_bytes); + + if (!module) { + printf("> Error compiling module!\n"); + + return 1; + } + + wasm_byte_vec_delete(&wasm_bytes); + + printf("Creating imports...\n"); + wasm_extern_vec_t import_object = WASM_EMPTY_VEC; + + printf("Instantiating module...\n"); + wasm_instance_t* instance = wasm_instance_new(store, module, &import_object, NULL); + + if (!instance) { + printf("> Error instantiating module!\n"); + + return 1; + } + + printf("Retrieving exports...\n"); + wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + + return 1; + } + + wasm_global_t* one = wasm_extern_as_global(exports.data[0]); + + if (one == NULL) { + printf("> Failed to get the `one` global!\n"); + + return 1; + } + + wasm_global_t* some = wasm_extern_as_global(exports.data[1]); + + if (some == NULL) { + printf("> Failed to get the `some` global!\n"); + + return 1; + } + + printf("Getting globals types information...\n"); + wasm_globaltype_t* one_type = wasm_global_type(one); + wasm_globaltype_t* some_type = wasm_global_type(some); + + wasm_mutability_t one_mutability = wasm_globaltype_mutability(one_type); + const wasm_valtype_t* one_content = wasm_globaltype_content(one_type); + wasm_valkind_t one_kind = wasm_valtype_kind(one_content); + + wasm_mutability_t some_mutability = wasm_globaltype_mutability(some_type); + const wasm_valtype_t* some_content = wasm_globaltype_content(some_type); + wasm_valkind_t some_kind = wasm_valtype_kind(some_content); + + printf("`one` type: %s %hhu\n", one_mutability == WASM_CONST ? "const" : "", one_kind); + printf("`some` type: %s %hhu\n", some_mutability == WASM_CONST ? "const" : "", some_kind); + + printf("Getting global values..."); + wasm_val_t one_value; + wasm_global_get(one, &one_value); + printf("`one` value: %.1f\n", one_value.of.f32); + + wasm_val_t some_value; + wasm_global_get(some, &some_value); + printf("`some` value: %.1f\n", some_value.of.f32); + + printf("Setting global values...\n"); + wasm_val_t one_set_value = WASM_F32_VAL(42); + wasm_global_set(one, &one_set_value); + + int error_length = wasmer_last_error_length(); + if (error_length > 0) { + char *error_message = malloc(error_length); + wasmer_last_error_message(error_message, error_length); + + printf("Attempted to set an immutable global: `%s`\n", error_message); + free(error_message); + } + + wasm_val_t some_set_value = WASM_F32_VAL(21); + wasm_global_set(some, &some_set_value); + printf("`some` value: %.1f\n", some_value.of.f32); + + wasm_globaltype_delete(one_type); + wasm_globaltype_delete(some_type); + wasm_module_delete(module); + wasm_extern_vec_delete(&exports); + wasm_instance_delete(instance); + wasm_store_delete(store); + wasm_engine_delete(engine); +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/features.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/features.c new file mode 100644 index 0000000..103017c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/features.c @@ -0,0 +1,96 @@ +#include +#include "wasmer.h" + +int main(int argc, const char* argv[]) { + // This is not enabled in JavascriptCore + #ifndef WASMER_JSC_BACKEND + + const char *wat_string = + "(module\n" + " (type $swap_t (func (param i32 i64) (result i64 i32)))\n" + " (func $swap (type $swap_t) (param $x i32) (param $y i64) (result i64 i32)\n" + " (local.get $y)\n" + " (local.get $x))\n" + " (export \"swap\" (func $swap)))"; + + wasm_byte_vec_t wat; + wasm_byte_vec_new(&wat, strlen(wat_string), wat_string); + wasm_byte_vec_t wasm_bytes; + wat2wasm(&wat, &wasm_bytes); + wasm_byte_vec_delete(&wat); + + printf("Creating the config and the features...\n"); + wasm_config_t* config = wasm_config_new(); + + wasmer_features_t* features = wasmer_features_new(); + wasmer_features_multi_value(features, true); // enable multi-value! + wasm_config_set_features(config, features); + + printf("Creating the store...\n"); + wasm_engine_t* engine = wasm_engine_new_with_config(config); + wasm_store_t* store = wasm_store_new(engine); + + printf("Compiling module...\n"); + wasm_module_t* module = wasm_module_new(store, &wasm_bytes); + + if (!module) { + printf("> Error compiling module!\n"); + + return 1; + } + + wasm_byte_vec_delete(&wasm_bytes); + + printf("Instantiating module...\n"); + wasm_extern_vec_t imports = WASM_EMPTY_VEC; + wasm_trap_t* trap = NULL; + wasm_instance_t* instance = wasm_instance_new(store, module, &imports,&trap); + + if (!instance) { + printf("> Error instantiating module!\n"); + + return 1; + } + + printf("Retrieving exports...\n"); + wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + + return 1; + } + + printf("Executing `swap(1, 2)`...\n"); + wasm_func_t* swap = wasm_extern_as_func(exports.data[0]); + + wasm_val_t arguments[2] = { WASM_I32_VAL(1), WASM_I64_VAL(2) }; + wasm_val_t results[2] = { WASM_INIT_VAL, WASM_INIT_VAL }; + wasm_val_vec_t arguments_as_array = WASM_ARRAY_VEC(arguments); + wasm_val_vec_t results_as_array = WASM_ARRAY_VEC(results); + + trap = wasm_func_call(swap, &arguments_as_array, &results_as_array); + + if (trap != NULL) { + printf("> Failed to call `swap`.\n"); + + return 1; + } + + if (results[0].of.i64 != 2 || results[1].of.i32 != 1) { + printf("> Multi-value failed.\n"); + + return 1; + } + + printf("Got `(2, 1)`!\n"); + + wasm_extern_vec_delete(&exports); + wasm_module_delete(module); + wasm_instance_delete(instance); + wasm_store_delete(store); + wasm_engine_delete(engine); + + #endif //WASMER_JSC_BACKEND +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/imports-exports.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/imports-exports.c new file mode 100644 index 0000000..e56a723 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/imports-exports.c @@ -0,0 +1,134 @@ +#include +#include "wasmer.h" + +wasm_trap_t* host_func_callback(const wasm_val_vec_t* args, wasm_val_vec_t* results) { + printf("Calling back...\n> "); + + wasm_val_t val = WASM_I32_VAL(42); + wasm_val_copy(&results->data[0], &val); + + wasm_val_delete(&val); + + return NULL; +} + +int main(int argc, const char* argv[]) { + const char *wat_string = + "(module\n" + " (func $host_function (import \"\" \"host_function\") (result i32))\n" + " (global $host_global (import \"env\" \"host_global\") i32)\n" + " (func $function (export \"guest_function\") (result i32) (global.get $global))\n" + " (global $global (export \"guest_global\") i32 (i32.const 42))\n" + " (table $table (export \"guest_table\") 1 1 funcref)\n" + " (memory $memory (export \"guest_memory\") 1))"; + + wasm_byte_vec_t wat; + wasm_byte_vec_new(&wat, strlen(wat_string), wat_string); + wasm_byte_vec_t wasm_bytes; + wat2wasm(&wat, &wasm_bytes); + wasm_byte_vec_delete(&wat); + + printf("Creating the store...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + printf("Compiling module...\n"); + wasm_module_t* module = wasm_module_new(store, &wasm_bytes); + + if (!module) { + printf("> Error compiling module!\n"); + + return 1; + } + + wasm_byte_vec_delete(&wasm_bytes); + + printf("Creating the imported function...\n"); + wasm_functype_t* host_func_type = wasm_functype_new_0_1(wasm_valtype_new_i32()); + wasm_func_t* host_func = wasm_func_new(store, host_func_type, host_func_callback); + wasm_functype_delete(host_func_type); + + printf("Creating the imported global...\n"); + wasm_globaltype_t* host_global_type = wasm_globaltype_new(wasm_valtype_new(WASM_F32), WASM_CONST); + wasm_val_t host_global_val = WASM_I32_VAL(42); + wasm_global_t* host_global = wasm_global_new(store, host_global_type, &host_global_val); + wasm_globaltype_delete(host_global_type); + + wasm_extern_t* externs[] = { + wasm_func_as_extern(host_func), + wasm_global_as_extern(host_global) + }; + + wasm_extern_vec_t import_object = WASM_ARRAY_VEC(externs); + + printf("Instantiating module...\n"); + wasm_instance_t* instance = wasm_instance_new(store, module, &import_object, NULL); + wasm_func_delete(host_func); + wasm_global_delete(host_global); + + if (!instance) { + printf("> Error instantiating module!\n"); + + return 1; + } + + printf("Retrieving exports...\n"); + wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + + return 1; + } + + printf("Retrieving the exported function...\n"); + wasm_func_t* func = wasm_extern_as_func(exports.data[0]); + + if (func == NULL) { + printf("> Failed to get the exported function!\n"); + + return 1; + } + + printf("Got the exported function: %p\n", func); + + printf("Retrieving the exported global...\n"); + wasm_global_t* global = wasm_extern_as_global(exports.data[1]); + + if (global == NULL) { + printf("> Failed to get the exported global!\n"); + + return 1; + } + + printf("Got the exported global: %p\n", global); + + printf("Retrieving the exported table...\n"); + wasm_table_t* table = wasm_extern_as_table(exports.data[2]); + + if (table == NULL) { + printf("> Failed to get the exported table!\n"); + + return 1; + } + + printf("Got the exported table: %p\n", table); + + printf("Retrieving the exported memory...\n"); + wasm_memory_t* memory = wasm_extern_as_memory(exports.data[3]); + + if (memory == NULL) { + printf("> Failed to get the exported memory!\n"); + + return 1; + } + + printf("Got the exported memory: %p\n", memory); + + wasm_module_delete(module); + wasm_extern_vec_delete(&exports); + wasm_instance_delete(instance); + wasm_store_delete(store); + wasm_engine_delete(engine); +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/instance.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/instance.c new file mode 100644 index 0000000..6eb867d --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/instance.c @@ -0,0 +1,84 @@ +#include +#include "wasmer.h" + +int main(int argc, const char* argv[]) { + const char *wat_string = + "(module\n" + " (type $add_one_t (func (param i32) (result i32)))\n" + " (func $add_one_f (type $add_one_t) (param $value i32) (result i32)\n" + " local.get $value\n" + " i32.const 1\n" + " i32.add)\n" + " (export \"add_one\" (func $add_one_f)))"; + + wasm_byte_vec_t wat; + wasm_byte_vec_new(&wat, strlen(wat_string), wat_string); + wasm_byte_vec_t wasm_bytes; + wat2wasm(&wat, &wasm_bytes); + wasm_byte_vec_delete(&wat); + + printf("Creating the store...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + printf("Compiling module...\n"); + wasm_module_t* module = wasm_module_new(store, &wasm_bytes); + + if (!module) { + printf("> Error compiling module!\n"); + + return 1; + } + + wasm_byte_vec_delete(&wasm_bytes); + + printf("Creating imports...\n"); + wasm_extern_vec_t imports = WASM_EMPTY_VEC; + + printf("Instantiating module...\n"); + wasm_instance_t* instance = wasm_instance_new(store, module, &imports, NULL); + + if (!instance) { + printf("> Error instantiating module!\n"); + + return 1; + } + + printf("Retrieving exports...\n"); + wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + + return 1; + } + + const wasm_func_t* add_one_func = wasm_extern_as_func(exports.data[0]); + if (add_one_func == NULL) { + printf("> Error accessing export!\n"); + + return 1; + } + + wasm_module_delete(module); + wasm_instance_delete(instance); + + printf("Calling `add_one` function...\n"); + wasm_val_t args_val[1] = { WASM_I32_VAL(1) }; + wasm_val_t results_val[1] = { WASM_INIT_VAL }; + wasm_val_vec_t args = WASM_ARRAY_VEC(args_val); + wasm_val_vec_t results = WASM_ARRAY_VEC(results_val); + + if (wasm_func_call(add_one_func, &args, &results)) { + printf("> Error calling function!\n"); + + return 1; + } + + printf("Results of `add_one`: %d\n", results_val[0].of.i32); + + wasm_extern_vec_delete(&exports); + wasm_store_delete(store); + wasm_engine_delete(engine); +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/memory.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/memory.c new file mode 100644 index 0000000..d332951 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/memory.c @@ -0,0 +1,107 @@ +#include +#include "wasmer.h" + +int main(int argc, const char* argv[]) { + const char *wat_string = + "(module\n" + " (type $mem_size_t (func (result i32)))\n" + " (type $get_at_t (func (param i32) (result i32)))\n" + " (type $set_at_t (func (param i32) (param i32)))\n" + " (memory $mem 1)\n" + " (func $get_at (type $get_at_t) (param $idx i32) (result i32)\n" + " (i32.load (local.get $idx)))\n" + " (func $set_at (type $set_at_t) (param $idx i32) (param $val i32)\n" + " (i32.store (local.get $idx) (local.get $val)))\n" + " (func $mem_size (type $mem_size_t) (result i32)\n" + " (memory.size))\n" + " (export \"get_at\" (func $get_at))\n" + " (export \"set_at\" (func $set_at))\n" + " (export \"mem_size\" (func $mem_size))\n" + " (export \"memory\" (memory $mem)))"; + + wasm_byte_vec_t wat; + wasm_byte_vec_new(&wat, strlen(wat_string), wat_string); + wasm_byte_vec_t wasm_bytes; + wat2wasm(&wat, &wasm_bytes); + wasm_byte_vec_delete(&wat); + + printf("Creating the store...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + printf("Compiling module...\n"); + wasm_module_t* module = wasm_module_new(store, &wasm_bytes); + + if (!module) { + printf("> Error compiling module!\n"); + + return 1; + } + + wasm_byte_vec_delete(&wasm_bytes); + + printf("Creating imports...\n"); + wasm_extern_vec_t import_object = WASM_EMPTY_VEC; + + printf("Instantiating module...\n"); + wasm_instance_t* instance = wasm_instance_new(store, module, &import_object, NULL); + + if (!instance) { + printf("> Error instantiating module!\n"); + + return 1; + } + + printf("Retrieving exports...\n"); + wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + + return 1; + } + + wasm_func_t* get_at = wasm_extern_as_func(exports.data[0]); + wasm_func_t* set_at = wasm_extern_as_func(exports.data[1]); + wasm_func_t* mem_size = wasm_extern_as_func(exports.data[2]); + wasm_memory_t* memory = wasm_extern_as_memory(exports.data[3]); + + printf("Querying memory size...\n"); + wasm_memory_pages_t pages = wasm_memory_size(memory); + size_t data_size = wasm_memory_data_size(memory); + printf("Memory size (pages): %d\n", pages); + printf("Memory size (bytes): %d\n", (int) data_size); + + printf("Growing memory...\n"); + if (!wasm_memory_grow(memory, 2)) { + printf("> Error growing memory!\n"); + + return 1; + } + + wasm_memory_pages_t new_pages = wasm_memory_size(memory); + printf("New memory size (pages): %d\n", new_pages); + + int mem_addr = 0x2220; + int val = 0xFEFEFFE; + + wasm_val_t set_at_args_val[2] = { WASM_I32_VAL(mem_addr), WASM_I32_VAL(val) }; + wasm_val_vec_t set_at_args = WASM_ARRAY_VEC(set_at_args_val); + wasm_val_vec_t set_at_results = WASM_EMPTY_VEC; + wasm_func_call(set_at, &set_at_args, &set_at_results); + + wasm_val_t get_at_args_val[1] = { WASM_I32_VAL(mem_addr) }; + wasm_val_vec_t get_at_args = WASM_ARRAY_VEC(get_at_args_val); + wasm_val_t get_at_results_val[1] = { WASM_INIT_VAL }; + wasm_val_vec_t get_at_results = WASM_ARRAY_VEC(get_at_results_val); + wasm_func_call(get_at, &get_at_args, &get_at_results); + + printf("Value at 0x%04x: %d\n", mem_addr, get_at_results_val[0].of.i32); + + wasm_extern_vec_delete(&exports); + wasm_module_delete(module); + wasm_instance_delete(instance); + wasm_store_delete(store); + wasm_engine_delete(engine); +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/memory2.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/memory2.c new file mode 100644 index 0000000..8091f0a --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/memory2.c @@ -0,0 +1,136 @@ +#include +#include +#include +#include + +#include "wasmer.h" + +#define own + +// Use the last_error API to retrieve error messages +own char* get_wasmer_error() { + int error_len = wasmer_last_error_length(); + printf("Error len: `%d`\n", error_len); + char *error_str = malloc(error_len); + wasmer_last_error_message(error_str, error_len); + return error_str; +} + +int main(int argc, const char *argv[]) { + printf("Initializing...\n"); + own wasm_engine_t* engine = wasm_engine_new(); + own wasm_store_t* store = wasm_store_new(engine); + + // ===================== + wasm_limits_t limits1 = { + .min = 0, + .max = 0x7FFFFFFF, + }; + own wasm_memorytype_t* memtype1 = wasm_memorytype_new(&limits1); + own wasm_memory_t* memory1 = wasm_memory_new(store, memtype1); + assert(memory1 == NULL); + char* error = get_wasmer_error(); + printf("Found error string: %s\n", error); + // We can't validate the exact error message because it's not universal for the engines + // assert(0 == strcmp("The maximum requested memory (2147483647 pages) is greater than the maximum allowed memory (65536 pages)", error)); + free(error); + + wasm_memorytype_delete(memtype1); + + // ===================== + wasm_limits_t limits2 = { + .min = 15, + .max = 25, + }; + own wasm_memorytype_t* memtype2 = wasm_memorytype_new(&limits2); + own wasm_memory_t* memory2 = wasm_memory_new(store, memtype2); + assert(memory2 != NULL); + + wasm_memorytype_delete(memtype2); + wasm_memory_delete(memory2); + + // ===================== + wasm_limits_t limits3 = { + .min = 15, + .max = wasm_limits_max_default, + }; + own wasm_memorytype_t* memtype3 = wasm_memorytype_new(&limits3); + own wasm_memory_t* memory3 = wasm_memory_new(store, memtype3); + assert(memory3 != NULL); + int size = wasm_memory_size(memory3); + printf("memory size: %d\n", size); + + wasm_memorytype_delete(memtype3); + wasm_memory_delete(memory3); + + // ===================== + wasm_limits_t limits4 = { + .min = 0x7FFFFFFF, + .max = 0x7FFFFFFF, + }; + own wasm_memorytype_t* memtype4 = wasm_memorytype_new(&limits4); + own wasm_memory_t* memory4 = wasm_memory_new(store, memtype4); + assert(memory4 == NULL); + error = get_wasmer_error(); + printf("Found error string: %s\n", error); + // We can't validate the exact error message because it's not universal for the engines + // assert(0 == strcmp("The minimum requested (2147483647 pages) memory is greater than the maximum allowed memory (65536 pages)", error)); + free(error); + + wasm_memorytype_delete(memtype4); + + // ===================== + wasm_limits_t limits5 = { + .min = 0x7FFFFFFF, + .max = 0x0FFFFFFF, + }; + own wasm_memorytype_t* memtype5 = wasm_memorytype_new(&limits5); + own wasm_memory_t* memory5 = wasm_memory_new(store, memtype5); + assert(memory5 == NULL); + error = get_wasmer_error(); + printf("Found error string: %s\n", error); + // We can't validate the exact error message because it's not universal for the engines + // assert(0 == strcmp("The minimum requested (2147483647 pages) memory is greater than the maximum allowed memory (65536 pages)", error)); + free(error); + + wasm_memorytype_delete(memtype5); + + // ===================== + wasm_limits_t limits6 = { + .min = 15, + .max = 10, + }; + own wasm_memorytype_t* memtype6 = wasm_memorytype_new(&limits6); + own wasm_memory_t* memory6 = wasm_memory_new(store, memtype6); + assert(memory6 == NULL); + error = get_wasmer_error(); + printf("Found error string: %s\n", error); + // We can't validate the exact error message because it's not universal for the engines + // assert(0 == strcmp("The memory is invalid because the maximum (10 pages) is less than the minimum (15 pages)", error)); + free(error); + + wasm_memorytype_delete(memtype6); + + // ===================== + wasm_limits_t limits7 = { + .min = 0x7FFFFFFF, + .max = 10, + }; + own wasm_memorytype_t* memtype7 = wasm_memorytype_new(&limits7); + own wasm_memory_t* memory7 = wasm_memory_new(store, memtype7); + assert(memory7 == NULL); + error = get_wasmer_error(); + printf("Found error string: %s\n", error); + // We can't validate the exact error message because it's not universal for the engines + // assert(0 == strcmp("The minimum requested (2147483647 pages) memory is greater than the maximum allowed memory (65536 pages)", error)); + free(error); + + wasm_memorytype_delete(memtype7); + + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + printf("Done.\n"); + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/wasi.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/wasi.c new file mode 100644 index 0000000..3e1fb38 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/wasi.c @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include + +#include "wasmer.h" + +#define BUF_SIZE 128 +#define own + +// Use the last_error API to retrieve error messages +void print_wasmer_error() +{ + int error_len = wasmer_last_error_length(); + if (error_len > 0) { + printf("Error len: `%d`\n", error_len); + char *error_str = malloc(error_len); + wasmer_last_error_message(error_str, error_len); + printf("Error str: `%s`\n", error_str); + } +} + +int main(int argc, const char* argv[]) { + #ifdef WASMER_WASI_ENABLED // If WASI is enabled + + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + printf("Setting up WASI...\n"); + wasi_config_t* config = wasi_config_new("example_program"); + // TODO: error checking + const char* js_string = "function greet(name) { return JSON.stringify('Hello, ' + name); }; print(greet('World'));"; + wasi_config_arg(config, "--eval"); + wasi_config_arg(config, js_string); + wasi_config_capture_stdout(config); + + // Load binary. + printf("Loading binary...\n"); + FILE* file = fopen("assets/qjs.wasm", "rb"); + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error initializing module!\n"); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + wasi_env_t* wasi_env = wasi_env_new(store, config); + + if (!wasi_env) { + printf("> Error building WASI env!\n"); + print_wasmer_error(); + return 1; + } + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_vec_t imports; + bool get_imports_result = wasi_get_imports(store, wasi_env,module,&imports); + + if (!get_imports_result) { + printf("> Error getting WASI imports!\n"); + print_wasmer_error(); + return 1; + } + + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + + if (!instance) { + printf("> Error instantiating module!\n"); + print_wasmer_error(); + return 1; + } + + if (!wasi_env_initialize_instance(wasi_env, store, instance)) { + printf("> Error initializing wasi env memory!\n"); + print_wasmer_error(); + return 1; + } + + + // Extract export. + printf("Extracting export...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return 1; + } + fprintf(stderr, "found %zu exports\n", exports.size); + + wasm_func_t* run_func = wasi_get_start_function(instance); + if (run_func == NULL) { + printf("> Error accessing export!\n"); + print_wasmer_error(); + return 1; + } + + // Call. + printf("Calling export...\n"); + printf("Evaluating \"%s\"\n", js_string); + + wasm_val_vec_t args = WASM_EMPTY_VEC; + wasm_val_vec_t res = WASM_EMPTY_VEC; + + if (wasm_func_call(run_func, &args, &res)) { + printf("> Error calling function!\n"); + return 1; + } + printf("Call completed\n"); + + if(true) { + + // NOTE: previously, this used open_memstream, + // which is not cross-platform + FILE *memory_stream = NULL; + memory_stream = tmpfile(); // stdio.h + + if (NULL == memory_stream) { + printf("> Error creating a memory stream.\n"); + return 1; + } + + char buffer[BUF_SIZE] = { 0 }; + size_t data_read_size = BUF_SIZE; + + do { + data_read_size = wasi_env_read_stdout(wasi_env, buffer, BUF_SIZE); + if (data_read_size == -1) { + printf("failed to read stdout: %s\n", strerror(errno)); + print_wasmer_error(); + return -1; + } + + if (data_read_size > 0) { + fwrite(buffer, sizeof(char), data_read_size, memory_stream); + } + } while (BUF_SIZE == data_read_size); + + // print memory_stream + rewind(memory_stream); + fputs("WASI Stdout: ", stdout); + char buffer2[256]; + while (!feof(memory_stream)) { + if (fgets(buffer2, 256, memory_stream) == NULL) break; + fputs(buffer2, stdout); + } + fputs("\n", stdout); + fclose(memory_stream); + } + + + wasm_extern_vec_delete(&exports); + wasm_extern_vec_delete(&imports); + + // Shut down. + printf("Shutting down...\n"); + wasm_func_delete(run_func); + wasi_env_delete(wasi_env); + wasm_module_delete(module); + wasm_instance_delete(instance); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + + #endif // WASMER_WASI_ENABLED + + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/wasmer-capi-examples-runner/.gitignore b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/wasmer-capi-examples-runner/.gitignore new file mode 100644 index 0000000..1b04d33 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/wasmer-capi-examples-runner/.gitignore @@ -0,0 +1,4 @@ +*.bat +*.ilk +*.pdb +*.exe \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/wasmer-capi-examples-runner/Cargo.toml b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/wasmer-capi-examples-runner/Cargo.toml new file mode 100644 index 0000000..d43a4a2 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/examples/wasmer-capi-examples-runner/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "wasmer-capi-examples-runner" +version = "4.2.8" +edition = "2021" +license = "MIT" +description = "wasmer-capi-examples-runner" + +[dependencies] +cc = "1.0" +target-lexicon = "0.11" +regex = "1.6" +walkdir = "2.3.2" \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/error.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/error.rs new file mode 100644 index 0000000..adb9884 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/error.rs @@ -0,0 +1,158 @@ +//! Utilities to read errors. +//! +//! Only one error can be registered at a time. Error are registered +//! by Rust only, and are usually read by C or C++. +//! +//! Reading an error from C or C++ happens in 2 steps: Getting the +//! error's length with [`wasmer_last_error_length`], and then reading +//! the actual error with [`wasmer_last_error_message`]. +//! +//! # Example +//! +//! ```rust +//! # use wasmer_inline_c::assert_c; +//! # fn main() { +//! # (assert_c! { +//! # #include "tests/wasmer.h" +//! # +//! int main() { +//! // Create an invalid WebAssembly module from a WAT definition, +//! // it will generate an error! +//! wasm_byte_vec_t wat; +//! wasmer_byte_vec_new_from_string(&wat, "(foobar)"); +//! wasm_byte_vec_t wasm; +//! wat2wasm(&wat, &wasm); +//! +//! int error_length = wasmer_last_error_length(); +//! +//! // There is an error! +//! assert(error_length > 0); +//! +//! char *error_message = malloc(error_length); +//! wasmer_last_error_message(error_message, error_length); +//! printf("Error message: %s\n", error_message); +//! +//! // Side note: The error has now been cleared on the Rust side! +//! assert(wasmer_last_error_length() == 0); +//! +//! // Free everything. +//! free(error_message); +//! wasm_byte_vec_delete(&wasm); +//! wasm_byte_vec_delete(&wat); +//! +//! return 0; +//! } +//! # }) +//! # .success(); +//! # } +//! ``` + +use libc::{c_char, c_int}; +use std::cell::RefCell; +use std::fmt::Display; +use std::ptr::{self, NonNull}; +use std::slice; + +thread_local! { + static LAST_ERROR: RefCell> = RefCell::new(None); +} + +/// Rust function to register a new error. +/// +/// # Example +/// +/// ```rust,no_run +/// # use wasmer::error::update_last_error; +/// +/// update_last_error("Hello, World!"); +/// ``` +pub fn update_last_error(err: E) { + LAST_ERROR.with(|prev| { + *prev.borrow_mut() = Some(err.to_string()); + }); +} + +/// Retrieve the most recent error, clearing it in the process. +pub(crate) fn take_last_error() -> Option { + LAST_ERROR.with(|prev| prev.borrow_mut().take()) +} + +/// Gets the length in bytes of the last error if any, zero otherwise. This +/// includes th NUL terminator byte. +/// +/// This can be used to dynamically allocate a buffer with the correct number of +/// bytes needed to store a message. +/// +/// # Example +/// +/// See this module's documentation to get a complete example. +// TODO(Amanieu): This should use size_t +#[no_mangle] +pub extern "C" fn wasmer_last_error_length() -> c_int { + LAST_ERROR.with(|prev| match *prev.borrow() { + Some(ref err) => err.len() as c_int + 1, + None => 0, + }) +} + +/// Gets the last error message if any into the provided buffer +/// `buffer` up to the given `length`. +/// +/// The `length` parameter must be large enough to store the last +/// error message. Ideally, the value should come from +/// [`wasmer_last_error_length`]. +/// +/// The function returns the length of the string in bytes, `-1` if an +/// error occurs. Potential errors are: +/// +/// * The `buffer` is a null pointer, +/// * The `buffer` is too small to hold the error message. +/// +/// Note: The error message always has a trailing NUL character. +/// +/// Important note: If the provided `buffer` is non-null, once this +/// function has been called, regardless whether it fails or succeeds, +/// the error is cleared. +/// +/// # Example +/// +/// See this module's documentation to get a complete example. +// TODO(Amanieu): This should use size_t +#[no_mangle] +pub unsafe extern "C" fn wasmer_last_error_message( + buffer: Option>, + length: c_int, +) -> c_int { + let buffer = if let Some(buffer_inner) = buffer { + buffer_inner + } else { + // buffer pointer is null + return -1; + }; + + let error_message = match take_last_error() { + Some(err) => err, + None => return 0, + }; + + let length = length as usize; + + if error_message.len() >= length { + // buffer is too small to hold the error message + return -1; + } + + let buffer = slice::from_raw_parts_mut(buffer.cast::().as_ptr(), length); + + ptr::copy_nonoverlapping( + error_message.as_ptr(), + buffer.as_mut_ptr(), + error_message.len(), + ); + + // Add a trailing null so people using the string as a `char *` don't + // accidentally read into garbage. + buffer[error_message.len()] = 0; + + error_message.len() as c_int + 1 +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/lib.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/lib.rs new file mode 100644 index 0000000..5b59ab7 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/lib.rs @@ -0,0 +1,32 @@ +//! Wasmer C API. +//! +//! [Wasmer](https://github.com/wasmerio/wasmer) is the leading +//! WebAssembly runtime. Wasmer is written in Rust; this crate +//! exposes its C and C++ bindings. +//! +//! This crate provides an API that follows the [official WebAssembly +//! C API](https://github.com/WebAssembly/wasm-c-api). This standard +//! can be characterized as a _living standard_. The API is not yet +//! stable, even though it shows maturity over time. It is described +//! by the `wasm.h` C header file. However, this crate API provides +//! some extensions, like the `wasi_*` or `wasmer_*` types and +//! functions, which aren't yet defined by the standard. The +//! `wasmer.h` header file already depends on the `wasm.h` +//! file. A copy lands in this repository for the sake of simplicity. + +#![doc(html_favicon_url = "https://wasmer.io/images/icons/favicon-32x32.png")] +#![doc(html_logo_url = "https://github.com/wasmerio.png?size=200")] +#![deny( + dead_code, + unused_imports, + unused_variables, + unused_unsafe, + unreachable_patterns +)] +// Because this crate exposes a lot of C APIs which are unsafe by definition, +// we allow unsafe without explicit safety documentation for each of them. +#![allow(clippy::missing_safety_doc)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +pub mod error; +pub mod wasm_c_api; diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/DEVELOPMENT.md b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/DEVELOPMENT.md new file mode 100644 index 0000000..048a82c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/DEVELOPMENT.md @@ -0,0 +1,137 @@ +# Development Notes + +## Ownerships + +The [`wasm.h`] header thankfully defines the [`own`] “annotation”. It +specifies _who_ owns _what_. For example, in the following code: + +```c +WASM_API_EXTERN own wasm_importtype_t* wasm_importtype_new( + own wasm_name_t* module, own wasm_name_t* name, own wasm_externtype_t*); +``` + +We must read that `wasm_importtype_new` takes the ownership of all its +three arguments. This function is then responsible to free those +data. We must also read that the returned value is owned by the caller +of this function. + +### Rust Pattern + +This ownership property translates well in Rust. We have decided to +use the `Box` type to represent an owned pointer. `Box` drops +its content when it's dropped. + +However, vectors (such as `wasm_name_t`) are a special case: for those, `own` +means that the ownership of contents of the vector are transfered, not the +allocation of the vector type itself. Hence these are represented using +`&mut T` instead. + +Consequently, apart from other patterns, the code above can be written +as follows in Rust: + +```rust +#[no_mangle] +pub extern "C" fn wasm_importtype_new( + module: &mut wasm_name_t, + name: &mut wasm_name_t, + extern_type: Box, +) -> Box { + â€Ļ +} +``` + +By reading the code, it is clear that `wasm_importtype_new` takes the +ownership for `module`, `name`, and `extern_type`, and that the result +is owned by the caller. + +## `const *T` + +A constant pointer can be interpreted in C as an immutable +pointer. Without the [`own`] annotation, it means the ownership is not +transfered anywhere (see [the Ownerships Section][#ownerships]). + +### Rust Pattern + +`const *T` translates to Rust as `&T`, it's a reference. + +## Null Pointer + +The [`wasm.h`] header does not say anything about null pointer. The +behavior we agreed on in that passing a null pointer where it is not +expected (i.e. everywhere) will make the function to return null too +with no error. + +### Rust Pattern + +A nice type property in Rust is that it is possible to write +`Option>` to nicely handle null pointer of kind `T`. For an +argument, it translates as follows: + +* When the given pointer is null, the argument holds `None`, +* When the given pointer is not null, the arguments holds + `Some(NonNull)`. + +Considering [the Ownerships Section][#ownerships], if the pointer is +owned, we can also write `Option>`. This pattern is largely +used in this codebase to represent a “nullable” owned +pointer. Consequently, a code like: + +```c +WASM_API_EXTERN own wasm_importtype_t* wasm_importtype_new( + own wasm_name_t* module, own wasm_name_t* name, own wasm_externtype_t*); +``` + +translates into Rust as: + +```rust +#[no_mangle] +pub extern "C" fn wasm_importtype_new( + module: Option<&mut wasm_name_t>, + name: Option<&mut wasm_name_t>, + extern_type: Option>, +) -> Option> { + Some(Box::new(wasm_importtype_t { + name: name?.take().into(), + module: module?.take().into(), + extern_type: extern_type?, + })) +} +``` + +What `name?` (and others) means? It is basically [the `Try` trait +implemented for +`Option`](https://doc.rust-lang.org/std/ops/trait.Try.html#impl-Try): +It returns `None` if the value is `None`, otherwise it unwraps the +`Option`. + +Because the function returns `Option>`, `None` represents a +null pointer. + +Considering [the `const *T` Section][#const-t], if the pointer is not +owned, we can either write `Option>` or `Option<&T>`. It +has been decided to use the second pattern in all the codebase. + +## Destructors + +The [`wasm.h`] defines `wasm_*_delete` functions. It represents destructors. + +### Rust Pattern + +The destructors in Rust translate as follow: + +```rust +#[no_mangle] +pub unsafe extern "C" fn wasm_*_delete(_: Option>) {} +``` + +`Box` will take the ownership of the value. It means that Rust will +drop it automatically as soon as it goes out of the +scope. Consequently, the “C destructors” really are the “Rust +destructors”. + +The `Option` is here to handle the situation where a null pointer is +passed to the destructor. + + +[`own`]: https://github.com/wasmerio/wasmer/blob/f548f268f2335693b97ad7ca08af72c320daf59a/lib/c-api/tests/wasm_c_api/wasm-c-api/include/wasm.h#L46-L65 +[`wasm.h`]: https://github.com/wasmerio/wasmer/blob/f548f268f2335693b97ad7ca08af72c320daf59a/lib/c-api/tests/wasm_c_api/wasm-c-api/include/wasm.h diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/engine.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/engine.rs new file mode 100644 index 0000000..f5705d8 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/engine.rs @@ -0,0 +1,486 @@ +#[cfg(feature = "compiler")] +pub use super::unstable::engine::wasmer_is_compiler_available; +pub use super::unstable::engine::{wasm_config_set_features, wasm_config_set_target}; +use super::unstable::features::wasmer_features_t; +#[cfg(feature = "middlewares")] +pub use super::unstable::middlewares::wasm_config_push_middleware; +#[cfg(feature = "middlewares")] +use super::unstable::middlewares::wasmer_middleware_t; +use super::unstable::target_lexicon::wasmer_target_t; +use crate::error::update_last_error; +use cfg_if::cfg_if; +#[cfg(not(any(feature = "compiler", feature = "compiler-headless")))] +use wasmer_api::Engine; +#[cfg(any(feature = "compiler", feature = "compiler-headless"))] +use wasmer_compiler::{Engine, EngineBuilder}; + +/// Kind of compilers that can be used by the engines. +/// +/// This is a Wasmer-specific type with Wasmer-specific functions for +/// manipulating it. +#[cfg(feature = "compiler")] +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub enum wasmer_compiler_t { + /// Variant to represent the Cranelift compiler. See the + /// [`wasmer_compiler_cranelift`] Rust crate. + CRANELIFT = 0, + + /// Variant to represent the LLVM compiler. See the + /// [`wasmer_compiler_llvm`] Rust crate. + LLVM = 1, + + /// Variant to represent the Singlepass compiler. See the + /// [`wasmer_compiler_singlepass`] Rust crate. + SINGLEPASS = 2, +} + +#[cfg(feature = "compiler")] +impl Default for wasmer_compiler_t { + fn default() -> Self { + cfg_if! { + if #[cfg(feature = "cranelift")] { + Self::CRANELIFT + } else if #[cfg(feature = "llvm")] { + Self::LLVM + } else if #[cfg(feature = "singlepass")] { + Self::SINGLEPASS + } else { + compile_error!("Please enable one of the compiler backends") + } + } + } +} + +/// Kind of engines that can be used by the store. +/// +/// This is a Wasmer-specific type with Wasmer-specific functions for +/// manipulating it. +#[derive(Debug, Copy, Clone)] +#[repr(C)] +#[allow(non_camel_case_types)] +pub enum wasmer_engine_t { + /// Variant to represent the Universal engine. See the + /// [`wasmer_engine_universal`] Rust crate. + UNIVERSAL = 0, +} + +impl Default for wasmer_engine_t { + fn default() -> Self { + Self::UNIVERSAL + } +} + +/// A configuration holds the compiler and the engine used by the store. +/// +/// cbindgen:ignore +#[derive(Debug, Default)] +#[repr(C)] +pub struct wasm_config_t { + engine: wasmer_engine_t, + #[cfg(feature = "compiler")] + compiler: wasmer_compiler_t, + #[cfg(feature = "middlewares")] + pub(super) middlewares: Vec, + pub(super) nan_canonicalization: bool, + pub(super) features: Option>, + pub(super) target: Option>, +} + +/// Create a new default Wasmer configuration. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the configuration. +/// wasm_config_t* config = wasm_config_new(); +/// +/// // Create the engine. +/// wasm_engine_t* engine = wasm_engine_new_with_config(config); +/// +/// // Check we have an engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +/// +/// cbindgen:ignore +#[no_mangle] +pub extern "C" fn wasm_config_new() -> Box { + Box::::default() +} + +/// Delete a Wasmer config object. +/// +/// This function does not need to be called if `wasm_engine_new_with_config` or +/// another function that takes ownership of the `wasm_config_t` is called. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the configuration. +/// wasm_config_t* config = wasm_config_new(); +/// +/// // Delete the configuration +/// wasm_config_delete(config); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +/// cbindgen:ignore +#[no_mangle] +pub extern "C" fn wasm_config_delete(_config: Option>) {} + +/// Updates the configuration to specify a particular compiler to use. +/// +/// This is a Wasmer-specific function. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the configuration. +/// wasm_config_t* config = wasm_config_new(); +/// +/// // Use the Cranelift compiler, if available. +/// if (wasmer_is_compiler_available(CRANELIFT)) { +/// wasm_config_set_compiler(config, CRANELIFT); +/// } +/// // Or maybe LLVM? +/// else if (wasmer_is_compiler_available(LLVM)) { +/// wasm_config_set_compiler(config, LLVM); +/// } +/// // Or maybe Singlepass? +/// else if (wasmer_is_compiler_available(SINGLEPASS)) { +/// wasm_config_set_compiler(config, SINGLEPASS); +/// } +/// // OK, let's run with no particular compiler. +/// +/// // Create the engine. +/// wasm_engine_t* engine = wasm_engine_new_with_config(config); +/// +/// // Check we have an engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[cfg(feature = "compiler")] +#[no_mangle] +pub extern "C" fn wasm_config_set_compiler( + config: &mut wasm_config_t, + compiler: wasmer_compiler_t, +) { + config.compiler = compiler; +} + +/// Updates the configuration to specify a particular engine to use. +/// +/// This is a Wasmer-specific function. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the configuration. +/// wasm_config_t* config = wasm_config_new(); +/// +/// // Create the engine. +/// wasm_engine_t* engine = wasm_engine_new_with_config(config); +/// +/// // Check we have an engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[no_mangle] +pub extern "C" fn wasm_config_set_engine(config: &mut wasm_config_t, engine: wasmer_engine_t) { + config.engine = engine; +} + +/// An engine is used by the store to drive the compilation and the +/// execution of a WebAssembly module. +/// +/// cbindgen:ignore +#[repr(C)] +pub struct wasm_engine_t { + pub(crate) inner: Engine, +} + +#[cfg(feature = "compiler")] +use wasmer_api::CompilerConfig; + +#[cfg(all(feature = "compiler", any(feature = "compiler", feature = "dylib")))] +fn get_default_compiler_config() -> Box { + cfg_if! { + if #[cfg(feature = "cranelift")] { + Box::::default() + } else if #[cfg(feature = "llvm")] { + Box::::default() + } else if #[cfg(feature = "singlepass")] { + Box::::default() + } else { + compile_error!("Please enable one of the compiler backends") + } + } +} + +cfg_if! { + if #[cfg(feature = "compiler")] { + /// Creates a new Universal engine with the default compiler. + /// + /// # Example + /// + /// See [`wasm_engine_delete`]. + /// + /// cbindgen:ignore + #[no_mangle] + pub extern "C" fn wasm_engine_new() -> Box { + let compiler_config: Box = get_default_compiler_config(); + let engine: Engine = EngineBuilder::new(compiler_config).engine(); + Box::new(wasm_engine_t { inner: engine }) + } + } else if #[cfg(feature = "compiler-headless")] { + /// Creates a new headless Universal engine. + /// + /// # Example + /// + /// See [`wasm_engine_delete`]. + /// + /// cbindgen:ignore + #[no_mangle] + pub extern "C" fn wasm_engine_new() -> Box { + let engine: Engine = EngineBuilder::headless().engine(); + Box::new(wasm_engine_t { inner: engine }) + } + } else if #[cfg(feature = "jsc")] { + /// Creates the JavascriptCore Engine. + /// + /// # Example + /// + /// See [`wasm_engine_delete`]. + /// + /// cbindgen:ignore + #[no_mangle] + pub extern "C" fn wasm_engine_new() -> Box { + let engine: Engine = Engine::default(); + Box::new(wasm_engine_t { inner: engine }) + } + } else { + /// Creates a new unknown engine, i.e. it will panic with an error message. + /// + /// # Example + /// + /// See [`wasm_engine_delete`]. + /// + /// cbindgen:ignore + #[no_mangle] + pub extern "C" fn wasm_engine_new() -> Box { + unimplemented!("No engine attached; You might want to recompile `wasmer_c_api` with for example `--feature compiler`"); + } + } +} + +/// Deletes an engine. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create a default engine. +/// wasm_engine_t* engine = wasm_engine_new(); +/// +/// // Check we have an engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +/// +/// cbindgen:ignore +#[no_mangle] +pub unsafe extern "C" fn wasm_engine_delete(_engine: Option>) {} + +/// Creates an engine with a particular configuration. +/// +/// # Example +/// +/// See [`wasm_config_new`]. +/// +/// cbindgen:ignore +#[no_mangle] +pub extern "C" fn wasm_engine_new_with_config( + config: Option>, +) -> Option> { + #[allow(dead_code)] + fn return_with_error(msg: &str) -> Option> { + update_last_error(msg); + None + } + + #[allow(unused)] + let config = config?; + #[cfg(not(any(feature = "compiler", feature = "compiler-headless")))] + return return_with_error("Wasmer has not been compiled with the `compiler` feature."); + cfg_if! { + if #[cfg(feature = "compiler")] { + #[allow(unused_mut)] + let mut compiler_config: Box = match config.compiler { + wasmer_compiler_t::CRANELIFT => { + cfg_if! { + if #[cfg(feature = "cranelift")] { + Box::::default() + } else { + return return_with_error("Wasmer has not been compiled with the `cranelift` feature."); + } + } + }, + wasmer_compiler_t::LLVM => { + cfg_if! { + if #[cfg(feature = "llvm")] { + Box::::default() + } else { + return return_with_error("Wasmer has not been compiled with the `llvm` feature."); + } + } + }, + wasmer_compiler_t::SINGLEPASS => { + cfg_if! { + if #[cfg(feature = "singlepass")] { + Box::::default() + } else { + return return_with_error("Wasmer has not been compiled with the `singlepass` feature."); + } + } + }, + }; + + #[cfg(feature = "middlewares")] + for middleware in config.middlewares { + compiler_config.push_middleware(middleware.inner); + } + + if config.nan_canonicalization { + compiler_config.canonicalize_nans(true); + } + + let inner: Engine = + { + let mut builder = EngineBuilder::new(compiler_config); + + if let Some(target) = config.target { + builder = builder.set_target(Some(target.inner)); + } + + if let Some(features) = config.features { + builder = builder.set_features(Some(features.inner)); + } + + builder.engine() + }; + Some(Box::new(wasm_engine_t { inner })) + } else { + #[cfg(feature = "compiler-headless")] + let inner: Engine = + { + let mut builder = EngineBuilder::headless(); + + if let Some(target) = config.target { + builder = builder.set_target(Some(target.inner)); + } + + if let Some(features) = config.features { + builder = builder.set_features(Some(features.inner)); + } + + builder.engine() + }; + #[cfg(not(any(feature = "compiler-headless", feature="compiler")))] + let inner: Engine = Engine::default(); + + Some(Box::new(wasm_engine_t { inner })) + } + } +} + +#[cfg(test)] +mod tests { + #[cfg(not(target_os = "windows"))] + use inline_c::assert_c; + #[cfg(target_os = "windows")] + use wasmer_inline_c::assert_c; + + #[cfg_attr(coverage, ignore)] + #[test] + fn test_engine_new() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_engine_t* engine = wasm_engine_new(); + assert(engine); + + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/externals/function.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/externals/function.rs new file mode 100644 index 0000000..e72c277 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/externals/function.rs @@ -0,0 +1,256 @@ +use super::super::store::wasm_store_t; +use super::super::trap::wasm_trap_t; +use super::super::types::{wasm_functype_t, wasm_valkind_enum}; +use super::super::value::{wasm_val_inner, wasm_val_t, wasm_val_vec_t}; +use super::wasm_extern_t; +use crate::wasm_c_api::function_env::FunctionCEnv; +use libc::c_void; +use std::convert::TryInto; +use std::mem::MaybeUninit; +use std::sync::{Arc, Mutex}; +use wasmer_api::{Extern, Function, FunctionEnv, FunctionEnvMut, RuntimeError, Value}; + +#[derive(Clone)] +#[allow(non_camel_case_types)] +#[repr(C)] +pub struct wasm_func_t { + pub(crate) extern_: wasm_extern_t, +} + +impl wasm_func_t { + pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_func_t> { + match &e.inner { + Extern::Function(_) => Some(unsafe { &*(e as *const _ as *const _) }), + _ => None, + } + } +} + +#[allow(non_camel_case_types)] +pub type wasm_func_callback_t = unsafe extern "C" fn( + args: &wasm_val_vec_t, + results: &mut wasm_val_vec_t, +) -> Option>; + +#[allow(non_camel_case_types)] +pub type wasm_func_callback_with_env_t = unsafe extern "C" fn( + env: *mut c_void, + args: &wasm_val_vec_t, + results: &mut wasm_val_vec_t, +) -> Option>; + +#[allow(non_camel_case_types)] +pub type wasm_env_finalizer_t = unsafe extern "C" fn(*mut c_void); + +#[no_mangle] +pub unsafe extern "C" fn wasm_func_new( + store: Option<&mut wasm_store_t>, + function_type: Option<&wasm_functype_t>, + callback: Option, +) -> Option> { + let function_type = function_type?; + let callback = callback?; + let store = store?; + let mut store_mut = store.inner.store_mut(); + + let func_sig = &function_type.inner().function_type; + let num_rets = func_sig.results().len(); + let inner_callback = move |mut _env: FunctionEnvMut<'_, FunctionCEnv>, + args: &[Value]| + -> Result, RuntimeError> { + let processed_args: wasm_val_vec_t = args + .iter() + .map(TryInto::try_into) + .collect::, _>>() + .expect("Argument conversion failed") + .into(); + + let mut results: wasm_val_vec_t = vec![ + wasm_val_t { + kind: wasm_valkind_enum::WASM_I64 as _, + of: wasm_val_inner { int64_t: 0 }, + }; + num_rets + ] + .into(); + + let trap = callback(&processed_args, &mut results); + + if let Some(trap) = trap { + return Err(trap.inner); + } + + let processed_results = results + .take() + .into_iter() + .map(TryInto::try_into) + .collect::, _>>() + .expect("Result conversion failed"); + + Ok(processed_results) + }; + let env = FunctionEnv::new(&mut store_mut, FunctionCEnv::default()); + let function = Function::new_with_env(&mut store_mut, &env, func_sig, inner_callback); + Some(Box::new(wasm_func_t { + extern_: wasm_extern_t::new(store.inner.clone(), function.into()), + })) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_func_new_with_env( + store: Option<&mut wasm_store_t>, + function_type: Option<&wasm_functype_t>, + callback: Option, + env: *mut c_void, + env_finalizer: Option, +) -> Option> { + let function_type = function_type?; + let callback = callback?; + let store = store?; + let mut store_mut = store.inner.store_mut(); + + let func_sig = &function_type.inner().function_type; + let num_rets = func_sig.results().len(); + + #[derive(Clone)] + #[repr(C)] + struct WrapperEnv { + env: FunctionCEnv, + env_finalizer: Arc>>, + } + + // Only relevant when using multiple threads in the C API; + // Synchronization will be done via the C API / on the C side. + unsafe impl Send for WrapperEnv {} + unsafe impl Sync for WrapperEnv {} + + impl Drop for WrapperEnv { + fn drop(&mut self) { + if let Ok(mut guard) = self.env_finalizer.lock() { + if Arc::strong_count(&self.env_finalizer) == 1 { + if let Some(env_finalizer) = guard.take() { + unsafe { (env_finalizer)(self.env.as_ptr()) }; + } + } + } + } + } + let inner_callback = move |env: FunctionEnvMut<'_, WrapperEnv>, + args: &[Value]| + -> Result, RuntimeError> { + let processed_args: wasm_val_vec_t = args + .iter() + .map(TryInto::try_into) + .collect::, _>>() + .expect("Argument conversion failed") + .into(); + + let mut results: wasm_val_vec_t = vec![ + wasm_val_t { + kind: wasm_valkind_enum::WASM_I64 as _, + of: wasm_val_inner { int64_t: 0 }, + }; + num_rets + ] + .into(); + + let trap = callback(env.data().env.as_ptr(), &processed_args, &mut results); + + if let Some(trap) = trap { + return Err(trap.inner); + } + + let processed_results = results + .take() + .into_iter() + .map(TryInto::try_into) + .collect::, _>>() + .expect("Result conversion failed"); + + Ok(processed_results) + }; + let env = FunctionEnv::new( + &mut store_mut, + WrapperEnv { + env: FunctionCEnv::new(c_try!( + std::ptr::NonNull::new(env), + "Function environment cannot be a null pointer." + )), + env_finalizer: Arc::new(Mutex::new(env_finalizer)), + }, + ); + let function = Function::new_with_env(&mut store_mut, &env, func_sig, inner_callback); + Some(Box::new(wasm_func_t { + extern_: wasm_extern_t::new(store.inner.clone(), function.into()), + })) +} + +#[no_mangle] +pub extern "C" fn wasm_func_copy(func: &wasm_func_t) -> Box { + Box::new(func.clone()) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_func_delete(_func: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn wasm_func_call( + func: Option<&mut wasm_func_t>, + args: Option<&wasm_val_vec_t>, + results: &mut wasm_val_vec_t, +) -> Option> { + let func = func?; + let args = args?; + let mut store = func.extern_.store.clone(); + let mut store_mut = store.store_mut(); + let params = args + .as_slice() + .iter() + .cloned() + .map(TryInto::try_into) + .collect::, _>>() + .expect("Arguments conversion failed"); + + match func.extern_.function().call(&mut store_mut, ¶ms) { + Ok(wasm_results) => { + for (slot, val) in results + .as_uninit_slice() + .iter_mut() + .zip(wasm_results.iter()) + { + *slot = MaybeUninit::new(val.try_into().expect("Results conversion failed")); + } + + None + } + Err(e) => Some(Box::new(e.into())), + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_func_param_arity(func: &wasm_func_t) -> usize { + func.extern_ + .function() + .ty(&func.extern_.store.store()) + .params() + .len() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_func_result_arity(func: &wasm_func_t) -> usize { + func.extern_ + .function() + .ty(&func.extern_.store.store()) + .results() + .len() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_func_type( + func: Option<&wasm_func_t>, +) -> Option> { + let func = func?; + Some(Box::new(wasm_functype_t::new( + func.extern_.function().ty(&func.extern_.store.store()), + ))) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/externals/global.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/externals/global.rs new file mode 100644 index 0000000..1c1519d --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/externals/global.rs @@ -0,0 +1,178 @@ +use super::super::store::wasm_store_t; +use super::super::types::wasm_globaltype_t; +use super::super::value::wasm_val_t; +use super::wasm_extern_t; +use std::convert::TryInto; +use wasmer_api::{Extern, Global, Value}; + +#[allow(non_camel_case_types)] +#[repr(C)] +#[derive(Clone)] +pub struct wasm_global_t { + pub(crate) extern_: wasm_extern_t, +} + +impl wasm_global_t { + pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_global_t> { + match &e.inner { + Extern::Global(_) => Some(unsafe { &*(e as *const _ as *const _) }), + _ => None, + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_global_new( + store: Option<&mut wasm_store_t>, + global_type: Option<&wasm_globaltype_t>, + val: Option<&wasm_val_t>, +) -> Option> { + let global_type = global_type?; + let store = store?; + let mut store_mut = store.inner.store_mut(); + let val = val?; + + let global_type = &global_type.inner().global_type; + let wasm_val = val.try_into().ok()?; + let global = if global_type.mutability.is_mutable() { + Global::new_mut(&mut store_mut, wasm_val) + } else { + Global::new(&mut store_mut, wasm_val) + }; + Some(Box::new(wasm_global_t { + extern_: wasm_extern_t::new(store.inner.clone(), global.into()), + })) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_global_delete(_global: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn wasm_global_copy(global: &wasm_global_t) -> Box { + // do shallow copy + Box::new(global.clone()) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_global_get( + global: &mut wasm_global_t, + // own + out: &mut wasm_val_t, +) { + let value = global + .extern_ + .global() + .get(&mut global.extern_.store.store_mut()); + *out = value.try_into().unwrap(); +} + +/// Note: This function returns nothing by design but it can raise an +/// error if setting a new value fails. +#[no_mangle] +pub unsafe extern "C" fn wasm_global_set(global: &mut wasm_global_t, val: &wasm_val_t) { + let value: Value = val.try_into().unwrap(); + c_try!(global + .extern_ + .global() + .set(&mut global.extern_.store.store_mut(), value); otherwise ()); +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_global_same( + wasm_global1: &wasm_global_t, + wasm_global2: &wasm_global_t, +) -> bool { + wasm_global1.extern_.global() == wasm_global2.extern_.global() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_global_type( + global: Option<&wasm_global_t>, +) -> Option> { + let global = global?; + Some(Box::new(wasm_globaltype_t::new( + global.extern_.global().ty(&global.extern_.store.store()), + ))) +} + +#[cfg(test)] +mod tests { + #[cfg(not(target_os = "windows"))] + use inline_c::assert_c; + #[cfg(target_os = "windows")] + use wasmer_inline_c::assert_c; + + #[cfg_attr(coverage, ignore)] + #[test] + fn test_set_host_global_immutable() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + wasm_val_t forty_two = WASM_F32_VAL(42); + wasm_val_t forty_three = WASM_F32_VAL(43); + + wasm_valtype_t* valtype = wasm_valtype_new_i32(); + wasm_globaltype_t* global_type = wasm_globaltype_new(valtype, WASM_CONST); + wasm_global_t* global = wasm_global_new(store, global_type, &forty_two); + + wasm_globaltype_delete(global_type); + + wasm_global_set(global, &forty_three); + + assert(wasmer_last_error_length() > 0); + + wasm_global_delete(global); + wasm_store_delete(store); + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } + + #[cfg_attr(coverage, ignore)] + #[test] + fn test_set_guest_global_immutable() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + wasm_byte_vec_t wat; + wasmer_byte_vec_new_from_string(&wat, "(module (global $global (export \"global\") f32 (f32.const 1)))"); + wasm_byte_vec_t wasm_bytes; + wat2wasm(&wat, &wasm_bytes); + wasm_module_t* module = wasm_module_new(store, &wasm_bytes); + wasm_extern_vec_t import_object = WASM_EMPTY_VEC; + wasm_instance_t* instance = wasm_instance_new(store, module, &import_object, NULL); + + wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + wasm_global_t* global = wasm_extern_as_global(exports.data[0]); + + wasm_val_t forty_two = WASM_F32_VAL(42); + wasm_global_set(global, &forty_two); + + printf("%d", wasmer_last_error_length()); + assert(wasmer_last_error_length() > 0); + + wasm_instance_delete(instance); + wasm_byte_vec_delete(&wasm_bytes); + wasm_byte_vec_delete(&wat); + wasm_extern_vec_delete(&exports); + wasm_store_delete(store); + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/externals/memory.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/externals/memory.rs new file mode 100644 index 0000000..a919ec9 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/externals/memory.rs @@ -0,0 +1,104 @@ +use super::super::types::wasm_memorytype_t; +use super::{super::store::wasm_store_t, wasm_extern_t}; +use wasmer_api::{Extern, Memory, Pages}; + +#[allow(non_camel_case_types)] +#[repr(C)] +#[derive(Clone)] +pub struct wasm_memory_t { + pub(crate) extern_: wasm_extern_t, +} + +impl wasm_memory_t { + pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_memory_t> { + match &e.inner { + Extern::Memory(_) => Some(unsafe { &*(e as *const _ as *const _) }), + _ => None, + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memory_new( + store: Option<&mut wasm_store_t>, + memory_type: Option<&wasm_memorytype_t>, +) -> Option> { + let memory_type = memory_type?; + let store = store?; + let mut store_mut = store.inner.store_mut(); + let memory_type = memory_type.inner().memory_type; + let memory = c_try!(Memory::new(&mut store_mut, memory_type)); + Some(Box::new(wasm_memory_t { + extern_: wasm_extern_t::new(store.inner.clone(), memory.into()), + })) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memory_delete(_memory: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memory_copy(memory: &wasm_memory_t) -> Box { + // do shallow copy + Box::new(memory.clone()) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memory_same( + wasm_memory1: &wasm_memory_t, + wasm_memory2: &wasm_memory_t, +) -> bool { + wasm_memory1.extern_.memory() == wasm_memory2.extern_.memory() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memory_type( + memory: Option<&wasm_memory_t>, +) -> Option> { + let memory = memory?; + Some(Box::new(wasm_memorytype_t::new( + memory.extern_.memory().ty(&memory.extern_.store.store()), + ))) +} + +// get a raw pointer into bytes +#[no_mangle] +pub unsafe extern "C" fn wasm_memory_data(memory: &mut wasm_memory_t) -> *mut u8 { + memory + .extern_ + .memory() + .view(&memory.extern_.store.store()) + .data_ptr() +} + +// size in bytes +#[no_mangle] +pub unsafe extern "C" fn wasm_memory_data_size(memory: &wasm_memory_t) -> usize { + memory + .extern_ + .memory() + .view(&memory.extern_.store.store()) + .size() + .bytes() + .0 +} + +// size in pages +#[no_mangle] +pub unsafe extern "C" fn wasm_memory_size(memory: &wasm_memory_t) -> u32 { + memory + .extern_ + .memory() + .view(&memory.extern_.store.store()) + .size() + .0 as _ +} + +// delta is in pages +#[no_mangle] +pub unsafe extern "C" fn wasm_memory_grow(memory: &mut wasm_memory_t, delta: u32) -> bool { + memory + .extern_ + .memory() + .grow(&mut memory.extern_.store.store_mut(), Pages(delta)) + .is_ok() +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/externals/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/externals/mod.rs new file mode 100644 index 0000000..a6a2e4e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/externals/mod.rs @@ -0,0 +1,193 @@ +mod function; +mod global; +mod memory; +mod table; + +use super::store::StoreRef; +// use super::types::{wasm_externkind_enum, wasm_externkind_t}; +pub use function::*; +pub use global::*; +pub use memory::*; +pub use table::*; +use wasmer_api::{Extern, ExternType, Function, Global, Memory, Table}; + +#[allow(non_camel_case_types)] +#[derive(Clone)] +pub struct wasm_extern_t { + pub(crate) inner: Extern, + pub(crate) store: StoreRef, +} + +impl wasm_extern_t { + pub(crate) fn new(store: StoreRef, inner: Extern) -> Self { + Self { inner, store } + } + + pub(crate) fn global(&self) -> Global { + match &self.inner { + Extern::Global(g) => g.clone(), + _ => unsafe { std::hint::unreachable_unchecked() }, + } + } + + pub(crate) fn function(&self) -> Function { + match &self.inner { + Extern::Function(f) => f.clone(), + _ => unsafe { std::hint::unreachable_unchecked() }, + } + } + + pub(crate) fn table(&self) -> Table { + match &self.inner { + Extern::Table(t) => t.clone(), + _ => unsafe { std::hint::unreachable_unchecked() }, + } + } + + pub(crate) fn memory(&self) -> Memory { + match &self.inner { + Extern::Memory(m) => m.clone(), + _ => unsafe { std::hint::unreachable_unchecked() }, + } + } +} + +// #[no_mangle] +// pub extern "C" fn wasm_extern_kind(e: &wasm_extern_t) -> wasm_externkind_t { +// (match e.inner { +// Extern::Function(_) => wasm_externkind_enum::WASM_EXTERN_FUNC, +// Extern::Table(_) => wasm_externkind_enum::WASM_EXTERN_TABLE, +// Extern::Global(_) => wasm_externkind_enum::WASM_EXTERN_GLOBAL, +// Extern::Memory(_) => wasm_externkind_enum::WASM_EXTERN_MEMORY, +// }) as wasm_externkind_t +// } + +impl wasm_extern_t { + pub(crate) unsafe fn ty(&self) -> ExternType { + self.inner.ty(&self.store.store()) + } +} + +impl From for Extern { + fn from(other: wasm_extern_t) -> Self { + other.inner + } +} + +wasm_declare_boxed_vec!(extern); + +/// Copy a `wasm_extern_t`. +#[no_mangle] +pub unsafe extern "C" fn wasm_extern_copy(r#extern: &wasm_extern_t) -> Box { + Box::new(r#extern.clone()) +} + +/// Delete an extern. +#[no_mangle] +pub unsafe extern "C" fn wasm_extern_delete(_extern: Option>) {} + +#[no_mangle] +pub extern "C" fn wasm_func_as_extern(func: Option<&wasm_func_t>) -> Option<&wasm_extern_t> { + Some(&func?.extern_) +} + +#[no_mangle] +pub extern "C" fn wasm_global_as_extern(global: Option<&wasm_global_t>) -> Option<&wasm_extern_t> { + Some(&global?.extern_) +} + +#[no_mangle] +pub extern "C" fn wasm_memory_as_extern(memory: Option<&wasm_memory_t>) -> Option<&wasm_extern_t> { + Some(&memory?.extern_) +} + +#[no_mangle] +pub extern "C" fn wasm_table_as_extern(table: Option<&wasm_table_t>) -> Option<&wasm_extern_t> { + Some(&table?.extern_) +} + +#[no_mangle] +pub extern "C" fn wasm_extern_as_func(r#extern: Option<&wasm_extern_t>) -> Option<&wasm_func_t> { + wasm_func_t::try_from(r#extern?) +} + +#[no_mangle] +pub extern "C" fn wasm_extern_as_global( + r#extern: Option<&wasm_extern_t>, +) -> Option<&wasm_global_t> { + wasm_global_t::try_from(r#extern?) +} + +#[no_mangle] +pub extern "C" fn wasm_extern_as_memory( + r#extern: Option<&wasm_extern_t>, +) -> Option<&wasm_memory_t> { + wasm_memory_t::try_from(r#extern?) +} + +#[no_mangle] +pub extern "C" fn wasm_extern_as_table(r#extern: Option<&wasm_extern_t>) -> Option<&wasm_table_t> { + wasm_table_t::try_from(r#extern?) +} + +#[cfg(test)] +mod tests { + #[cfg(not(target_os = "windows"))] + use inline_c::assert_c; + #[cfg(target_os = "windows")] + use wasmer_inline_c::assert_c; + + #[cfg_attr(coverage, ignore)] + #[test] + fn test_extern_copy() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + wasm_byte_vec_t wat; + wasmer_byte_vec_new_from_string( + &wat, + "(module\n" + " (func (export \"function\")))" + ); + wasm_byte_vec_t wasm; + wat2wasm(&wat, &wasm); + + wasm_module_t* module = wasm_module_new(store, &wasm); + assert(module); + + wasm_extern_vec_t imports = WASM_EMPTY_VEC; + wasm_trap_t* trap = NULL; + + wasm_instance_t* instance = wasm_instance_new(store, module, &imports, &trap); + assert(instance); + + wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + + assert(exports.size == 1); + + wasm_extern_t* function = exports.data[0]; + assert(wasm_extern_kind(function) == WASM_EXTERN_FUNC); + + wasm_extern_t* function_copy = wasm_extern_copy(function); + assert(wasm_extern_kind(function_copy) == WASM_EXTERN_FUNC); + + wasm_extern_delete(function_copy); + wasm_extern_vec_delete(&exports); + wasm_instance_delete(instance); + wasm_module_delete(module); + wasm_byte_vec_delete(&wasm); + wasm_byte_vec_delete(&wat); + wasm_store_delete(store); + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/externals/table.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/externals/table.rs new file mode 100644 index 0000000..839cb44 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/externals/table.rs @@ -0,0 +1,62 @@ +use super::super::store::wasm_store_t; +use super::super::types::{wasm_ref_t, wasm_table_size_t, wasm_tabletype_t}; +use super::wasm_extern_t; +use wasmer_api::Extern; + +#[allow(non_camel_case_types)] +#[repr(C)] +#[derive(Clone)] +pub struct wasm_table_t { + pub(crate) extern_: wasm_extern_t, +} + +impl wasm_table_t { + pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_table_t> { + match &e.inner { + Extern::Table(_) => Some(unsafe { &*(e as *const _ as *const _) }), + _ => None, + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_table_new( + _store: Option<&wasm_store_t>, + _table_type: Option<&wasm_tabletype_t>, + _init: *const wasm_ref_t, +) -> Option> { + todo!("get val from init somehow"); +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_table_delete(_table: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn wasm_table_copy(table: &wasm_table_t) -> Box { + // do shallow copy + Box::new(table.clone()) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_table_size(table: &wasm_table_t) -> usize { + table.extern_.table().size(&table.extern_.store.store()) as _ +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_table_same( + wasm_table1: &wasm_table_t, + wasm_table2: &wasm_table_t, +) -> bool { + wasm_table1.extern_.table() == wasm_table2.extern_.table() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_table_grow( + _table: &mut wasm_table_t, + _delta: wasm_table_size_t, + _init: *mut wasm_ref_t, +) -> bool { + // TODO: maybe need to look at result to return `true`; also maybe report error here + //wasm_table.inner.grow(delta, init).is_ok() + todo!("Blocked on transforming ExternRef into a val type") +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/function_env.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/function_env.rs new file mode 100644 index 0000000..a9e3910 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/function_env.rs @@ -0,0 +1,63 @@ +use crate::wasm_c_api::store::wasm_store_t; +use std::ffi::c_void; +use wasmer_api::FunctionEnv; + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct FunctionCEnv { + #[allow(dead_code)] + inner: std::ptr::NonNull, +} + +impl FunctionCEnv { + #[allow(dead_code)] + pub(crate) fn as_ptr(&self) -> *mut c_void { + self.inner.as_ptr() + } +} + +static NULL_ENV_PLACEHOLDER: u32 = 42; + +impl FunctionCEnv { + pub(crate) fn new(inner: std::ptr::NonNull) -> Self { + Self { inner } + } +} + +impl Default for FunctionCEnv { + fn default() -> Self { + Self { + inner: unsafe { + std::ptr::NonNull::new_unchecked( + &NULL_ENV_PLACEHOLDER as *const u32 as *mut u32 as *mut c_void, + ) + }, + } + } +} + +unsafe impl Send for FunctionCEnv {} + +#[derive(Clone)] +#[allow(non_camel_case_types)] +#[repr(C)] +pub struct wasmer_funcenv_t { + inner: FunctionCEnv, +} + +#[no_mangle] +pub unsafe extern "C" fn wasmer_funcenv_new( + store: Option<&mut wasm_store_t>, + mut data: *mut c_void, +) -> Option> { + let store = store?; + if data.is_null() { + data = &NULL_ENV_PLACEHOLDER as *const u32 as *mut u32 as *mut c_void; + } + let inner = FunctionCEnv::new(std::ptr::NonNull::new_unchecked(data)); + let _ = FunctionEnv::new(&mut store.inner.store_mut(), inner); + Some(Box::new(wasmer_funcenv_t { inner })) +} + +#[no_mangle] +pub unsafe extern "C" fn wasmer_funcenv_delete(_funcenv: Option>) {} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/instance.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/instance.rs new file mode 100644 index 0000000..ab07dff --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/instance.rs @@ -0,0 +1,318 @@ +use super::externals::{wasm_extern_t, wasm_extern_vec_t}; +use super::module::wasm_module_t; +use super::store::{wasm_store_t, StoreRef}; +use super::trap::wasm_trap_t; +use wasmer_api::{Extern, Instance, InstantiationError}; + +/// Opaque type representing a WebAssembly instance. +#[allow(non_camel_case_types)] +pub struct wasm_instance_t { + pub(crate) store: StoreRef, + pub(crate) inner: Instance, +} + +/// Creates a new instance from a WebAssembly module and a +/// set of imports. +/// +/// ## Errors +/// +/// The function can fail in 2 ways: +/// +/// 1. Link errors that happen when plugging the imports into the +/// instance, +/// 2. Runtime errors that happen when running the module `start` +/// function. +/// +/// The failure is stored in the `trap` argument; the program doesn't +/// panic. +/// +/// # Notes +/// +/// The `store` argument is ignored. The store from the given module +/// will be used. +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub unsafe extern "C" fn wasm_instance_new( + store: Option<&mut wasm_store_t>, + module: Option<&wasm_module_t>, + imports: Option<&wasm_extern_vec_t>, + trap: Option<&mut *mut wasm_trap_t>, +) -> Option> { + let store = store?; + let mut store_mut = store.inner.store_mut(); + let module = module?; + let imports = imports?; + + let wasm_module = &module.inner; + let module_imports = wasm_module.imports(); + let module_import_count = module_imports.len(); + let externs = imports + .as_slice() + .iter() + .map(|imp| Extern::from(imp.as_ref().unwrap().as_ref().clone())) + .take(module_import_count) + .collect::>(); + + let instance = match Instance::new_by_index(&mut store_mut, wasm_module, &externs) { + Ok(instance) => instance, + + Err(InstantiationError::Link(link_error)) => { + crate::error::update_last_error(link_error); + + return None; + } + + Err(InstantiationError::Start(runtime_error)) => { + if let Some(trap) = trap { + let this_trap: Box = Box::new(runtime_error.into()); + *trap = Box::into_raw(this_trap); + } + + return None; + } + + Err(e @ InstantiationError::CpuFeature(_)) => { + crate::error::update_last_error(e); + + return None; + } + + Err(e @ InstantiationError::DifferentStores) => { + crate::error::update_last_error(e); + + return None; + } + + Err(e @ InstantiationError::DifferentArchOS) => { + crate::error::update_last_error(e); + + return None; + } + }; + + Some(Box::new(wasm_instance_t { + store: store.inner.clone(), + inner: instance, + })) +} + +/// Deletes an instance. +/// +/// # Example +/// +/// See [`wasm_instance_new`]. +#[no_mangle] +pub unsafe extern "C" fn wasm_instance_delete(_instance: Option>) {} + +/// Gets the exports of the instance. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the engine and the store. +/// wasm_engine_t* engine = wasm_engine_new(); +/// wasm_store_t* store = wasm_store_new(engine); +/// +/// // Create a WebAssembly module from a WAT definition. +/// wasm_byte_vec_t wat; +/// wasmer_byte_vec_new_from_string( +/// &wat, +/// "(module\n" +/// " (func (export \"function\") (param i32 i64))\n" +/// " (global (export \"global\") i32 (i32.const 7))\n" +/// " (table (export \"table\") 0 funcref)\n" +/// " (memory (export \"memory\") 1))" +/// ); +/// wasm_byte_vec_t wasm; +/// wat2wasm(&wat, &wasm); +/// +/// // Create the module. +/// wasm_module_t* module = wasm_module_new(store, &wasm); +/// +/// // Instantiate the module. +/// wasm_extern_vec_t imports = WASM_EMPTY_VEC; +/// wasm_trap_t* trap = NULL; +/// +/// wasm_instance_t* instance = wasm_instance_new(store, module, &imports, &trap); +/// assert(instance); +/// +/// // Read the exports. +/// wasm_extern_vec_t exports; +/// wasm_instance_exports(instance, &exports); +/// +/// // We have 4 of them. +/// assert(exports.size == 4); +/// +/// // The first one is a function. Use `wasm_extern_as_func` +/// // to go further. +/// assert(wasm_extern_kind(exports.data[0]) == WASM_EXTERN_FUNC); +/// +/// // The second one is a global. Use `wasm_extern_as_global` to +/// // go further. +/// assert(wasm_extern_kind(exports.data[1]) == WASM_EXTERN_GLOBAL); +/// +/// // The third one is a table. Use `wasm_extern_as_table` to +/// // go further. +/// assert(wasm_extern_kind(exports.data[2]) == WASM_EXTERN_TABLE); +/// +/// // The fourth one is a memory. Use `wasm_extern_as_memory` to +/// // go further. +/// assert(wasm_extern_kind(exports.data[3]) == WASM_EXTERN_MEMORY); +/// +/// // Free everything. +/// wasm_extern_vec_delete(&exports); +/// wasm_instance_delete(instance); +/// wasm_module_delete(module); +/// wasm_byte_vec_delete(&wasm); +/// wasm_byte_vec_delete(&wat); +/// wasm_store_delete(store); +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +/// +/// To go further: +/// +/// * [`wasm_extern_as_func`][super::externals::wasm_extern_as_func], +/// * [`wasm_extern_as_global`][super::externals::wasm_extern_as_global], +/// * [`wasm_extern_as_table`][super::externals::wasm_extern_as_table], +/// * [`wasm_extern_as_memory`][super::externals::wasm_extern_as_memory]. +#[no_mangle] +pub unsafe extern "C" fn wasm_instance_exports( + instance: &wasm_instance_t, + // own + out: &mut wasm_extern_vec_t, +) { + let original_instance = instance; + let instance = &instance.inner; + let extern_vec: Vec>> = instance + .exports + .iter() + .map(|(_name, r#extern)| { + Some(Box::new(wasm_extern_t::new( + original_instance.store.clone(), + r#extern.clone(), + ))) + }) + .collect(); + out.set_buffer(extern_vec); +} + +#[cfg(test)] +mod tests { + #[cfg(not(target_os = "windows"))] + use inline_c::assert_c; + #[cfg(target_os = "windows")] + use wasmer_inline_c::assert_c; + + #[cfg_attr(coverage, ignore)] + #[test] + fn test_instance_new() { + (assert_c! { + #include "tests/wasmer.h" + + // The `sum` host function implementation. + wasm_trap_t* sum_callback( + const wasm_val_vec_t* arguments, + wasm_val_vec_t* results + ) { + wasm_val_t sum = { + .kind = WASM_I32, + .of = { arguments->data[0].of.i32 + arguments->data[1].of.i32 }, + }; + results->data[0] = sum; + + return NULL; + } + + int main() { + // Create the engine and the store. + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Create a WebAssembly module from a WAT definition. + wasm_byte_vec_t wat; + wasmer_byte_vec_new_from_string( + &wat, + "(module\n" + " (import \"math\" \"sum\" (func $sum (param i32 i32) (result i32)))\n" + " (func (export \"add_one\") (param i32) (result i32)\n" + " local.get 0\n" + " i32.const 1\n" + " call $sum))" + ); + wasm_byte_vec_t wasm; + wat2wasm(&wat, &wasm); + + // Create the module. + wasm_module_t* module = wasm_module_new(store, &wasm); + + assert(module); + + // Prepare the imports. + wasm_functype_t* sum_type = wasm_functype_new_2_1( + wasm_valtype_new_i32(), + wasm_valtype_new_i32(), + wasm_valtype_new_i32() + ); + wasm_func_t* sum_function = wasm_func_new(store, sum_type, sum_callback); + wasm_extern_t* externs[] = { wasm_func_as_extern(sum_function) }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + + // Instantiate the module. + wasm_trap_t* trap = NULL; + wasm_instance_t* instance = wasm_instance_new(store, module, &imports, &trap); + + assert(instance); + + // Run the exported function. + wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + + assert(exports.size == 1); + + const wasm_func_t* run_function = wasm_extern_as_func(exports.data[0]); + + assert(run_function); + + wasm_val_t arguments[1] = { WASM_I32_VAL(1) }; + wasm_val_t results[1] = { WASM_INIT_VAL }; + + wasm_val_vec_t arguments_as_array = WASM_ARRAY_VEC(arguments); + wasm_val_vec_t results_as_array = WASM_ARRAY_VEC(results); + + trap = wasm_func_call(run_function, &arguments_as_array, &results_as_array); + + assert(trap == NULL); + assert(results[0].of.i32 == 2); + + // Free everything. + wasm_extern_vec_delete(&exports); + wasm_instance_delete(instance); + wasm_func_delete(sum_function); + wasm_functype_delete(sum_type); + wasm_module_delete(module); + wasm_byte_vec_delete(&wasm); + wasm_byte_vec_delete(&wat); + wasm_store_delete(store); + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/macros.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/macros.rs new file mode 100644 index 0000000..19a3090 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/macros.rs @@ -0,0 +1,327 @@ +macro_rules! wasm_declare_vec_inner { + ( + name: $name:ident, + ty: $elem_ty:ty, + c_ty: $c_ty:expr, + c_val: $c_val:expr, + new: $new:ident, + empty: $empty:ident, + uninit: $uninit:ident, + copy: $copy:ident, + delete: $delete:ident, + ) => { + #[doc = concat!("Represents a vector of `", $c_ty, "`. + +Read the documentation of [`", $c_ty, "`] to see more concrete examples. + +# Example + +```rust +# use wasmer_inline_c::assert_c; +# fn main() { +# (assert_c! { +# #include \"tests/wasmer.h\" +# +void example(", $c_ty, " x, ", $c_ty, " y) { + // Create a vector of 2 `", $c_ty, "`. + ", $c_ty, " items[2] = {x, y}; + + ", stringify!($name), " vector; + ", stringify!($new), "(&vector, 2, items); + + // Check that it contains 2 items. + assert(vector.size == 2); + + // Free it. + ", stringify!($delete), "(&vector); +} +# +# int main() { example(", $c_val, ", ", $c_val, "); return 0; } +# }) +# .success(); +# } +```")] + #[repr(C)] + pub struct $name { + pub size: usize, + pub data: *mut $elem_ty, + } + + impl $name { + // Note that this does not free any existing buffer. + pub fn set_buffer(&mut self, buffer: Vec<$elem_ty>) { + let mut vec = buffer.into_boxed_slice(); + self.size = vec.len(); + self.data = vec.as_mut_ptr(); + std::mem::forget(vec); + } + + pub fn as_slice(&self) -> &[$elem_ty] { + // Note that we're careful to not create a slice with a null + // pointer as the data pointer, since that isn't defined + // behavior in Rust. + if self.size == 0 { + &[] + } else { + assert!(!self.data.is_null()); + unsafe { std::slice::from_raw_parts(self.data, self.size) } + } + } + + pub fn as_uninit_slice(&mut self) -> &mut [std::mem::MaybeUninit<$elem_ty>] { + // Note that we're careful to not create a slice with a null + // pointer as the data pointer, since that isn't defined + // behavior in Rust. + if self.size == 0 { + &mut [] + } else { + assert!(!self.data.is_null()); + unsafe { std::slice::from_raw_parts_mut(self.data as _, self.size) } + } + } + + pub fn take(&mut self) -> Vec<$elem_ty> { + if self.data.is_null() { + return Vec::new(); + } + let vec = unsafe { Vec::from_raw_parts(self.data, self.size, self.size) }; + self.data = std::ptr::null_mut(); + self.size = 0; + return vec; + } + } + + impl From> for $name { + fn from(vec: Vec<$elem_ty>) -> Self { + let mut vec = vec.into_boxed_slice(); + let result = $name { + size: vec.len(), + data: vec.as_mut_ptr(), + }; + std::mem::forget(vec); + result + } + } + + impl Clone for $name { + fn clone(&self) -> Self { + self.as_slice().to_vec().into() + } + } + + impl Drop for $name { + fn drop(&mut self) { + drop(self.take()); + } + } + + #[doc = concat!("Creates an empty vector of [`", $c_ty, "`]. + +# Example + +```rust +# use wasmer_inline_c::assert_c; +# fn main() { +# (assert_c! { +# #include \"tests/wasmer.h\" +# +int main() { + // Creates an empty vector of `", $c_ty, "`. + ", stringify!($name), " vector; + ", stringify!($empty), "(&vector); + + // Check that it is empty. + assert(vector.size == 0); + + // Free it. + ", stringify!($delete), "(&vector); + + return 0; +} +# }) +# .success(); +# } +```")] + #[no_mangle] + pub extern "C" fn $empty(out: &mut $name) { + out.size = 0; + out.data = std::ptr::null_mut(); + } + + #[doc = concat!("Creates a new uninitialized vector of [`", $c_ty, "`]. + +# Example + +```rust +# use wasmer_inline_c::assert_c; +# fn main() { +# (assert_c! { +# #include \"tests/wasmer.h\" +# +int main() { + // Creates an empty vector of `", $c_ty, "`. + ", stringify!($name), " vector; + ", stringify!($uninit), "(&vector, 3); + + // Check that it contains 3 items. + assert(vector.size == 3); + + // Free it. + ", stringify!($delete), "(&vector); + + return 0; +} +# }) +# .success(); +# } +```")] + #[no_mangle] + pub extern "C" fn $uninit(out: &mut $name, size: usize) { + out.set_buffer(vec![Default::default(); size]); + } + + #[doc = concat!("Creates a new vector of [`", $c_ty, "`]. + +# Example + +See the [`", stringify!($name), "`] type to get an example.")] + #[no_mangle] + pub unsafe extern "C" fn $new(out: &mut $name, size: usize, ptr: *const $elem_ty) { + let vec = (0..size).map(|i| ptr.add(i).read()).collect(); + out.set_buffer(vec); + } + + #[doc = concat!("Performs a deep copy of a vector of [`", $c_ty, "`].")] + #[no_mangle] + pub extern "C" fn $copy(out: &mut $name, src: &$name) { + out.set_buffer(src.as_slice().to_vec()); + } + + #[doc = concat!("Deletes a vector of [`", $c_ty, "`]. + +# Example + +See the [`", stringify!($name), "`] type to get an example.")] + #[no_mangle] + pub extern "C" fn $delete(out: &mut $name) { + out.take(); + } + }; +} + +macro_rules! wasm_declare_vec { + ($name:ident) => { + wasm_declare_vec!($name, wasm); + }; + + ($name:ident, $prefix:ident) => { + paste::paste! { + wasm_declare_vec_inner!( + name: [<$prefix _ $name _vec_t>], + ty: [<$prefix _ $name _t>], + c_ty: stringify!([<$prefix _ $name _t>]), + c_val: concat!("({ ", + stringify!([<$prefix _ $name _t>]), " foo;\n", + "memset(&foo, 0, sizeof(foo));\n", + "foo;\n", + "})"), + new: [<$prefix _ $name _vec_new>], + empty: [<$prefix _ $name _vec_new_empty>], + uninit: [<$prefix _ $name _vec_new_uninitialized>], + copy: [<$prefix _ $name _vec_copy>], + delete: [<$prefix _ $name _vec_delete>], + ); + } + }; +} + +macro_rules! wasm_declare_boxed_vec { + ($name:ident) => { + wasm_declare_boxed_vec!($name, wasm); + }; + + ($name:ident, $prefix:ident) => { + paste::paste! { + wasm_declare_vec_inner!( + name: [<$prefix _ $name _vec_t>], + ty: Option]>>, + c_ty: stringify!([<$prefix _ $name _t>] *), + c_val: "NULL", + new: [<$prefix _ $name _vec_new>], + empty: [<$prefix _ $name _vec_new_empty>], + uninit: [<$prefix _ $name _vec_new_uninitialized>], + copy: [<$prefix _ $name _vec_copy>], + delete: [<$prefix _ $name _vec_delete>], + ); + } + }; +} + +macro_rules! wasm_impl_copy { + ($name:ident) => { + wasm_impl_copy!($name, wasm); + }; + + ($name:ident, $prefix:ident) => { + paste::paste! { + #[no_mangle] + pub extern "C" fn [<$prefix _ $name _copy>](src: Option<&[<$prefix _ $name _t>]>) -> Option]>> { + Some(Box::new(src?.clone())) + } + } + }; +} + +macro_rules! wasm_impl_delete { + ($name:ident) => { + wasm_impl_delete!($name, wasm); + }; + + ($name:ident, $prefix:ident) => { + paste::paste! { + #[no_mangle] + pub extern "C" fn [<$prefix _ $name _delete>](_: Option]>>) {} + } + }; +} + +macro_rules! wasm_impl_copy_delete { + ($name:ident) => { + wasm_impl_copy_delete!($name, wasm); + }; + + ($name:ident, $prefix:ident) => { + wasm_impl_copy!($name, $prefix); + wasm_impl_delete!($name, $prefix); + }; +} + +macro_rules! c_try { + ($expr:expr; otherwise ()) => {{ + let res: Result<_, _> = $expr; + match res { + Ok(val) => val, + Err(err) => { + crate::error::update_last_error(err); + return; + } + } + }}; + ($expr:expr; otherwise $return:expr) => {{ + let res: Result<_, _> = $expr; + match res { + Ok(val) => val, + Err(err) => { + crate::error::update_last_error(err); + return $return; + } + } + }}; + ($expr:expr) => {{ + c_try!($expr; otherwise None) + }}; + ($expr:expr, $e:expr) => {{ + let opt: Option<_> = $expr; + c_try!(opt.ok_or_else(|| $e)) + }}; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/mod.rs new file mode 100644 index 0000000..2b79d9f --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/mod.rs @@ -0,0 +1,368 @@ +//! Implementation of the [official WebAssembly C +//! API](https://github.com/WebAssembly/wasm-c-api) for Wasmer. +//! +//! We would like to remind the reader that this official standard can +//! be characterized as a _living standard_. As such, the API is not +//! yet stable, even though it shows maturity over time. The API is +//! described by the `wasm.h` C header, which is included by +//! `wasmer.h` C header file (which contains extension of the +//! standard API, for example to provide WASI or vendor-specific +//! features). +//! +//! # Quick Guide +//! +//! Usually, the user first needs to create an [`engine`] and a +//! [`store`]. Once it's done, the user needs to create a [`module`] +//! and then [instantiate][instance] it. When instantiating the +//! module, the user is able to pass a set of +//! [imports][externals]. With an instance, the user is able to call +//! the [exports][instance::wasm_instance_exports]. +//! +//! Every module comes with examples and entry points to guide the +//! discovery of this API. + +/// `Context`. +mod function_env; + +/// Private Rust macros. +#[macro_use] +mod macros; + +/// An engine drives the compilation and the runtime. +/// +/// Entry points: A default engine is created with +/// [`wasm_engine_new`][engine::wasm_engine_new] and freed with +/// [`wasm_engine_delete`][engine::wasm_engine_delete]. +/// +/// # Example +/// +/// The simplest way to get a default engine is the following: +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the engine. +/// wasm_engine_t* engine = wasm_engine_new(); +/// +/// // Check we have a valid engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +/// +/// To configure the engine, see the [`wasm_config_new`][engine::wasm_config_new]. +pub mod engine; + +/// cbindgen:ignore +pub mod externals; + +/// 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. +/// +/// Entry points: A WebAssembly instance is created with +/// [`wasm_instance_new`][instance::wasm_instance_new] and freed with +/// [`wasm_instance_delete`][instance::wasm_instance_delete]. +/// +/// # Example +/// +/// The simplest way to instantiate a Wasm module is the following: +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the engine and the store. +/// wasm_engine_t* engine = wasm_engine_new(); +/// wasm_store_t* store = wasm_store_new(engine); +/// +/// // Create a WebAssembly module from a WAT definition. +/// wasm_byte_vec_t wat; +/// wasmer_byte_vec_new_from_string(&wat, "(module)"); +/// wasm_byte_vec_t wasm; +/// wat2wasm(&wat, &wasm); +/// +/// // Create the module. +/// wasm_module_t* module = wasm_module_new(store, &wasm); +/// assert(module); +/// +/// // Instantiate the module. +/// wasm_extern_vec_t imports = WASM_EMPTY_VEC; +/// wasm_trap_t* trap = NULL; +/// +/// wasm_instance_t* instance = wasm_instance_new(store, module, &imports, &trap); +/// assert(instance); +/// +/// // Now do something with the instance, like calling the +/// // exports with `wasm_instance_exports`. +/// +/// // Free everything. +/// wasm_instance_delete(instance); +/// wasm_module_delete(module); +/// wasm_byte_vec_delete(&wasm); +/// wasm_byte_vec_delete(&wat); +/// wasm_store_delete(store); +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +/// +/// cbindgen:ignore +pub mod instance; + +/// A WebAssembly module contains stateless WebAssembly code that has +/// already been compiled and can be instantiated multiple times. +/// +/// Entry points: A WebAssembly module is created with +/// [`wasm_module_new`][module::wasm_module_new] and freed with +/// [`wasm_module_delete`][module::wasm_module_delete]. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the engine and the store. +/// wasm_engine_t* engine = wasm_engine_new(); +/// wasm_store_t* store = wasm_store_new(engine); +/// +/// // Create a WebAssembly module from a WAT definition. +/// wasm_byte_vec_t wat; +/// wasmer_byte_vec_new_from_string(&wat, "(module)"); +/// wasm_byte_vec_t wasm; +/// wat2wasm(&wat, &wasm); +/// +/// // Create the module. +/// wasm_module_t* module = wasm_module_new(store, &wasm); +/// +/// // It works! +/// assert(module); +/// +/// // Free everything. +/// wasm_byte_vec_delete(&wasm); +/// wasm_byte_vec_delete(&wat); +/// wasm_module_delete(module); +/// wasm_store_delete(store); +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +/// +/// cbindgen:ignore +pub mod module; + +/// A 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 —amonst many things— used to +/// compile the Wasm bytes into a valid [module] artifact), in addition +/// to extra private types. +/// +/// Entry points: A store is created with +/// [`wasm_store_new`][store::wasm_store_new] and freed with +/// [`wasm_store_delete`][store::wasm_store_delete]. To customize the +/// engine the store holds, see +/// [`wasm_config_new`][engine::wasm_config_new]. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the engine. +/// wasm_engine_t* engine = wasm_engine_new(); +/// +/// // Create the store. +/// wasm_store_t* store = wasm_store_new(engine); +/// +/// // It works! +/// assert(store); +/// +/// // Free everything. +/// wasm_store_delete(store); +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +/// +/// cbindgen:ignore +pub mod store; + +/// A trap represents an error which stores trace message with +/// backtrace. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create an engine and a store. +/// wasm_engine_t* engine = wasm_engine_new(); +/// wasm_store_t* store = wasm_store_new(engine); +/// +/// // Create the trap message. +/// wasm_message_t message; +/// wasm_name_new_from_string_nt(&message, "foobar"); +/// +/// // Create the trap with its message. +/// // The backtrace will be generated automatically. +/// wasm_trap_t* trap = wasm_trap_new(store, &message); +/// assert(trap); +/// +/// wasm_name_delete(&message); +/// +/// // Do something with the trap. +/// +/// // Free everything. +/// wasm_trap_delete(trap); +/// wasm_store_delete(store); +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +/// +/// Usually, a trap is returned from a host function (an imported +/// function). +/// +/// cbindgen:ignore +pub mod trap; + +/// cbindgen:ignore +pub mod types; + +/// This module contains _unstable non-standard_ C API. +/// +/// Use them at your own risks. The API is subject to change or to +/// break without any plan to keep any compatibility :-). +pub mod unstable; + +/// Possible runtime values that a WebAssembly module can either +/// consume or produce. +/// +/// cbindgen:ignore +pub mod value; + +/// Wasmer-specific API to get or query the version of this Wasm C API. +/// +/// The `wasmer.h` file provides the `WASMER_VERSION`, +/// `WASMER_VERSION_MAJOR`, `WASMER_VERSION_MINOR`, +/// `WASMER_VERSION_PATCH` and `WASMER_VERSION_PRE` +/// constants. However, in absence of this header file, it is possible +/// to retrieve the same information with their respective functions, +/// namely [`wasmer_version`][version::wasmer_version], +/// [`wasmer_version_major`][version::wasmer_version_major], +/// [`wasmer_version_minor`][version::wasmer_version_minor], +/// [`wasmer_version_patch`][version::wasmer_version_patch], and +/// [`wasmer_version_pre`][version::wasmer_version_pre]. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Get and print the version. +/// const char* version = wasmer_version(); +/// printf("%s", version); +/// +/// // No need to free the string. It's statically allocated on +/// // the Rust side. +/// +/// return 0; +/// } +/// # }) +/// # .success() +/// # .stdout(env!("CARGO_PKG_VERSION")); +/// # } +/// ``` +pub mod version; + +#[cfg(feature = "wasi")] +pub mod wasi; + +/// Wasmer-specific API to transform the WAT format into Wasm bytes. +/// +/// It is used mostly for testing or for small program purposes. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Our WAT module. +/// wasm_byte_vec_t wat; +/// wasm_byte_vec_new(&wat, 8, "(module)"); +/// +/// // Our Wasm bytes. +/// wasm_byte_vec_t wasm; +/// wat2wasm(&wat, &wasm); +/// +/// // It works! +/// assert(wasm.size > 0); +/// +/// // Free everything. +/// wasm_byte_vec_delete(&wasm); +/// wasm_byte_vec_delete(&wat); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[cfg(feature = "wat")] +pub mod wat; diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/module.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/module.rs new file mode 100644 index 0000000..2bc7b1f --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/module.rs @@ -0,0 +1,863 @@ +use super::store::wasm_store_t; +use super::types::{wasm_byte_vec_t, wasm_exporttype_vec_t, wasm_importtype_vec_t}; +use std::ptr::NonNull; +use wasmer_api::Module; + +/// Opaque type representing a WebAssembly module. +#[derive(Clone)] +#[allow(non_camel_case_types)] +pub struct wasm_module_t { + pub(crate) inner: Module, +} + +/// A WebAssembly module contains stateless WebAssembly code that has +/// already been compiled and can be instantiated multiple times. +/// +/// Creates a new WebAssembly Module given the configuration +/// in the store. +/// +/// ## Security +/// +/// Before the code is compiled, it will be validated using the store +/// features. +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub unsafe extern "C" fn wasm_module_new( + store: Option<&mut wasm_store_t>, + bytes: Option<&wasm_byte_vec_t>, +) -> Option> { + let store = store?.inner.store_mut(); + let bytes = bytes?; + + let module = c_try!(Module::from_binary(&store, bytes.as_slice())); + + Some(Box::new(wasm_module_t { inner: module })) +} + +/// Deletes a WebAssembly module. +/// +/// # Example +/// +/// See [`wasm_module_new`]. +#[no_mangle] +pub unsafe extern "C" fn wasm_module_delete(_module: Option>) {} + +/// Validates a new WebAssembly module given the configuration +/// in the [store][super::store]. +/// +/// This validation is normally pretty fast and checks the enabled +/// WebAssembly features in the [store engine][super::engine] to +/// assure deterministic validation of the module. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the engine and the store. +/// wasm_engine_t* engine = wasm_engine_new(); +/// wasm_store_t* store = wasm_store_new(engine); +/// +/// // Create a WebAssembly module from a WAT definition. +/// wasm_byte_vec_t wat; +/// wasmer_byte_vec_new_from_string(&wat, "(module)"); +/// wasm_byte_vec_t wasm; +/// wat2wasm(&wat, &wasm); +/// +/// // Validate that the WebAssembly module is correct. +/// assert(wasm_module_validate(store, &wasm)); +/// +/// // Free everything. +/// wasm_byte_vec_delete(&wasm); +/// wasm_byte_vec_delete(&wat); +/// wasm_store_delete(store); +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[no_mangle] +pub unsafe extern "C" fn wasm_module_validate( + store: Option<&mut wasm_store_t>, + bytes: Option<&wasm_byte_vec_t>, +) -> bool { + let store = match store { + Some(store) => store.inner.store_mut(), + None => return false, + }; + let bytes = match bytes { + Some(bytes) => bytes, + None => return false, + }; + + Module::validate(&store, bytes.as_slice()) + .map(|_| true) + .unwrap_or(false) +} + +/// Returns an array of the exported types in the module. +/// +/// The order of the exports is guaranteed to be the same as in the +/// WebAssembly bytecode. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the engine and the store. +/// wasm_engine_t* engine = wasm_engine_new(); +/// wasm_store_t* store = wasm_store_new(engine); +/// +/// // Create a WebAssembly module from a WAT definition. +/// wasm_byte_vec_t wat; +/// wasmer_byte_vec_new_from_string( +/// &wat, +/// "(module\n" +/// " (func (export \"function\") (param i32 i64))\n" +/// " (global (export \"global\") i32 (i32.const 7))\n" +/// " (table (export \"table\") 0 funcref)\n" +/// " (memory (export \"memory\") 1))" +/// ); +/// wasm_byte_vec_t wasm; +/// wat2wasm(&wat, &wasm); +/// +/// // Create the module. +/// wasm_module_t* module = wasm_module_new(store, &wasm); +/// assert(module); +/// +/// // Extract the types exported by this module. +/// wasm_exporttype_vec_t export_types; +/// wasm_module_exports(module, &export_types); +/// +/// // We have 4 of them. +/// assert(export_types.size == 4); +/// +/// // The first one is a function. Use +/// // `wasm_externtype_as_functype_const` to continue to inspect the +/// // type. +/// { +/// wasm_exporttype_t* export_type = export_types.data[0]; +/// +/// const wasm_name_t* export_name = wasm_exporttype_name(export_type); +/// wasmer_assert_name(export_name, "function"); +/// +/// const wasm_externtype_t* extern_type = wasm_exporttype_type(export_type); +/// assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_FUNC); +/// } +/// +/// // The second one is a global. Use +/// // `wasm_externtype_as_globaltype_const` to continue to inspect the +/// // type. +/// { +/// wasm_exporttype_t* export_type = export_types.data[1]; +/// +/// const wasm_name_t* export_name = wasm_exporttype_name(export_type); +/// wasmer_assert_name(export_name, "global"); +/// +/// const wasm_externtype_t* extern_type = wasm_exporttype_type(export_type); +/// assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_GLOBAL); +/// } +/// +/// // The third one is a table. Use +/// // `wasm_externtype_as_tabletype_const` to continue to inspect the +/// // type. +/// { +/// wasm_exporttype_t* export_type = export_types.data[2]; +/// +/// const wasm_name_t* export_name = wasm_exporttype_name(export_type); +/// wasmer_assert_name(export_name, "table"); +/// +/// const wasm_externtype_t* extern_type = wasm_exporttype_type(export_type); +/// assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_TABLE); +/// } +/// +/// // The fourth one is a memory. Use +/// // `wasm_externtype_as_memorytype_const` to continue to inspect the +/// // type. +/// { +/// wasm_exporttype_t* export_type = export_types.data[3]; +/// +/// const wasm_name_t* export_name = wasm_exporttype_name(export_type); +/// wasmer_assert_name(export_name, "memory"); +/// +/// const wasm_externtype_t* extern_type = wasm_exporttype_type(export_type); +/// assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_MEMORY); +/// } +/// +/// // Free everything. +/// wasm_exporttype_vec_delete(&export_types); +/// wasm_byte_vec_delete(&wasm); +/// wasm_byte_vec_delete(&wat); +/// wasm_module_delete(module); +/// wasm_store_delete(store); +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[no_mangle] +pub unsafe extern "C" fn wasm_module_exports( + module: &wasm_module_t, + // own + out: &mut wasm_exporttype_vec_t, +) { + let exports = module + .inner + .exports() + .map(|export| Some(Box::new(export.into()))) + .collect(); + + out.set_buffer(exports); +} + +/// Returns an array of the imported types in the module. +/// +/// The order of the imports is guaranteed to be the same as in the +/// WebAssembly bytecode. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the engine and the store. +/// wasm_engine_t* engine = wasm_engine_new(); +/// wasm_store_t* store = wasm_store_new(engine); +/// +/// // Create a WebAssembly module from a WAT definition. +/// wasm_byte_vec_t wat; +/// wasmer_byte_vec_new_from_string( +/// &wat, +/// "(module\n" +/// " (import \"ns\" \"function\" (func))\n" +/// " (import \"ns\" \"global\" (global f32))\n" +/// " (import \"ns\" \"table\" (table 1 2 anyfunc))\n" +/// " (import \"ns\" \"memory\" (memory 3 4)))" +/// ); +/// wasm_byte_vec_t wasm; +/// wat2wasm(&wat, &wasm); +/// +/// // Create the module. +/// wasm_module_t* module = wasm_module_new(store, &wasm); +/// assert(module); +/// +/// // Extract the types imported by the module. +/// wasm_importtype_vec_t import_types; +/// wasm_module_imports(module, &import_types); +/// +/// // We have 4 of them. +/// assert(import_types.size == 4); +/// +/// // The first one is a function. Use +/// // `wasm_externtype_as_functype_const` to continue to inspect the +/// // type. +/// { +/// const wasm_importtype_t* import_type = import_types.data[0]; +/// +/// const wasm_name_t* import_module = wasm_importtype_module(import_type); +/// wasmer_assert_name(import_module, "ns"); +/// +/// const wasm_name_t* import_name = wasm_importtype_name(import_type); +/// wasmer_assert_name(import_name, "function"); +/// +/// const wasm_externtype_t* extern_type = wasm_importtype_type(import_type); +/// assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_FUNC); +/// } +/// +/// // The second one is a global. Use +/// // `wasm_externtype_as_globaltype_const` to continue to inspect +/// // the type. +/// { +/// const wasm_importtype_t* import_type = import_types.data[1]; +/// +/// const wasm_name_t* import_module = wasm_importtype_module(import_type); +/// wasmer_assert_name(import_module, "ns"); +/// +/// const wasm_name_t* import_name = wasm_importtype_name(import_type); +/// wasmer_assert_name(import_name, "global"); +/// +/// const wasm_externtype_t* extern_type = wasm_importtype_type(import_type); +/// assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_GLOBAL); +/// } +/// +/// // The third one is a table. Use +/// // `wasm_externtype_as_tabletype_const` to continue to inspect +/// // the type. +/// { +/// const wasm_importtype_t* import_type = import_types.data[2]; +/// +/// const wasm_name_t* import_module = wasm_importtype_module(import_type); +/// wasmer_assert_name(import_module, "ns"); +/// +/// const wasm_name_t* import_name = wasm_importtype_name(import_type); +/// wasmer_assert_name(import_name, "table"); +/// +/// const wasm_externtype_t* extern_type = wasm_importtype_type(import_type); +/// assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_TABLE); +/// } +/// +/// // The fourth one is a memory. Use +/// // `wasm_externtype_as_memorytype_const` to continue to inspect +/// // the type. +/// { +/// const wasm_importtype_t* import_type = import_types.data[3]; +/// +/// const wasm_name_t* import_module = wasm_importtype_module(import_type); +/// wasmer_assert_name(import_module, "ns"); +/// +/// const wasm_name_t* import_name = wasm_importtype_name(import_type); +/// wasmer_assert_name(import_name, "memory"); +/// +/// const wasm_externtype_t* extern_type = wasm_importtype_type(import_type); +/// assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_MEMORY); +/// +/// const wasm_memorytype_t* memory_type = wasm_externtype_as_memorytype_const(extern_type); +/// const wasm_limits_t* memory_limits = wasm_memorytype_limits(memory_type); +/// assert(memory_limits->min == 3); +/// assert(memory_limits->max == 4); +/// } +/// +/// // Free everything. +/// wasm_importtype_vec_delete(&import_types); +/// wasm_module_delete(module); +/// wasm_byte_vec_delete(&wasm); +/// wasm_byte_vec_delete(&wat); +/// wasm_store_delete(store); +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[no_mangle] +pub unsafe extern "C" fn wasm_module_imports( + module: &wasm_module_t, + // own + out: &mut wasm_importtype_vec_t, +) { + let imports = module + .inner + .imports() + .map(|import| Some(Box::new(import.into()))) + .collect(); + + out.set_buffer(imports); +} + +/// Deserializes a serialized module binary into a `wasm_module_t`. +/// +/// Note: the module has to be serialized before with the +/// `wasm_module_serialize` function. +/// +/// # Safety +/// +/// This function is inherently **unsafe** as the provided bytes: +/// +/// 1. Are going to be deserialized directly into Rust and C objects, +/// 2. Contains the function assembly bodies and, if intercepted, +/// a malicious actor could inject code into executable +/// memory. +/// +/// And as such, the `wasm_module_deserialize` method is unsafe. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the engine and the store. +/// wasm_engine_t* engine = wasm_engine_new(); +/// wasm_store_t* store = wasm_store_new(engine); +/// +/// // Create a WebAssembly module from a WAT definition. +/// wasm_byte_vec_t wat; +/// wasmer_byte_vec_new_from_string( +/// &wat, +/// "(module\n" +/// " (func (export \"function\") (param i32 i64))\n" +/// " (global (export \"global\") i32 (i32.const 7))\n" +/// " (table (export \"table\") 0 funcref)\n" +/// " (memory (export \"memory\") 1))" +/// ); +/// wasm_byte_vec_t wasm; +/// wat2wasm(&wat, &wasm); +/// +/// // Create the module. +/// wasm_module_t* module = wasm_module_new(store, &wasm); +/// assert(module); +/// +/// // Serialize the module into bytes. +/// wasm_byte_vec_t serialized_module; +/// wasm_module_serialize(module, &serialized_module); +/// assert(serialized_module.size > 0); +/// +/// // Free the module. +/// wasm_module_delete(module); +/// +/// // Deserialize the serialized module. Note that the store must +/// // be the same as the one used to serialize. +/// wasm_module_t* deserialized_module = wasm_module_deserialize( +/// store, +/// &serialized_module +/// ); +/// wasm_byte_vec_delete(&serialized_module); +/// assert(deserialized_module); +/// +/// // Check we have our 4 export types. +/// wasm_exporttype_vec_t export_types; +/// wasm_module_exports(deserialized_module, &export_types); +/// +/// assert(export_types.size == 4); +/// +/// // Free everything. +/// wasm_exporttype_vec_delete(&export_types); +/// wasm_module_delete(deserialized_module); +/// wasm_byte_vec_delete(&wasm); +/// wasm_byte_vec_delete(&wat); +/// wasm_store_delete(store); +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[no_mangle] +pub unsafe extern "C" fn wasm_module_deserialize( + store: &wasm_store_t, + bytes: Option<&wasm_byte_vec_t>, +) -> Option> { + let bytes = bytes?; + + let module = c_try!(Module::deserialize(&store.inner.store(), bytes.as_slice())); + + Some(NonNull::new_unchecked(Box::into_raw(Box::new( + wasm_module_t { inner: module }, + )))) +} + +/// Serializes a module into a binary representation that the +/// [engine][super::engine] can later process via +/// [`wasm_module_deserialize`]. +/// +/// # Example +/// +/// See [`wasm_module_deserialize`]. +#[no_mangle] +pub unsafe extern "C" fn wasm_module_serialize(module: &wasm_module_t, out: &mut wasm_byte_vec_t) { + let byte_vec = c_try!(module.inner.serialize(); otherwise ()); + out.set_buffer(byte_vec.to_vec()); +} + +#[cfg(test)] +mod tests { + #[cfg(not(target_os = "windows"))] + use inline_c::assert_c; + #[cfg(target_os = "windows")] + use wasmer_inline_c::assert_c; + + #[cfg_attr(coverage, ignore)] + #[test] + fn test_module_validate() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + wasm_byte_vec_t wat; + wasmer_byte_vec_new_from_string(&wat, "(module)"); + wasm_byte_vec_t wasm; + wat2wasm(&wat, &wasm); + + assert(wasm_module_validate(store, &wasm)); + + wasm_byte_vec_delete(&wasm); + wasm_byte_vec_delete(&wat); + wasm_store_delete(store); + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } + + #[cfg_attr(coverage, ignore)] + #[test] + fn test_module_new() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + wasm_byte_vec_t wat; + wasmer_byte_vec_new_from_string(&wat, "(module)"); + wasm_byte_vec_t wasm; + wat2wasm(&wat, &wasm); + + wasm_module_t* module = wasm_module_new(store, &wasm); + assert(module); + + wasm_byte_vec_delete(&wasm); + wasm_byte_vec_delete(&wat); + wasm_module_delete(module); + wasm_store_delete(store); + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } + + #[cfg_attr(coverage, ignore)] + #[test] + fn test_module_exports() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + wasm_byte_vec_t wat; + wasmer_byte_vec_new_from_string( + &wat, + "(module\n" + " (func (export \"function\") (param i32 i64))\n" + " (global (export \"global\") i32 (i32.const 7))\n" + " (table (export \"table\") 0 funcref)\n" + " (memory (export \"memory\") 1))" + ); + wasm_byte_vec_t wasm; + wat2wasm(&wat, &wasm); + + wasm_module_t* module = wasm_module_new(store, &wasm); + assert(module); + + wasm_exporttype_vec_t export_types; + wasm_module_exports(module, &export_types); + + assert(export_types.size == 4); + + { + wasm_exporttype_t* export_type = export_types.data[0]; + + const wasm_name_t* export_name = wasm_exporttype_name(export_type); + wasmer_assert_name(export_name, "function"); + + const wasm_externtype_t* extern_type = wasm_exporttype_type(export_type); + assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_FUNC); + + const wasm_functype_t* func_type = wasm_externtype_as_functype_const(extern_type); + + const wasm_valtype_vec_t* func_params = wasm_functype_params(func_type); + assert(func_params && func_params->size == 2); + assert(wasm_valtype_kind(func_params->data[0]) == WASM_I32); + assert(wasm_valtype_kind(func_params->data[1]) == WASM_I64); + + const wasm_valtype_vec_t* func_results = wasm_functype_results(func_type); + assert(func_results && func_results->size == 0); + } + + { + wasm_exporttype_t* export_type = export_types.data[1]; + + const wasm_name_t* export_name = wasm_exporttype_name(export_type); + wasmer_assert_name(export_name, "global"); + + const wasm_externtype_t* extern_type = wasm_exporttype_type(export_type); + assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_GLOBAL); + + const wasm_globaltype_t* global_type = wasm_externtype_as_globaltype_const(extern_type); + assert(wasm_valtype_kind(wasm_globaltype_content(global_type)) == WASM_I32); + assert(wasm_globaltype_mutability(global_type) == WASM_CONST); + } + + { + wasm_exporttype_t* export_type = export_types.data[2]; + + const wasm_name_t* export_name = wasm_exporttype_name(export_type); + wasmer_assert_name(export_name, "table"); + + const wasm_externtype_t* extern_type = wasm_exporttype_type(export_type); + assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_TABLE); + + const wasm_tabletype_t* table_type = wasm_externtype_as_tabletype_const(extern_type); + assert(wasm_valtype_kind(wasm_tabletype_element(table_type)) == WASM_FUNCREF); + + const wasm_limits_t* table_limits = wasm_tabletype_limits(table_type); + assert(table_limits->min == 0); + assert(table_limits->max == wasm_limits_max_default); + } + + { + wasm_exporttype_t* export_type = export_types.data[3]; + + const wasm_name_t* export_name = wasm_exporttype_name(export_type); + wasmer_assert_name(export_name, "memory"); + + const wasm_externtype_t* extern_type = wasm_exporttype_type(export_type); + assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_MEMORY); + + const wasm_memorytype_t* memory_type = wasm_externtype_as_memorytype_const(extern_type); + const wasm_limits_t* memory_limits = wasm_memorytype_limits(memory_type); + assert(memory_limits->min == 1); + assert(memory_limits->max == wasm_limits_max_default); + } + + wasm_exporttype_vec_delete(&export_types); + wasm_byte_vec_delete(&wasm); + wasm_byte_vec_delete(&wat); + wasm_module_delete(module); + wasm_store_delete(store); + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } + + #[cfg_attr(coverage, ignore)] + #[test] + fn test_module_imports() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + wasm_byte_vec_t wat; + wasmer_byte_vec_new_from_string( + &wat, + "(module\n" + " (import \"ns\" \"function\" (func))\n" + " (import \"ns\" \"global\" (global f32))\n" + " (import \"ns\" \"table\" (table 1 2 anyfunc))\n" + " (import \"ns\" \"memory\" (memory 3 4)))" + ); + wasm_byte_vec_t wasm; + wat2wasm(&wat, &wasm); + + wasm_module_t* module = wasm_module_new(store, &wasm); + assert(module); + + wasm_importtype_vec_t import_types; + wasm_module_imports(module, &import_types); + + assert(import_types.size == 4); + + { + const wasm_importtype_t* import_type = import_types.data[0]; + + const wasm_name_t* import_module = wasm_importtype_module(import_type); + wasmer_assert_name(import_module, "ns"); + + const wasm_name_t* import_name = wasm_importtype_name(import_type); + wasmer_assert_name(import_name, "function"); + + const wasm_externtype_t* extern_type = wasm_importtype_type(import_type); + assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_FUNC); + + const wasm_functype_t* func_type = wasm_externtype_as_functype_const(extern_type); + + const wasm_valtype_vec_t* func_params = wasm_functype_params(func_type); + assert(func_params && func_params->size == 0); + + const wasm_valtype_vec_t* func_results = wasm_functype_results(func_type); + assert(func_results && func_results->size == 0); + } + + { + const wasm_importtype_t* import_type = import_types.data[1]; + + const wasm_name_t* import_module = wasm_importtype_module(import_type); + wasmer_assert_name(import_module, "ns"); + + const wasm_name_t* import_name = wasm_importtype_name(import_type); + wasmer_assert_name(import_name, "global"); + + const wasm_externtype_t* extern_type = wasm_importtype_type(import_type); + assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_GLOBAL); + + const wasm_globaltype_t* global_type = wasm_externtype_as_globaltype_const(extern_type); + assert(wasm_valtype_kind(wasm_globaltype_content(global_type)) == WASM_F32); + assert(wasm_globaltype_mutability(global_type) == WASM_CONST); + } + + { + const wasm_importtype_t* import_type = import_types.data[2]; + + const wasm_name_t* import_module = wasm_importtype_module(import_type); + wasmer_assert_name(import_module, "ns"); + + const wasm_name_t* import_name = wasm_importtype_name(import_type); + wasmer_assert_name(import_name, "table"); + + const wasm_externtype_t* extern_type = wasm_importtype_type(import_type); + assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_TABLE); + + const wasm_tabletype_t* table_type = wasm_externtype_as_tabletype_const(extern_type); + assert(wasm_valtype_kind(wasm_tabletype_element(table_type)) == WASM_FUNCREF); + + const wasm_limits_t* table_limits = wasm_tabletype_limits(table_type); + assert(table_limits->min == 1); + assert(table_limits->max == 2); + } + + { + const wasm_importtype_t* import_type = import_types.data[3]; + + const wasm_name_t* import_module = wasm_importtype_module(import_type); + wasmer_assert_name(import_module, "ns"); + + const wasm_name_t* import_name = wasm_importtype_name(import_type); + wasmer_assert_name(import_name, "memory"); + + const wasm_externtype_t* extern_type = wasm_importtype_type(import_type); + assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_MEMORY); + + const wasm_memorytype_t* memory_type = wasm_externtype_as_memorytype_const(extern_type); + const wasm_limits_t* memory_limits = wasm_memorytype_limits(memory_type); + assert(memory_limits->min == 3); + assert(memory_limits->max == 4); + } + + wasm_importtype_vec_delete(&import_types); + wasm_module_delete(module); + wasm_byte_vec_delete(&wasm); + wasm_byte_vec_delete(&wat); + wasm_store_delete(store); + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } + + #[cfg_attr(coverage, ignore)] + #[test] + fn test_module_serialize() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + wasm_byte_vec_t wat; + wasmer_byte_vec_new_from_string(&wat, "(module)"); + wasm_byte_vec_t wasm; + wat2wasm(&wat, &wasm); + + wasm_module_t* module = wasm_module_new(store, &wasm); + assert(module); + + wasm_byte_vec_t serialized_module; + wasm_module_serialize(module, &serialized_module); + assert(serialized_module.size > 0); + + wasm_module_delete(module); + wasm_byte_vec_delete(&serialized_module); + wasm_byte_vec_delete(&wasm); + wasm_byte_vec_delete(&wat); + wasm_store_delete(store); + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } + + #[cfg_attr(coverage, ignore)] + #[test] + fn test_module_serialize_and_deserialize() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + wasm_byte_vec_t wat; + wasmer_byte_vec_new_from_string( + &wat, + "(module\n" + " (func (export \"function\") (param i32 i64))\n" + " (global (export \"global\") i32 (i32.const 7))\n" + " (table (export \"table\") 0 funcref)\n" + " (memory (export \"memory\") 1))" + ); + wasm_byte_vec_t wasm; + wat2wasm(&wat, &wasm); + + wasm_module_t* module = wasm_module_new(store, &wasm); + assert(module); + + wasm_byte_vec_t serialized_module; + wasm_module_serialize(module, &serialized_module); + assert(serialized_module.size > 0); + + wasm_module_delete(module); + wasm_module_t* deserialized_module = wasm_module_deserialize( + store, + &serialized_module + ); + wasm_byte_vec_delete(&serialized_module); + assert(deserialized_module); + + wasm_exporttype_vec_t export_types; + wasm_module_exports(deserialized_module, &export_types); + + assert(export_types.size == 4); + + wasm_exporttype_vec_delete(&export_types); + wasm_module_delete(deserialized_module); + wasm_byte_vec_delete(&wasm); + wasm_byte_vec_delete(&wat); + wasm_store_delete(store); + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/store.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/store.rs new file mode 100644 index 0000000..24c6b06 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/store.rs @@ -0,0 +1,52 @@ +use super::engine::wasm_engine_t; +use std::cell::UnsafeCell; +use std::rc::Rc; +use wasmer_api::{AsStoreMut, AsStoreRef, Store, StoreMut, StoreRef as BaseStoreRef}; + +#[derive(Clone)] +pub struct StoreRef { + inner: Rc>, +} + +impl StoreRef { + pub unsafe fn store(&self) -> BaseStoreRef<'_> { + (*self.inner.get()).as_store_ref() + } + + pub unsafe fn store_mut(&mut self) -> StoreMut<'_> { + (*self.inner.get()).as_store_mut() + } +} + +/// Opaque type representing a WebAssembly store. +#[allow(non_camel_case_types)] +pub struct wasm_store_t { + pub(crate) inner: StoreRef, +} + +/// Creates a new WebAssembly store given a specific [engine][super::engine]. +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub unsafe extern "C" fn wasm_store_new( + engine: Option<&wasm_engine_t>, +) -> Option> { + let engine = engine?; + let store = Store::new(&engine.inner); + + Some(Box::new(wasm_store_t { + inner: StoreRef { + inner: Rc::new(UnsafeCell::new(store)), + }, + })) +} + +/// Deletes a WebAssembly store. +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub unsafe extern "C" fn wasm_store_delete(_store: Option>) {} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/trap.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/trap.rs new file mode 100644 index 0000000..390e8a9 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/trap.rs @@ -0,0 +1,218 @@ +use super::store::wasm_store_t; +use super::types::{wasm_byte_vec_t, wasm_message_t}; +use super::types::{wasm_frame_t, wasm_frame_vec_t}; +use std::ffi::CString; +use wasmer_api::RuntimeError; + +// opaque type which is a `RuntimeError` +#[allow(non_camel_case_types)] +pub struct wasm_trap_t { + pub(crate) inner: RuntimeError, +} + +impl From for wasm_trap_t { + fn from(other: RuntimeError) -> Self { + Self { inner: other } + } +} + +/// Create a new trap message. +/// +/// Be careful, the message is typed with `wasm_message_t` which +/// represents a null-terminated string. +/// +/// # Example +/// +/// See the module's documentation for a complete example. +#[no_mangle] +pub unsafe extern "C" fn wasm_trap_new( + _store: &mut wasm_store_t, + message: &wasm_message_t, +) -> Option> { + let message_bytes = message.as_slice(); + + // The trap message is typed with `wasm_message_t` which is a + // typeref to `wasm_name_t` with the exception that it's a + // null-terminated string. `RuntimeError` must contain a valid + // Rust `String` that doesn't contain a null byte. We must ensure + // this behavior. + let runtime_error = match CString::new(message_bytes) { + // The string is well-formed and doesn't contain a nul byte. + Ok(cstring) => RuntimeError::new(cstring.into_string().ok()?), + + // The string is well-formed but is nul-terminated. Let's + // create a `String` which is null-terminated too. + Err(nul_error) if nul_error.nul_position() + 1 == message_bytes.len() => { + let mut vec = nul_error.into_vec(); + vec.pop(); + + RuntimeError::new(String::from_utf8(vec).ok()?) + } + + // The string not well-formed. + Err(_) => return None, + }; + + let trap = runtime_error.into(); + + Some(Box::new(trap)) +} + +/// Deletes a trap. +/// +/// # Example +/// +/// See the module's documentation for a complete example. +#[no_mangle] +pub unsafe extern "C" fn wasm_trap_delete(_trap: Option>) {} + +/// Gets the message attached to the trap. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create an engine and a store. +/// wasm_engine_t* engine = wasm_engine_new(); +/// wasm_store_t* store = wasm_store_new(engine); +/// +/// // Create the trap message. +/// wasm_message_t message; +/// wasm_name_new_from_string_nt(&message, "foobar"); +/// +/// // Create the trap with its message. +/// // The backtrace will be generated automatically. +/// wasm_trap_t* trap = wasm_trap_new(store, &message); +/// assert(trap); +/// +/// // Get the trap's message back. +/// wasm_message_t retrieved_message; +/// wasm_trap_message(trap, &retrieved_message); +/// assert(retrieved_message.size == message.size); +/// +/// // Free everything. +/// wasm_name_delete(&message); +/// wasm_name_delete(&retrieved_message); +/// wasm_trap_delete(trap); +/// wasm_store_delete(store); +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[no_mangle] +pub unsafe extern "C" fn wasm_trap_message( + trap: &wasm_trap_t, + // own + out: &mut wasm_byte_vec_t, +) { + let message = trap.inner.message(); + let mut byte_vec = message.into_bytes(); + byte_vec.push(0); + + out.set_buffer(byte_vec); +} + +/// Gets the origin frame attached to the trap. +#[no_mangle] +pub unsafe extern "C" fn wasm_trap_origin(trap: &wasm_trap_t) -> Option> { + trap.inner.trace().first().map(Into::into).map(Box::new) +} + +/// Gets the trace (as a list of frames) attached to the trap. +#[no_mangle] +pub unsafe extern "C" fn wasm_trap_trace( + trap: &wasm_trap_t, + // own + out: &mut wasm_frame_vec_t, +) { + let frames = trap.inner.trace(); + out.set_buffer( + frames + .iter() + .map(|frame| Some(Box::new(frame.into()))) + .collect(), + ); +} + +#[cfg(test)] +mod tests { + #[cfg(not(target_os = "windows"))] + use inline_c::assert_c; + #[cfg(target_os = "windows")] + use wasmer_inline_c::assert_c; + + #[cfg_attr(coverage, ignore)] + #[test] + fn test_trap_message_null_terminated() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + wasm_message_t original_message; + wasm_name_new_from_string_nt(&original_message, "foobar"); + assert(original_message.size == 7); // 6 for `foobar` + 1 for nul byte. + + wasm_trap_t* trap = wasm_trap_new(store, &original_message); + assert(trap); + + wasm_message_t retrieved_message; + wasm_trap_message(trap, &retrieved_message); + assert(retrieved_message.size == 7); + + wasm_name_delete(&original_message); + wasm_name_delete(&retrieved_message); + wasm_trap_delete(trap); + wasm_store_delete(store); + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } + + #[cfg_attr(coverage, ignore)] + #[test] + fn test_trap_message_not_null_terminated() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + wasm_message_t original_message; + wasm_name_new_from_string(&original_message, "foobar"); + assert(original_message.size == 6); // 6 for `foobar` + 0 for nul byte. + + wasm_trap_t* trap = wasm_trap_new(store, &original_message); + assert(trap); + + wasm_message_t retrieved_message; + wasm_trap_message(trap, &retrieved_message); + assert(retrieved_message.size == 7); + + wasm_name_delete(&original_message); + wasm_name_delete(&retrieved_message); + wasm_trap_delete(trap); + wasm_store_delete(store); + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/export.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/export.rs new file mode 100644 index 0000000..a60ccf4 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/export.rs @@ -0,0 +1,48 @@ +use super::{wasm_externtype_t, wasm_name_t}; +use wasmer_api::ExportType; + +#[allow(non_camel_case_types)] +#[derive(Clone)] +pub struct wasm_exporttype_t { + name: wasm_name_t, + extern_type: wasm_externtype_t, +} + +wasm_declare_boxed_vec!(exporttype); +wasm_impl_copy_delete!(exporttype); + +#[no_mangle] +pub extern "C" fn wasm_exporttype_new( + name: &wasm_name_t, + extern_type: Box, +) -> Box { + Box::new(wasm_exporttype_t { + name: name.clone(), + extern_type: *extern_type, + }) +} + +#[no_mangle] +pub extern "C" fn wasm_exporttype_name(export_type: &wasm_exporttype_t) -> &wasm_name_t { + &export_type.name +} + +#[no_mangle] +pub extern "C" fn wasm_exporttype_type(export_type: &wasm_exporttype_t) -> &wasm_externtype_t { + &export_type.extern_type +} + +impl From for wasm_exporttype_t { + fn from(other: ExportType) -> Self { + (&other).into() + } +} + +impl From<&ExportType> for wasm_exporttype_t { + fn from(other: &ExportType) -> Self { + let name: wasm_name_t = other.name().to_string().into(); + let extern_type: wasm_externtype_t = other.ty().into(); + + wasm_exporttype_t { name, extern_type } + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/extern_.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/extern_.rs new file mode 100644 index 0000000..4218c2a --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/extern_.rs @@ -0,0 +1,284 @@ +use super::super::externals::wasm_extern_t; +use super::{ + wasm_functype_t, wasm_globaltype_t, wasm_memorytype_t, wasm_tabletype_t, WasmFunctionType, + WasmGlobalType, WasmMemoryType, WasmTableType, +}; +use std::convert::{TryFrom, TryInto}; +use std::mem; +use thiserror::Error; +use wasmer_api::ExternType; + +#[allow(non_camel_case_types)] +pub type wasm_externkind_t = u8; + +#[allow(non_camel_case_types)] +#[repr(u8)] +pub enum wasm_externkind_enum { + WASM_EXTERN_FUNC = 0, + WASM_EXTERN_GLOBAL = 1, + WASM_EXTERN_TABLE = 2, + WASM_EXTERN_MEMORY = 3, +} + +impl From for wasm_externkind_enum { + fn from(other: ExternType) -> Self { + (&other).into() + } +} +impl From<&ExternType> for wasm_externkind_enum { + fn from(other: &ExternType) -> Self { + match other { + ExternType::Function(_) => Self::WASM_EXTERN_FUNC, + ExternType::Global(_) => Self::WASM_EXTERN_GLOBAL, + ExternType::Table(_) => Self::WASM_EXTERN_TABLE, + ExternType::Memory(_) => Self::WASM_EXTERN_MEMORY, + } + } +} + +#[derive(Debug, Clone)] +pub(crate) enum WasmExternType { + Function(WasmFunctionType), + Global(WasmGlobalType), + Table(WasmTableType), + Memory(WasmMemoryType), +} + +#[allow(non_camel_case_types)] +#[derive(Debug, Clone)] +pub struct wasm_externtype_t { + pub(crate) inner: WasmExternType, +} + +impl wasm_externtype_t { + pub(crate) fn new(extern_type: ExternType) -> Self { + Self { + inner: match extern_type { + ExternType::Function(function_type) => { + WasmExternType::Function(WasmFunctionType::new(function_type)) + } + ExternType::Global(global_type) => { + WasmExternType::Global(WasmGlobalType::new(global_type)) + } + ExternType::Table(table_type) => { + WasmExternType::Table(WasmTableType::new(table_type)) + } + ExternType::Memory(memory_type) => { + WasmExternType::Memory(WasmMemoryType::new(memory_type)) + } + }, + } + } +} + +impl From for wasm_externtype_t { + fn from(extern_type: ExternType) -> Self { + Self::new(extern_type) + } +} + +impl From<&ExternType> for wasm_externtype_t { + fn from(other: &ExternType) -> Self { + other.clone().into() + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_extern_type(r#extern: &wasm_extern_t) -> Box { + Box::new(wasm_externtype_t::new(r#extern.ty())) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_extern_kind(r#extern: &wasm_extern_t) -> wasm_externkind_t { + wasm_externkind_enum::from(r#extern.ty()) as wasm_externkind_t +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_externtype_delete(_extern_type: Option>) {} + +#[no_mangle] +pub extern "C" fn wasm_externtype_copy(extern_type: &wasm_externtype_t) -> Box { + Box::new(extern_type.clone()) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_externtype_kind( + extern_type: &wasm_externtype_t, +) -> wasm_externkind_t { + (match extern_type.inner { + WasmExternType::Function(_) => wasm_externkind_enum::WASM_EXTERN_FUNC, + WasmExternType::Global(_) => wasm_externkind_enum::WASM_EXTERN_GLOBAL, + WasmExternType::Table(_) => wasm_externkind_enum::WASM_EXTERN_TABLE, + WasmExternType::Memory(_) => wasm_externkind_enum::WASM_EXTERN_MEMORY, + }) as wasm_externkind_t +} + +#[derive(Debug, Clone, Error)] +#[error("failed to convert from `wasm_externtype_t`: {0}")] +pub struct ExternTypeConversionError(&'static str); + +impl From<&'static str> for ExternTypeConversionError { + fn from(other: &'static str) -> Self { + Self(other) + } +} + +impl TryFrom<&'static wasm_externtype_t> for &'static wasm_functype_t { + type Error = ExternTypeConversionError; + + fn try_from(other: &'static wasm_externtype_t) -> Result { + if let WasmExternType::Function(_) = other.inner { + Ok(unsafe { mem::transmute::<&'static wasm_externtype_t, Self>(other) }) + } else { + Err(ExternTypeConversionError("Wrong type: expected function")) + } + } +} + +impl TryFrom<&'static wasm_externtype_t> for &'static wasm_globaltype_t { + type Error = ExternTypeConversionError; + + fn try_from(other: &'static wasm_externtype_t) -> Result { + if let WasmExternType::Global(_) = other.inner { + Ok(unsafe { mem::transmute::<&'static wasm_externtype_t, Self>(other) }) + } else { + Err(ExternTypeConversionError("Wrong type: expected global")) + } + } +} + +impl TryFrom<&'static wasm_externtype_t> for &'static wasm_tabletype_t { + type Error = ExternTypeConversionError; + + fn try_from(other: &'static wasm_externtype_t) -> Result { + if let WasmExternType::Table(_) = other.inner { + Ok(unsafe { mem::transmute::<&'static wasm_externtype_t, Self>(other) }) + } else { + Err(ExternTypeConversionError("Wrong type: expected table")) + } + } +} + +impl TryFrom<&'static wasm_externtype_t> for &'static wasm_memorytype_t { + type Error = ExternTypeConversionError; + + fn try_from(other: &'static wasm_externtype_t) -> Result { + if let WasmExternType::Memory(_) = other.inner { + Ok(unsafe { mem::transmute::<&'static wasm_externtype_t, Self>(other) }) + } else { + Err(ExternTypeConversionError("Wrong type: expected memory")) + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_externtype_as_functype_const( + extern_type: &'static wasm_externtype_t, +) -> Option<&'static wasm_functype_t> { + Some(c_try!(extern_type.try_into())) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_externtype_as_functype( + extern_type: &'static wasm_externtype_t, +) -> Option<&'static wasm_functype_t> { + Some(c_try!(extern_type.try_into())) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_functype_as_externtype_const( + function_type: &'static wasm_functype_t, +) -> &'static wasm_externtype_t { + &function_type.extern_type +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_functype_as_externtype( + function_type: &'static wasm_functype_t, +) -> &'static wasm_externtype_t { + &function_type.extern_type +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_externtype_as_globaltype_const( + extern_type: &'static wasm_externtype_t, +) -> Option<&'static wasm_globaltype_t> { + Some(c_try!(extern_type.try_into())) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_externtype_as_globaltype( + extern_type: &'static wasm_externtype_t, +) -> Option<&'static wasm_globaltype_t> { + Some(c_try!(extern_type.try_into())) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_globaltype_as_externtype_const( + global_type: &'static wasm_globaltype_t, +) -> &'static wasm_externtype_t { + &global_type.extern_type +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_globaltype_as_externtype( + global_type: &'static wasm_globaltype_t, +) -> &'static wasm_externtype_t { + &global_type.extern_type +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_externtype_as_tabletype_const( + extern_type: &'static wasm_externtype_t, +) -> Option<&'static wasm_tabletype_t> { + Some(c_try!(extern_type.try_into())) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_externtype_as_tabletype( + extern_type: &'static wasm_externtype_t, +) -> Option<&'static wasm_tabletype_t> { + Some(c_try!(extern_type.try_into())) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_tabletype_as_externtype_const( + table_type: &'static wasm_tabletype_t, +) -> &'static wasm_externtype_t { + &table_type.extern_type +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_tabletype_as_externtype( + table_type: &'static wasm_tabletype_t, +) -> &'static wasm_externtype_t { + &table_type.extern_type +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_externtype_as_memorytype_const( + extern_type: &'static wasm_externtype_t, +) -> Option<&'static wasm_memorytype_t> { + Some(c_try!(extern_type.try_into())) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_externtype_as_memorytype( + extern_type: &'static wasm_externtype_t, +) -> Option<&'static wasm_memorytype_t> { + Some(c_try!(extern_type.try_into())) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memorytype_as_externtype_const( + memory_type: &'static wasm_memorytype_t, +) -> &'static wasm_externtype_t { + &memory_type.extern_type +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memorytype_as_externtype( + memory_type: &'static wasm_memorytype_t, +) -> &'static wasm_externtype_t { + &memory_type.extern_type +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/frame.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/frame.rs new file mode 100644 index 0000000..9cccd95 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/frame.rs @@ -0,0 +1,127 @@ +use super::super::instance::wasm_instance_t; +use libc::c_char; +use std::ffi::CString; +use wasmer_api::FrameInfo; + +#[allow(non_camel_case_types)] +#[derive(Debug, Clone)] +pub struct wasm_frame_t { + info: FrameInfo, +} + +impl<'a> From<&'a FrameInfo> for wasm_frame_t { + fn from(other: &'a FrameInfo) -> Self { + other.clone().into() + } +} + +impl From for wasm_frame_t { + fn from(other: FrameInfo) -> Self { + Self { info: other } + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_frame_copy(frame: &wasm_frame_t) -> Box { + Box::new(frame.clone()) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_frame_delete(_frame: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn wasm_frame_instance(_frame: &wasm_frame_t) -> *const wasm_instance_t { + std::ptr::null() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_frame_func_index(frame: &wasm_frame_t) -> u32 { + frame.info.func_index() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_frame_func_offset(frame: &wasm_frame_t) -> usize { + frame.info.func_offset() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_frame_module_offset(frame: &wasm_frame_t) -> usize { + frame.info.module_offset() +} + +#[repr(C)] +#[allow(non_camel_case_types)] +#[derive(Debug, Clone)] +pub struct wasm_name_t { + pub name: *mut c_char, +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_frame_module_name(frame: &wasm_frame_t) -> wasm_name_t { + let module_name = + Some(frame.info.module_name()).and_then(|f| Some(CString::new(f).ok()?.into_raw())); + + match module_name { + Some(s) => wasm_name_t { name: s }, + None => wasm_name_t { + name: core::ptr::null_mut(), + }, + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_frame_func_name(frame: &wasm_frame_t) -> wasm_name_t { + let func_name = frame + .info + .function_name() + .and_then(|f| Some(CString::new(f).ok()?.into_raw())); + + match func_name { + Some(s) => wasm_name_t { name: s }, + None => wasm_name_t { + name: core::ptr::null_mut(), + }, + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_name_delete(name: Option<&mut wasm_name_t>) { + if let Some(s) = name { + if !s.name.is_null() { + let _ = CString::from_raw(s.name); + } + } +} + +wasm_declare_boxed_vec!(frame); + +#[cfg(test)] +#[test] +fn test_frame_name() { + use std::ffi::CStr; + use wasmer_types::SourceLoc; + + let info = wasm_frame_t { + info: FrameInfo::new( + "module_name".to_string(), + 5, + Some("function_name".to_string()), + SourceLoc::new(10), + SourceLoc::new(20), + ), + }; + + unsafe { + let mut wasm_frame_func_name = wasm_frame_func_name(&info); + let s = CStr::from_ptr(wasm_frame_func_name.name); + assert_eq!(s.to_str().unwrap(), "function_name"); + wasm_name_delete(Some(&mut wasm_frame_func_name)); + + let mut wasm_frame_module_name = wasm_frame_module_name(&info); + let s = CStr::from_ptr(wasm_frame_module_name.name); + assert_eq!(s.to_str().unwrap(), "module_name"); + wasm_name_delete(Some(&mut wasm_frame_module_name)); + } + + println!("{:#?}", info); +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/function.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/function.rs new file mode 100644 index 0000000..52d10d5 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/function.rs @@ -0,0 +1,113 @@ +use super::{wasm_externtype_t, wasm_valtype_vec_t, WasmExternType}; +use std::fmt; +use wasmer_api::{ExternType, FunctionType}; +use wasmer_types::Type; + +pub(crate) struct WasmFunctionType { + pub(crate) function_type: FunctionType, + params: wasm_valtype_vec_t, + results: wasm_valtype_vec_t, +} + +impl WasmFunctionType { + pub(crate) fn new(function_type: FunctionType) -> Self { + let params: Vec<_> = function_type + .params() + .iter() + .map(|&valtype| Some(Box::new(valtype.into()))) + .collect(); + let results: Vec<_> = function_type + .results() + .iter() + .map(|&valtype| Some(Box::new(valtype.into()))) + .collect(); + + Self { + function_type, + params: params.into(), + results: results.into(), + } + } +} + +impl Clone for WasmFunctionType { + fn clone(&self) -> Self { + Self::new(self.function_type.clone()) + } +} + +impl fmt::Debug for WasmFunctionType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.function_type.fmt(f) + } +} + +#[allow(non_camel_case_types)] +#[derive(Debug, Clone)] +#[repr(transparent)] +pub struct wasm_functype_t { + pub(crate) extern_type: wasm_externtype_t, +} + +impl wasm_functype_t { + pub(crate) fn new(function_type: FunctionType) -> Self { + Self { + extern_type: wasm_externtype_t::new(ExternType::Function(function_type)), + } + } + + pub(crate) fn inner(&self) -> &WasmFunctionType { + match &self.extern_type.inner { + WasmExternType::Function(wasm_function_type) => wasm_function_type, + _ => { + unreachable!("Data corruption: `wasm_functype_t` does not contain a function type") + } + } + } +} + +wasm_declare_boxed_vec!(functype); +wasm_impl_copy_delete!(functype); + +#[no_mangle] +pub unsafe extern "C" fn wasm_functype_new( + params: Option<&mut wasm_valtype_vec_t>, + results: Option<&mut wasm_valtype_vec_t>, +) -> Option> { + let params = params?; + let results = results?; + + let params_as_valtype: Vec = params + .take() + .into_iter() + .map(|val| val.as_ref().unwrap().as_ref().into()) + .collect::>(); + let results_as_valtype: Vec = results + .take() + .into_iter() + .map(|val| val.as_ref().unwrap().as_ref().into()) + .collect::>(); + + Some(Box::new(wasm_functype_t::new(FunctionType::new( + params_as_valtype, + results_as_valtype, + )))) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_functype_params( + function_type: Option<&wasm_functype_t>, +) -> Option<&wasm_valtype_vec_t> { + let function_type = function_type?; + + Some(&function_type.inner().params) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_functype_results( + function_type: Option<&wasm_functype_t>, +) -> Option<&wasm_valtype_vec_t> { + let function_type = function_type?; + + Some(&function_type.inner().results) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/global.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/global.rs new file mode 100644 index 0000000..b15d3e8 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/global.rs @@ -0,0 +1,83 @@ +use super::{ + wasm_externtype_t, wasm_mutability_enum, wasm_mutability_t, wasm_valtype_delete, + wasm_valtype_t, WasmExternType, +}; +use std::convert::TryInto; +use wasmer_api::{ExternType, GlobalType}; + +#[derive(Debug, Clone)] +pub(crate) struct WasmGlobalType { + pub(crate) global_type: GlobalType, + content: wasm_valtype_t, +} + +impl WasmGlobalType { + pub(crate) fn new(global_type: GlobalType) -> Self { + let content = global_type.ty.into(); + + Self { + global_type, + content, + } + } +} + +#[allow(non_camel_case_types)] +#[derive(Debug, Clone)] +#[repr(transparent)] +pub struct wasm_globaltype_t { + pub(crate) extern_type: wasm_externtype_t, +} + +impl wasm_globaltype_t { + pub(crate) fn new(global_type: GlobalType) -> Self { + Self { + extern_type: wasm_externtype_t::new(ExternType::Global(global_type)), + } + } + + pub(crate) fn inner(&self) -> &WasmGlobalType { + match &self.extern_type.inner { + WasmExternType::Global(wasm_global_type) => wasm_global_type, + _ => { + unreachable!("Data corruption: `wasm_globaltype_t` does not contain a global type") + } + } + } +} + +wasm_declare_boxed_vec!(globaltype); + +#[no_mangle] +pub unsafe extern "C" fn wasm_globaltype_new( + valtype: Option>, + mutability: wasm_mutability_t, +) -> Option> { + let valtype = valtype?; + let mutability: wasm_mutability_enum = mutability.try_into().ok()?; + let global_type = Box::new(wasm_globaltype_t::new(GlobalType::new( + (*valtype).into(), + mutability.into(), + ))); + + wasm_valtype_delete(Some(valtype)); + + Some(global_type) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_globaltype_delete(_global_type: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn wasm_globaltype_mutability( + global_type: &wasm_globaltype_t, +) -> wasm_mutability_t { + wasm_mutability_enum::from(global_type.inner().global_type.mutability).into() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_globaltype_content( + global_type: &wasm_globaltype_t, +) -> &wasm_valtype_t { + &global_type.inner().content +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/import.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/import.rs new file mode 100644 index 0000000..8be6b99 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/import.rs @@ -0,0 +1,65 @@ +use super::{wasm_externtype_t, wasm_name_t}; +use wasmer_api::ImportType; + +#[allow(non_camel_case_types)] +#[derive(Clone)] +#[repr(C)] +pub struct wasm_importtype_t { + module: wasm_name_t, + name: wasm_name_t, + extern_type: wasm_externtype_t, +} + +wasm_declare_boxed_vec!(importtype); +wasm_impl_copy!(importtype); + +#[no_mangle] +pub extern "C" fn wasm_importtype_new( + module: Option<&mut wasm_name_t>, + name: Option<&mut wasm_name_t>, + extern_type: Option>, +) -> Option> { + Some(Box::new(wasm_importtype_t { + name: name?.take().into(), + module: module?.take().into(), + extern_type: *extern_type?, + })) +} + +#[no_mangle] +pub extern "C" fn wasm_importtype_module(import_type: &wasm_importtype_t) -> &wasm_name_t { + &import_type.module +} + +#[no_mangle] +pub extern "C" fn wasm_importtype_name(import_type: &wasm_importtype_t) -> &wasm_name_t { + &import_type.name +} + +#[no_mangle] +pub extern "C" fn wasm_importtype_type(import_type: &wasm_importtype_t) -> &wasm_externtype_t { + &import_type.extern_type +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_importtype_delete(_import_type: Option>) {} + +impl From for wasm_importtype_t { + fn from(other: ImportType) -> Self { + (&other).into() + } +} + +impl From<&ImportType> for wasm_importtype_t { + fn from(other: &ImportType) -> Self { + let module: wasm_name_t = other.module().to_string().into(); + let name: wasm_name_t = other.name().to_string().into(); + let extern_type: wasm_externtype_t = other.ty().into(); + + wasm_importtype_t { + module, + name, + extern_type, + } + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/memory.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/memory.rs new file mode 100644 index 0000000..0cbbba7 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/memory.rs @@ -0,0 +1,83 @@ +use super::{wasm_externtype_t, WasmExternType}; +use wasmer_api::{ExternType, MemoryType, Pages}; + +#[derive(Debug, Clone)] +pub(crate) struct WasmMemoryType { + pub(crate) memory_type: MemoryType, + limits: wasm_limits_t, +} + +impl WasmMemoryType { + pub(crate) fn new(memory_type: MemoryType) -> Self { + let limits = wasm_limits_t { + min: memory_type.minimum.0 as _, + max: memory_type + .maximum + .map(|max| max.0 as _) + .unwrap_or(LIMITS_MAX_SENTINEL), + }; + + Self { + memory_type, + limits, + } + } +} + +#[allow(non_camel_case_types)] +#[derive(Debug, Clone)] +#[repr(transparent)] +pub struct wasm_memorytype_t { + pub(crate) extern_type: wasm_externtype_t, +} + +impl wasm_memorytype_t { + pub(crate) fn new(memory_type: MemoryType) -> Self { + Self { + extern_type: wasm_externtype_t::new(ExternType::Memory(memory_type)), + } + } + + pub(crate) fn inner(&self) -> &WasmMemoryType { + match &self.extern_type.inner { + WasmExternType::Memory(wasm_memory_type) => wasm_memory_type, + _ => { + unreachable!("Data corruption: `wasm_memorytype_t` does not contain a memory type") + } + } + } +} + +wasm_declare_boxed_vec!(memorytype); + +#[no_mangle] +pub unsafe extern "C" fn wasm_memorytype_new(limits: &wasm_limits_t) -> Box { + let min_pages = Pages(limits.min as _); + let max_pages = if limits.max == LIMITS_MAX_SENTINEL { + None + } else { + Some(Pages(limits.max as _)) + }; + + Box::new(wasm_memorytype_t::new(MemoryType::new( + min_pages, max_pages, false, + ))) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_memorytype_delete(_memory_type: Option>) {} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct wasm_limits_t { + pub min: u32, + pub max: u32, +} + +const LIMITS_MAX_SENTINEL: u32 = u32::max_value(); + +#[no_mangle] +pub unsafe extern "C" fn wasm_memorytype_limits(memory_type: &wasm_memorytype_t) -> &wasm_limits_t { + &memory_type.inner().limits +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/mod.rs new file mode 100644 index 0000000..c5b7ed0 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/mod.rs @@ -0,0 +1,42 @@ +mod export; +mod extern_; +mod frame; +mod function; +mod global; +mod import; +mod memory; +mod mutability; +mod table; +mod value; + +pub use export::*; +pub use extern_::*; +pub use frame::*; +pub use function::*; +pub use global::*; +pub use import::*; +pub use memory::*; +pub use mutability::*; +pub use table::*; +pub use value::*; + +#[allow(non_camel_case_types)] +pub type wasm_byte_t = u8; + +wasm_declare_vec!(byte); + +#[allow(non_camel_case_types)] +pub type wasm_name_t = wasm_byte_vec_t; + +impl From for wasm_name_t { + fn from(string: String) -> Self { + string.into_bytes().into() + } +} + +// opaque type over `ExternRef`? +#[allow(non_camel_case_types)] +pub struct wasm_ref_t; + +#[allow(non_camel_case_types)] +pub type wasm_message_t = wasm_byte_vec_t; diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/mutability.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/mutability.rs new file mode 100644 index 0000000..bef1ec1 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/mutability.rs @@ -0,0 +1,56 @@ +use std::convert::TryFrom; +use wasmer_api::Mutability; + +#[allow(non_camel_case_types)] +pub type wasm_mutability_t = u8; + +#[allow(non_camel_case_types)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum wasm_mutability_enum { + WASM_CONST = 0, + WASM_VAR, +} + +impl wasm_mutability_enum { + #[allow(dead_code)] + fn is_mutable(self) -> bool { + self == Self::WASM_VAR + } +} + +impl TryFrom for wasm_mutability_enum { + type Error = &'static str; + + fn try_from(item: wasm_mutability_t) -> Result { + Ok(match item { + 0 => wasm_mutability_enum::WASM_CONST, + 1 => wasm_mutability_enum::WASM_VAR, + _ => return Err("wasm_mutability_t value out of bounds"), + }) + } +} + +impl From for wasm_mutability_t { + fn from(other: wasm_mutability_enum) -> Self { + other as wasm_mutability_t + } +} + +impl From for Mutability { + fn from(other: wasm_mutability_enum) -> Self { + match other { + wasm_mutability_enum::WASM_CONST => Mutability::Const, + wasm_mutability_enum::WASM_VAR => Mutability::Var, + } + } +} + +impl From for wasm_mutability_enum { + fn from(other: Mutability) -> Self { + match other { + Mutability::Const => wasm_mutability_enum::WASM_CONST, + Mutability::Var => wasm_mutability_enum::WASM_VAR, + } + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/table.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/table.rs new file mode 100644 index 0000000..3affb52 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/table.rs @@ -0,0 +1,91 @@ +use super::{ + wasm_externtype_t, wasm_limits_t, wasm_valtype_delete, wasm_valtype_t, WasmExternType, +}; +use wasmer_api::{ExternType, TableType}; + +#[allow(non_camel_case_types)] +pub type wasm_table_size_t = u32; + +const LIMITS_MAX_SENTINEL: u32 = u32::max_value(); + +#[derive(Debug, Clone)] +pub(crate) struct WasmTableType { + pub(crate) _table_type: TableType, + limits: wasm_limits_t, + content: wasm_valtype_t, +} + +impl WasmTableType { + pub(crate) fn new(table_type: TableType) -> Self { + let limits = wasm_limits_t { + min: table_type.minimum as _, + max: table_type.maximum.unwrap_or(LIMITS_MAX_SENTINEL), + }; + let content = table_type.ty.into(); + + Self { + _table_type: table_type, + limits, + content, + } + } +} + +#[allow(non_camel_case_types)] +#[derive(Debug, Clone)] +#[repr(transparent)] +pub struct wasm_tabletype_t { + pub(crate) extern_type: wasm_externtype_t, +} + +impl wasm_tabletype_t { + pub(crate) fn new(table_type: TableType) -> Self { + Self { + extern_type: wasm_externtype_t::new(ExternType::Table(table_type)), + } + } + + pub(crate) fn inner(&self) -> &WasmTableType { + match &self.extern_type.inner { + WasmExternType::Table(wasm_table_type) => wasm_table_type, + _ => unreachable!("Data corruption: `wasm_tabletype_t` does not contain a table type"), + } + } +} + +wasm_declare_boxed_vec!(tabletype); + +#[no_mangle] +pub unsafe extern "C" fn wasm_tabletype_new( + valtype: Option>, + limits: &wasm_limits_t, +) -> Option> { + let valtype = valtype?; + let max_elements = if limits.max == LIMITS_MAX_SENTINEL { + None + } else { + Some(limits.max as _) + }; + let table_type = Box::new(wasm_tabletype_t::new(TableType::new( + (*valtype).into(), + limits.min as _, + max_elements, + ))); + + wasm_valtype_delete(Some(valtype)); + + Some(table_type) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_tabletype_limits(table_type: &wasm_tabletype_t) -> &wasm_limits_t { + &table_type.inner().limits +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_tabletype_element(table_type: &wasm_tabletype_t) -> &wasm_valtype_t { + &table_type.inner().content +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_tabletype_delete(_table_type: Option>) {} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/value.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/value.rs new file mode 100644 index 0000000..ae2189c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/types/value.rs @@ -0,0 +1,97 @@ +use super::super::value::wasm_valkind_t; +use std::convert::TryInto; +use wasmer_api::Type; + +#[allow(non_camel_case_types)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum wasm_valkind_enum { + WASM_I32 = 0, + WASM_I64 = 1, + WASM_F32 = 2, + WASM_F64 = 3, + WASM_ANYREF = 128, + WASM_FUNCREF = 129, +} + +impl From for wasm_valkind_enum { + fn from(other: Type) -> Self { + match other { + Type::I32 => Self::WASM_I32, + Type::I64 => Self::WASM_I64, + Type::F32 => Self::WASM_F32, + Type::F64 => Self::WASM_F64, + Type::V128 => todo!("no v128 type in Wasm C API yet!"), + Type::ExternRef => Self::WASM_ANYREF, + Type::FuncRef => Self::WASM_FUNCREF, + } + } +} + +impl From for Type { + fn from(other: wasm_valkind_enum) -> Self { + use wasm_valkind_enum::*; + match other { + WASM_I32 => Type::I32, + WASM_I64 => Type::I64, + WASM_F32 => Type::F32, + WASM_F64 => Type::F64, + WASM_ANYREF => Type::ExternRef, + WASM_FUNCREF => Type::FuncRef, + } + } +} + +#[allow(non_camel_case_types)] +#[derive(Debug, Clone, Copy)] +pub struct wasm_valtype_t { + valkind: wasm_valkind_enum, +} + +impl Default for wasm_valtype_t { + fn default() -> Self { + Self { + valkind: wasm_valkind_enum::WASM_I32, + } + } +} + +wasm_declare_boxed_vec!(valtype); + +impl From for Type { + fn from(other: wasm_valtype_t) -> Self { + (&other).into() + } +} + +impl From<&wasm_valtype_t> for Type { + fn from(other: &wasm_valtype_t) -> Self { + other.valkind.into() + } +} + +impl From for wasm_valtype_t { + fn from(other: Type) -> Self { + Self { + valkind: other.into(), + } + } +} + +#[no_mangle] +pub extern "C" fn wasm_valtype_new(kind: wasm_valkind_t) -> Option> { + let kind_enum = kind.try_into().ok()?; + let valtype = wasm_valtype_t { valkind: kind_enum }; + + Some(Box::new(valtype)) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_valtype_delete(_valtype: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn wasm_valtype_kind(valtype: Option<&wasm_valtype_t>) -> wasm_valkind_t { + valtype + .expect("`wasm_valtype_kind: argument is a null pointer") + .valkind as wasm_valkind_t +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/engine.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/engine.rs new file mode 100644 index 0000000..fe3d53c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/engine.rs @@ -0,0 +1,257 @@ +//! Unstable non-standard Wasmer-specific types for the +//! `wasm_engine_t` and siblings. + +#[cfg(feature = "compiler")] +use super::super::engine::wasmer_compiler_t; +use super::super::engine::{wasm_config_t, wasmer_engine_t}; + +use super::features::wasmer_features_t; +use super::target_lexicon::wasmer_target_t; + +/// Unstable non-standard Wasmer-specific API to update the +/// configuration to specify a particular target for the engine. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the configuration. +/// wasm_config_t* config = wasm_config_new(); +/// +/// // Set the target. +/// { +/// wasmer_triple_t* triple = wasmer_triple_new_from_host(); +/// wasmer_cpu_features_t* cpu_features = wasmer_cpu_features_new(); +/// wasmer_target_t* target = wasmer_target_new(triple, cpu_features); +/// +/// wasm_config_set_target(config, target); +/// } +/// +/// // Create the engine. +/// wasm_engine_t* engine = wasm_engine_new_with_config(config); +/// +/// // Check we have an engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[no_mangle] +pub extern "C" fn wasm_config_set_target(config: &mut wasm_config_t, target: Box) { + config.target = Some(target); +} + +/// Unstable non-standard Wasmer-specific API to update the +/// configuration to specify particular features for the engine. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the configuration. +/// wasm_config_t* config = wasm_config_new(); +/// +/// // Set the target. +/// { +/// wasmer_features_t* features = wasmer_features_new(); +/// wasmer_features_simd(features, true); +/// +/// wasm_config_set_features(config, features); +/// } +/// +/// // Create the engine. +/// wasm_engine_t* engine = wasm_engine_new_with_config(config); +/// +/// // Check we have an engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[no_mangle] +pub extern "C" fn wasm_config_set_features( + config: &mut wasm_config_t, + features: Box, +) { + config.features = Some(features); +} + +/// Updates the configuration to enable NaN canonicalization. +/// +/// This is a Wasmer-specific function. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the configuration. +/// wasm_config_t* config = wasm_config_new(); +/// +/// // Enable NaN canonicalization. +/// wasm_config_canonicalize_nans(config, true); +/// +/// // Create the engine. +/// wasm_engine_t* engine = wasm_engine_new_with_config(config); +/// +/// // Check we have an engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[no_mangle] +pub extern "C" fn wasm_config_canonicalize_nans(config: &mut wasm_config_t, enable: bool) { + config.nan_canonicalization = enable; +} + +/// Check whether the given compiler is available, i.e. part of this +/// compiled library. +#[no_mangle] +#[cfg(feature = "compiler")] +pub extern "C" fn wasmer_is_compiler_available(compiler: wasmer_compiler_t) -> bool { + match compiler { + wasmer_compiler_t::CRANELIFT if cfg!(feature = "cranelift") => true, + wasmer_compiler_t::LLVM if cfg!(feature = "llvm") => true, + wasmer_compiler_t::SINGLEPASS if cfg!(feature = "singlepass") => true, + _ => false, + } +} + +/// Check whether there is no compiler available in this compiled +/// library. +#[no_mangle] +pub extern "C" fn wasmer_is_headless() -> bool { + !cfg!(feature = "compiler") +} + +/// Check whether the given engine is available, i.e. part of this +/// compiled library. +#[no_mangle] +pub extern "C" fn wasmer_is_engine_available(engine: wasmer_engine_t) -> bool { + matches!(engine, wasmer_engine_t::UNIVERSAL if cfg!(feature = "compiler")) +} + +#[cfg(test)] +mod tests { + #[cfg(not(target_os = "windows"))] + use inline_c::assert_c; + use std::env::{remove_var, set_var}; + #[cfg(target_os = "windows")] + use wasmer_inline_c::assert_c; + + #[test] + fn test_wasmer_is_headless() { + set_var( + "COMPILER", + if cfg!(feature = "compiler") { "0" } else { "1" }, + ); + + (assert_c! { + #include "tests/wasmer.h" + #include + + int main() { + assert(wasmer_is_headless() == (getenv("COMPILER")[0] == '1')); + + return 0; + } + }) + .success(); + + remove_var("COMPILER"); + } + + #[test] + fn test_wasmer_is_compiler_available() { + set_var( + "CRANELIFT", + if cfg!(feature = "cranelift") { + "1" + } else { + "0" + }, + ); + set_var("LLVM", if cfg!(feature = "llvm") { "1" } else { "0" }); + set_var( + "SINGLEPASS", + if cfg!(feature = "singlepass") { + "1" + } else { + "0" + }, + ); + + (assert_c! { + #include "tests/wasmer.h" + #include + + int main() { + assert(wasmer_is_compiler_available(CRANELIFT) == (getenv("CRANELIFT")[0] == '1')); + assert(wasmer_is_compiler_available(LLVM) == (getenv("LLVM")[0] == '1')); + assert(wasmer_is_compiler_available(SINGLEPASS) == (getenv("SINGLEPASS")[0] == '1')); + + return 0; + } + }) + .success(); + + remove_var("CRANELIFT"); + remove_var("LLVM"); + remove_var("SINGLEPASS"); + } + + #[test] + fn test_wasmer_is_engine_available() { + set_var( + "UNIVERSAL", + if cfg!(feature = "compiler") { "1" } else { "0" }, + ); + + (assert_c! { + #include "tests/wasmer.h" + #include + + int main() { + assert(wasmer_is_engine_available(UNIVERSAL) == (getenv("UNIVERSAL")[0] == '1')); + + return 0; + } + }) + .success(); + + remove_var("UNIVERSAL"); + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/features.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/features.rs new file mode 100644 index 0000000..b00cffb --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/features.rs @@ -0,0 +1,364 @@ +//! Unstable non-standard Wasmer-specific API that contains a Features +//! API for the engine and the compiler. +//! +//! +//! # Example +//! +//! ```rust +//! # use wasmer_inline_c::assert_c; +//! # fn main() { +//! # (assert_c! { +//! # #include "tests/wasmer.h" +//! # +//! int main() { +//! // Declare features. +//! wasmer_features_t* features = wasmer_features_new(); +//! +//! // Now, let's enable de SIMD feature. +//! wasmer_features_simd(features, true); +//! +//! // And also the memory64 feature. +//! wasmer_features_memory64(features, true); +//! +//! wasmer_features_delete(features); +//! +//! return 0; +//! } +//! # }) +//! # .success(); +//! # } +//! ``` +//! +//! To go further, see +//! [`wasm_config_set_features`](super::engine::wasm_config_set_features). + +use wasmer_types::Features; + +/// Controls which experimental features will be enabled. +/// Features usually have a corresponding [WebAssembly proposal]. +/// +/// [WebAssembly proposal]: https://github.com/WebAssembly/proposals +/// +/// # Example +/// +/// See the module's documentation. +#[derive(Debug)] +#[allow(non_camel_case_types)] +pub struct wasmer_features_t { + pub(crate) inner: Features, +} + +/// Creates a new [`wasmer_features_t`]. +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub extern "C" fn wasmer_features_new() -> Box { + Box::new(wasmer_features_t { + inner: Features::new(), + }) +} + +/// Delete a [`wasmer_features_t`]. +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub extern "C" fn wasmer_features_delete(_features: Option>) {} + +/// Configures whether the WebAssembly threads proposal will be enabled. +/// +/// The [WebAssembly threads proposal][threads] is not currently fully +/// standardized and is undergoing development. Support for this feature can +/// be disabled through this method for appropriate WebAssembly modules. +/// +/// This feature gates items such as shared memories and atomic +/// instructions. +/// +/// This is `true` by default. +/// +/// [threads]: https://github.com/webassembly/threads +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub extern "C" fn wasmer_features_threads( + features: Option<&mut wasmer_features_t>, + enable: bool, +) -> bool { + let features = match features { + Some(features) => features, + _ => return false, + }; + + features.inner.threads(enable); + + true +} + +/// Configures whether the WebAssembly reference types proposal will be +/// enabled. +/// +/// The [WebAssembly reference types proposal][proposal] is not currently +/// fully standardized and is undergoing development. Support for this +/// feature can be enabled through this method for appropriate WebAssembly +/// modules. +/// +/// This feature gates items such as the `externref` type and multiple tables +/// being in a module. Note that enabling the reference types feature will +/// also enable the bulk memory feature. +/// +/// This is `false` by default. +/// +/// [proposal]: https://github.com/webassembly/reference-types +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub extern "C" fn wasmer_features_reference_types( + features: Option<&mut wasmer_features_t>, + enable: bool, +) -> bool { + let features = match features { + Some(features) => features, + _ => return false, + }; + + features.inner.reference_types(enable); + + true +} + +/// Configures whether the WebAssembly SIMD proposal will be +/// enabled. +/// +/// The [WebAssembly SIMD proposal][proposal] is not currently +/// fully standardized and is undergoing development. Support for this +/// feature can be enabled through this method for appropriate WebAssembly +/// modules. +/// +/// This feature gates items such as the `v128` type and all of its +/// operators being in a module. +/// +/// This is `false` by default. +/// +/// [proposal]: https://github.com/webassembly/simd +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub extern "C" fn wasmer_features_simd( + features: Option<&mut wasmer_features_t>, + enable: bool, +) -> bool { + let features = match features { + Some(features) => features, + _ => return false, + }; + + features.inner.simd(enable); + + true +} + +/// Configures whether the WebAssembly bulk memory operations proposal will +/// be enabled. +/// +/// The [WebAssembly bulk memory operations proposal][proposal] is not +/// currently fully standardized and is undergoing development. +/// Support for this feature can be enabled through this method for +/// appropriate WebAssembly modules. +/// +/// This feature gates items such as the `memory.copy` instruction, passive +/// data/table segments, etc, being in a module. +/// +/// This is `false` by default. +/// +/// [proposal]: https://github.com/webassembly/bulk-memory-operations +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub extern "C" fn wasmer_features_bulk_memory( + features: Option<&mut wasmer_features_t>, + enable: bool, +) -> bool { + let features = match features { + Some(features) => features, + _ => return false, + }; + + features.inner.bulk_memory(enable); + + true +} + +/// Configures whether the WebAssembly multi-value proposal will +/// be enabled. +/// +/// The [WebAssembly multi-value proposal][proposal] is not +/// currently fully standardized and is undergoing development. +/// Support for this feature can be enabled through this method for +/// appropriate WebAssembly modules. +/// +/// This feature gates functions and blocks returning multiple values in a +/// module, for example. +/// +/// This is `false` by default. +/// +/// [proposal]: https://github.com/webassembly/multi-value +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub extern "C" fn wasmer_features_multi_value( + features: Option<&mut wasmer_features_t>, + enable: bool, +) -> bool { + let features = match features { + Some(features) => features, + _ => return false, + }; + + features.inner.multi_value(enable); + + true +} + +/// Configures whether the WebAssembly tail-call proposal will +/// be enabled. +/// +/// The [WebAssembly tail-call proposal][proposal] is not +/// currently fully standardized and is undergoing development. +/// Support for this feature can be enabled through this method for +/// appropriate WebAssembly modules. +/// +/// This feature gates tail-call functions in WebAssembly. +/// +/// This is `false` by default. +/// +/// [proposal]: https://github.com/webassembly/tail-call +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub extern "C" fn wasmer_features_tail_call( + features: Option<&mut wasmer_features_t>, + enable: bool, +) -> bool { + let features = match features { + Some(features) => features, + _ => return false, + }; + + features.inner.tail_call(enable); + + true +} + +/// Configures whether the WebAssembly tail-call proposal will +/// be enabled. +/// +/// The [WebAssembly tail-call proposal][proposal] is not +/// currently fully standardized and is undergoing development. +/// Support for this feature can be enabled through this method for +/// appropriate WebAssembly modules. +/// +/// This feature allows WebAssembly modules to define, import and +/// export modules and instances. +/// +/// This is `false` by default. +/// +/// [proposal]: https://github.com/webassembly/module-linking +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub extern "C" fn wasmer_features_module_linking( + features: Option<&mut wasmer_features_t>, + enable: bool, +) -> bool { + let features = match features { + Some(features) => features, + _ => return false, + }; + + features.inner.module_linking(enable); + + true +} + +/// Configures whether the WebAssembly multi-memory proposal will +/// be enabled. +/// +/// The [WebAssembly multi-memory proposal][proposal] is not +/// currently fully standardized and is undergoing development. +/// Support for this feature can be enabled through this method for +/// appropriate WebAssembly modules. +/// +/// This feature adds the ability to use multiple memories within a +/// single Wasm module. +/// +/// This is `false` by default. +/// +/// [proposal]: https://github.com/WebAssembly/multi-memory +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub extern "C" fn wasmer_features_multi_memory( + features: Option<&mut wasmer_features_t>, + enable: bool, +) -> bool { + let features = match features { + Some(features) => features, + _ => return false, + }; + + features.inner.multi_memory(enable); + + true +} + +/// Configures whether the WebAssembly 64-bit memory proposal will +/// be enabled. +/// +/// The [WebAssembly 64-bit memory proposal][proposal] is not +/// currently fully standardized and is undergoing development. +/// Support for this feature can be enabled through this method for +/// appropriate WebAssembly modules. +/// +/// This feature gates support for linear memory of sizes larger than +/// 2^32 bits. +/// +/// This is `false` by default. +/// +/// [proposal]: https://github.com/WebAssembly/memory64 +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub extern "C" fn wasmer_features_memory64( + features: Option<&mut wasmer_features_t>, + enable: bool, +) -> bool { + let features = match features { + Some(features) => features, + _ => return false, + }; + + features.inner.memory64(enable); + + true +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/middlewares/metering.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/middlewares/metering.rs new file mode 100644 index 0000000..41958ea --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/middlewares/metering.rs @@ -0,0 +1,326 @@ +//! Unstable non-standard Wasmer-specific API that contains everything +//! to create the middleware metering API. +//! +//! The metering middleware is used for tracking how many operators +//! are executed in total and putting a limit on the total number of +//! operators executed. +//! +//! # Example +//! +//! ```rust +//! # use wasmer_inline_c::assert_c; +//! # fn main() { +//! # (assert_c! { +//! # #include "tests/wasmer.h" +//! # +//! // Define our “cost function”. +//! uint64_t cost_function(wasmer_parser_operator_t wasm_operator) { +//! switch(wasm_operator) { +//! // `local.get` and `i32.const` cost 1 unit. +//! case LocalGet: +//! case I32Const: +//! return 1; +//! +//! // `i32.add` costs 2 units. +//! case I32Add: +//! return 2; +//! +//! // The other operations are free. +//! default: +//! return 0; +//! } +//! } +//! +//! int main() { +//! // Create a new metering middleware, with our cost function. +//! wasmer_metering_t* metering = wasmer_metering_new(10, cost_function); +//! +//! // Consume `metering` to produce a generic `wasmer_middleware_t` value. +//! wasmer_middleware_t* middleware = wasmer_metering_as_middleware(metering); +//! +//! // Create a new configuration, and push the middleware in it. +//! wasm_config_t* config = wasm_config_new(); +//! wasm_config_push_middleware(config, middleware); +//! +//! // Create the engine and the store based on the configuration. +//! wasm_engine_t* engine = wasm_engine_new_with_config(config); +//! wasm_store_t* store = wasm_store_new(engine); +//! +//! // Create the new WebAssembly module. +//! wasm_byte_vec_t wat; +//! wasmer_byte_vec_new_from_string( +//! &wat, +//! "(module\n" +//! " (type $add_t (func (param i32) (result i32)))\n" +//! " (func $add_two_f (type $add_t) (param $value i32) (result i32)\n" +//! " local.get $value\n" +//! " i32.const 1\n" +//! " i32.add)\n" +//! " (export \"add_two\" (func $add_two_f)))" +//! ); +//! wasm_byte_vec_t wasm; +//! wat2wasm(&wat, &wasm); +//! +//! wasm_module_t* module = wasm_module_new(store, &wasm); +//! assert(module); +//! +//! // Instantiate the module. +//! wasm_extern_vec_t imports = WASM_EMPTY_VEC; +//! wasm_trap_t* trap = NULL; +//! wasm_instance_t* instance = wasm_instance_new(store, module, &imports, &trap); +//! assert(instance); +//! +//! // Here we go. At this step, we will get the `add_two` exported function, and +//! // call it. +//! wasm_extern_vec_t exports; +//! wasm_instance_exports(instance, &exports); +//! assert(exports.size >= 1); +//! assert(wasm_extern_kind(exports.data[0]) == WASM_EXTERN_FUNC); +//! +//! const wasm_func_t* add_two = wasm_extern_as_func(exports.data[0]); +//! assert(add_two); +//! +//! wasm_val_t arguments[1] = { WASM_I32_VAL(41) }; +//! wasm_val_t results[1] = { WASM_INIT_VAL }; +//! +//! wasm_val_vec_t arguments_as_array = WASM_ARRAY_VEC(arguments); +//! wasm_val_vec_t results_as_array = WASM_ARRAY_VEC(results); +//! +//! // Let's call `add_two` for the first time! +//! { +//! trap = wasm_func_call(add_two, &arguments_as_array, &results_as_array); +//! assert(trap == NULL); +//! assert(results[0].of.i32 == 42); +//! +//! // There is 6 points left! +//! assert(wasmer_metering_get_remaining_points(instance) == 6); +//! assert(wasmer_metering_points_are_exhausted(instance) == false); +//! } +//! +//! // Let's call `add_two` for the second time! +//! { +//! trap = wasm_func_call(add_two, &arguments_as_array, &results_as_array); +//! assert(trap == NULL); +//! assert(results[0].of.i32 == 42); +//! +//! // There is 2 points left! +//! assert(wasmer_metering_get_remaining_points(instance) == 2); +//! assert(wasmer_metering_points_are_exhausted(instance) == false); +//! } +//! +//! // Let's call `add_two` for the third time! +//! { +//! trap = wasm_func_call(add_two, &arguments_as_array, &results_as_array); +//! // Oh, it failed! +//! assert(trap != NULL); +//! +//! // There is 0 point leftâ€Ļ they are exhausted. +//! assert(wasmer_metering_points_are_exhausted(instance) == true); +//! } +//! +//! wasm_extern_vec_delete(&exports); +//! wasm_instance_delete(instance); +//! wasm_module_delete(module); +//! wasm_store_delete(store); +//! wasm_engine_delete(engine); +//! +//! return 0; +//! } +//! # }) +//! # .success(); +//! # } +//! ``` + +use super::super::super::instance::wasm_instance_t; +use super::super::parser::operator::wasmer_parser_operator_t; +use super::wasmer_middleware_t; +use std::sync::Arc; +use wasmer_api::wasmparser::Operator; +use wasmer_middlewares::{ + metering::{get_remaining_points, set_remaining_points, MeteringPoints}, + Metering, +}; + +/// Opaque type representing a metering middleware. +/// +/// To transform this specific middleware into a generic one, please +/// see [`wasmer_metering_as_middleware`]. +/// +/// # Example +/// +/// See module's documentation. +#[allow(non_camel_case_types, clippy::type_complexity)] +pub struct wasmer_metering_t { + pub(crate) inner: Arc u64 + Send + Sync>>>, +} + +/// Function type to represent a user-defined cost function +/// implemented in C. +/// +/// # Example +/// +/// See module's documentation. +#[allow(non_camel_case_types)] +pub type wasmer_metering_cost_function_t = + extern "C" fn(wasm_operator: wasmer_parser_operator_t) -> u64; + +/// Creates a new metering middleware with an initial limit, i.e. a +/// total number of operators to execute (regarding their respective +/// cost), in addition to a cost function. The cost function defines +/// the cost of an operation, that will decrease the initial limit. +/// +/// # Example +/// +/// See module's documentation. +#[no_mangle] +pub extern "C" fn wasmer_metering_new( + initial_limit: u64, + cost_function: wasmer_metering_cost_function_t, +) -> Box { + let cost_function = move |operator: &Operator| -> u64 { cost_function(operator.into()) }; + + Box::new(wasmer_metering_t { + inner: Arc::new(Metering::new(initial_limit, Box::new(cost_function))), + }) +} + +/// Deletes a [`wasmer_metering_t`]. +/// +/// # Example +/// +/// See module's documentation. +#[no_mangle] +pub extern "C" fn wasmer_metering_delete(_metering: Option>) {} + +/// Returns the remaining metering points. `u64::MAX` means +/// points are exhausted, otherwise it returns the number of +/// points. Notice that it could include zero! Zero doesn't mean +/// points are exhausted _yet_. +/// +/// # Example +/// +/// See module's documentation. +#[no_mangle] +pub unsafe extern "C" fn wasmer_metering_get_remaining_points( + instance: &mut wasm_instance_t, +) -> u64 { + match get_remaining_points(&mut instance.store.store_mut(), &instance.inner) { + MeteringPoints::Remaining(value) => value, + MeteringPoints::Exhausted => std::u64::MAX, + } +} + +/// Returns true if the remaning points are exhausted, false otherwise. +/// +/// # Example +/// +/// See module's documentation. +#[no_mangle] +pub unsafe extern "C" fn wasmer_metering_points_are_exhausted( + instance: &mut wasm_instance_t, +) -> bool { + matches!( + get_remaining_points(&mut instance.store.store_mut(), &instance.inner), + MeteringPoints::Exhausted, + ) +} + +/// Set a new amount of points for the given metering middleware. +/// +/// # Example +/// +/// This example only illustrates the +/// `wasmer_metering_set_remaining_points` function, as the number of +/// points aren't updated by the WebAssembly module execution +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// // Define a dummy “cost function”. +/// uint64_t cost_function(wasmer_parser_operator_t wasm_operator) { +/// switch(wasm_operator) { +/// default: +/// return 0; +/// } +/// } +/// +/// int main() { +/// // Set the initial amount of points to 7. +/// wasmer_metering_t* metering = wasmer_metering_new(7, cost_function); +/// +/// // Consume `metering` to produce `middleware`. +/// wasmer_middleware_t* middleware = wasmer_metering_as_middleware(metering); +/// +/// // Create the configuration (which consumes `middleware`), +/// // the engine, and the store. +/// wasm_config_t* config = wasm_config_new(); +/// wasm_config_push_middleware(config, middleware); +/// +/// wasm_engine_t* engine = wasm_engine_new_with_config(config); +/// +/// wasm_store_t* store = wasm_store_new(engine); +/// +/// // Create the module and instantiate it. +/// wasm_byte_vec_t wat; +/// wasmer_byte_vec_new_from_string(&wat, "(module)"); +/// wasm_byte_vec_t wasm; +/// wat2wasm(&wat, &wasm); +/// +/// wasm_module_t* module = wasm_module_new(store, &wasm); +/// assert(module); +/// +/// wasm_extern_vec_t imports = WASM_EMPTY_VEC; +/// wasm_trap_t* trap = NULL; +/// wasm_instance_t* instance = wasm_instance_new(store, module, &imports, &trap); +/// assert(instance); +/// +/// // Read the number of points. +/// assert(wasmer_metering_get_remaining_points(instance) == 7); +/// +/// // Set a new number of points. +/// wasmer_metering_set_remaining_points(instance, 42); +/// +/// // Read the number of points. +/// assert(wasmer_metering_get_remaining_points(instance) == 42); +/// +/// wasm_instance_delete(instance); +/// wasm_module_delete(module); +/// wasm_store_delete(store); +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[no_mangle] +pub unsafe extern "C" fn wasmer_metering_set_remaining_points( + instance: &mut wasm_instance_t, + new_limit: u64, +) { + set_remaining_points(&mut instance.store.store_mut(), &instance.inner, new_limit); +} + +/// Transforms a [`wasmer_metering_t`] into a generic +/// [`wasmer_middleware_t`], to then be pushed in the configuration with +/// [`wasm_config_push_middleware`][super::wasm_config_push_middleware]. +/// +/// This function takes ownership of `metering`. +/// +/// # Example +/// +/// See module's documentation. +#[no_mangle] +pub extern "C" fn wasmer_metering_as_middleware( + metering: Option>, +) -> Option> { + let metering = metering?; + + Some(Box::new(wasmer_middleware_t { + inner: metering.inner, + })) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/middlewares/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/middlewares/mod.rs new file mode 100644 index 0000000..238154b --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/middlewares/mod.rs @@ -0,0 +1,40 @@ +//! Unstable non-standard Wasmer-specific types to manipulate module +//! middlewares. + +pub mod metering; + +use super::super::engine::wasm_config_t; +use std::sync::Arc; +use wasmer_api::ModuleMiddleware; + +#[cfg(all(feature = "middlewares", not(feature = "compiler")))] +compile_error!("The `middlewares` feature requires the `compiler` feature to be turned on"); + +/// Opaque representing any kind of middleware. +/// +/// Used by `wasm_config_push_middleware`. A specific middleware is +/// transformed into this type to get a generic middleware. See for +/// example +/// [`wasmer_metering_as_middleware`][metering::wasmer_metering_as_middleware]. +#[derive(Debug)] +#[allow(non_camel_case_types)] +pub struct wasmer_middleware_t { + pub(in crate::wasm_c_api) inner: Arc, +} + +/// Updates the configuration to add a module middleware. +/// +/// This function takes ownership of `middleware`. +/// +/// This is a Wasmer-specific function. +/// +/// # Example +/// +/// See the documentation of the [`metering`] module. +#[no_mangle] +pub extern "C" fn wasm_config_push_middleware( + config: &mut wasm_config_t, + middleware: Box, +) { + config.middlewares.push(*middleware); +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/mod.rs new file mode 100644 index 0000000..be245f1 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/mod.rs @@ -0,0 +1,10 @@ +pub mod engine; +pub mod features; +#[cfg(feature = "middlewares")] +pub mod middlewares; +pub mod module; +#[cfg(feature = "compiler")] +pub mod parser; +pub mod target_lexicon; +#[cfg(feature = "wasi")] +pub mod wasi; diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/module.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/module.rs new file mode 100644 index 0000000..7fdbf88 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/module.rs @@ -0,0 +1,181 @@ +//! Unstable non-standard Wasmer-specific extensions to the Wasm C API. + +use super::super::engine::wasm_engine_t; +use super::super::module::wasm_module_t; +use super::super::types::{wasm_byte_vec_t, wasm_name_t}; +use std::ptr; +use std::str; +use wasmer_api::Module; + +/// Unstable non-standard Wasmer-specific API to get the module's +/// name, otherwise `out->size` is set to `0` and `out->data` to +/// `NULL`. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the engine and the store. +/// wasm_engine_t* engine = wasm_engine_new(); +/// wasm_store_t* store = wasm_store_new(engine); +/// +/// // Create a WebAssembly module from a WAT definition. +/// wasm_byte_vec_t wat; +/// wasmer_byte_vec_new_from_string(&wat, "(module $moduleName)"); +/// // ^~~~~~~~~~~ that's the name! +/// wasm_byte_vec_t wasm; +/// wat2wasm(&wat, &wasm); +/// +/// // Create the module. +/// wasm_module_t* module = wasm_module_new(store, &wasm); +/// +/// // Read the module's name. +/// wasm_name_t name; +/// wasmer_module_name(module, &name); +/// +/// // It works! +/// wasmer_assert_name(&name, "moduleName"); +/// +/// // Free everything. +/// wasm_byte_vec_delete(&name); +/// wasm_module_delete(module); +/// wasm_byte_vec_delete(&wasm); +/// wasm_byte_vec_delete(&wat); +/// wasm_store_delete(store); +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[no_mangle] +pub unsafe extern "C" fn wasmer_module_name( + module: &wasm_module_t, + // own + out: &mut wasm_name_t, +) { + let name = match module.inner.name() { + Some(name) => name, + None => { + out.data = ptr::null_mut(); + out.size = 0; + + return; + } + }; + + out.set_buffer(name.as_bytes().to_vec()); +} + +/// Unstable non-standard Wasmer-specific API to set the module's +/// name. The function returns `true` if the name has been updated, +/// `false` otherwise. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the engine and the store. +/// wasm_engine_t* engine = wasm_engine_new(); +/// wasm_store_t* store = wasm_store_new(engine); +/// +/// // Create a WebAssembly module from a WAT definition. +/// wasm_byte_vec_t wat; +/// wasmer_byte_vec_new_from_string(&wat, "(module)"); +/// wasm_byte_vec_t wasm; +/// wat2wasm(&wat, &wasm); +/// +/// // Create the module. +/// wasm_module_t* module = wasm_module_new(store, &wasm); +/// +/// // Read the module's name. There is none for the moment. +/// { +/// wasm_name_t name; +/// wasmer_module_name(module, &name); +/// +/// assert(name.size == 0); +/// } +/// +/// // So, let's set a new name. +/// { +/// wasm_name_t name; +/// wasmer_byte_vec_new_from_string(&name, "hello"); +/// wasmer_module_set_name(module, &name); +/// } +/// +/// // And now, let's see the new name. +/// { +/// wasm_name_t name; +/// wasmer_module_name(module, &name); +/// +/// // It works! +/// wasmer_assert_name(&name, "hello"); +/// +/// wasm_byte_vec_delete(&name); +/// } +/// +/// // Free everything. +/// wasm_module_delete(module); +/// wasm_byte_vec_delete(&wasm); +/// wasm_byte_vec_delete(&wat); +/// wasm_store_delete(store); +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[no_mangle] +pub unsafe extern "C" fn wasmer_module_set_name( + module: &mut wasm_module_t, + // own + name: &wasm_name_t, +) -> bool { + let name = match str::from_utf8(name.as_slice()) { + Ok(name) => name, + Err(_) => return false, // not ideal! + }; + + module.inner.set_name(name) +} + +/// A WebAssembly module contains stateless WebAssembly code that has +/// already been compiled and can be instantiated multiple times. +/// +/// Creates a new WebAssembly Module using the provided engine, +/// respecting its configuration. +/// +/// ## Security +/// +/// Before the code is compiled, it will be validated using the engine +/// features. +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub unsafe extern "C" fn wasmer_module_new( + engine: Option<&mut wasm_engine_t>, + bytes: Option<&wasm_byte_vec_t>, +) -> Option> { + let engine: wasmer_api::Engine = engine?.inner.clone().into(); + let bytes = bytes?; + + let module = c_try!(Module::from_binary(&engine, bytes.as_slice())); + + Some(Box::new(wasm_module_t { inner: module })) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/parser/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/parser/mod.rs new file mode 100644 index 0000000..f4086e5 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/parser/mod.rs @@ -0,0 +1,3 @@ +//! Unstable non-standard Wasmer-specific types about WebAssembly parser. + +pub mod operator; diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/parser/operator.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/parser/operator.rs new file mode 100644 index 0000000..8a50317 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/parser/operator.rs @@ -0,0 +1,1080 @@ +use wasmer_api::wasmparser::Operator; + +#[repr(C)] +#[allow(non_camel_case_types)] +pub enum wasmer_parser_operator_t { + Unreachable, + Nop, + Block, + Loop, + If, + Else, + Try, + Catch, + CatchAll, + Delegate, + Throw, + Rethrow, + Unwind, + End, + Br, + BrIf, + BrTable, + Return, + Call, + CallIndirect, + ReturnCall, + ReturnCallIndirect, + Drop, + Select, + TypedSelect, + LocalGet, + LocalSet, + LocalTee, + GlobalGet, + GlobalSet, + I32Load, + I64Load, + F32Load, + F64Load, + I32Load8S, + I32Load8U, + I32Load16S, + I32Load16U, + I64Load8S, + I64Load8U, + I64Load16S, + I64Load16U, + I64Load32S, + I64Load32U, + I32Store, + I64Store, + F32Store, + F64Store, + I32Store8, + I32Store16, + I64Store8, + I64Store16, + I64Store32, + MemorySize, + MemoryGrow, + I32Const, + I64Const, + F32Const, + F64Const, + RefNull, + RefIsNull, + RefFunc, + I32Eqz, + I32Eq, + I32Ne, + I32LtS, + I32LtU, + I32GtS, + I32GtU, + I32LeS, + I32LeU, + I32GeS, + I32GeU, + I64Eqz, + I64Eq, + I64Ne, + I64LtS, + I64LtU, + I64GtS, + I64GtU, + I64LeS, + I64LeU, + I64GeS, + I64GeU, + F32Eq, + F32Ne, + F32Lt, + F32Gt, + F32Le, + F32Ge, + F64Eq, + F64Ne, + F64Lt, + F64Gt, + F64Le, + F64Ge, + I32Clz, + I32Ctz, + I32Popcnt, + I32Add, + I32Sub, + I32Mul, + I32DivS, + I32DivU, + I32RemS, + I32RemU, + I32And, + I32Or, + I32Xor, + I32Shl, + I32ShrS, + I32ShrU, + I32Rotl, + I32Rotr, + I64Clz, + I64Ctz, + I64Popcnt, + I64Add, + I64Sub, + I64Mul, + I64DivS, + I64DivU, + I64RemS, + I64RemU, + I64And, + I64Or, + I64Xor, + I64Shl, + I64ShrS, + I64ShrU, + I64Rotl, + I64Rotr, + F32Abs, + F32Neg, + F32Ceil, + F32Floor, + F32Trunc, + F32Nearest, + F32Sqrt, + F32Add, + F32Sub, + F32Mul, + F32Div, + F32Min, + F32Max, + F32Copysign, + F64Abs, + F64Neg, + F64Ceil, + F64Floor, + F64Trunc, + F64Nearest, + F64Sqrt, + F64Add, + F64Sub, + F64Mul, + F64Div, + F64Min, + F64Max, + F64Copysign, + I32WrapI64, + I32TruncF32S, + I32TruncF32U, + I32TruncF64S, + I32TruncF64U, + I64ExtendI32S, + I64ExtendI32U, + I64TruncF32S, + I64TruncF32U, + I64TruncF64S, + I64TruncF64U, + F32ConvertI32S, + F32ConvertI32U, + F32ConvertI64S, + F32ConvertI64U, + F32DemoteF64, + F64ConvertI32S, + F64ConvertI32U, + F64ConvertI64S, + F64ConvertI64U, + F64PromoteF32, + I32ReinterpretF32, + I64ReinterpretF64, + F32ReinterpretI32, + F64ReinterpretI64, + I32Extend8S, + I32Extend16S, + I64Extend8S, + I64Extend16S, + I64Extend32S, + I32TruncSatF32S, + I32TruncSatF32U, + I32TruncSatF64S, + I32TruncSatF64U, + I64TruncSatF32S, + I64TruncSatF32U, + I64TruncSatF64S, + I64TruncSatF64U, + MemoryInit, + DataDrop, + MemoryCopy, + MemoryFill, + TableInit, + ElemDrop, + TableCopy, + TableFill, + TableGet, + TableSet, + TableGrow, + TableSize, + MemoryAtomicNotify, + MemoryAtomicWait32, + MemoryAtomicWait64, + AtomicFence, + I32AtomicLoad, + I64AtomicLoad, + I32AtomicLoad8U, + I32AtomicLoad16U, + I64AtomicLoad8U, + I64AtomicLoad16U, + I64AtomicLoad32U, + I32AtomicStore, + I64AtomicStore, + I32AtomicStore8, + I32AtomicStore16, + I64AtomicStore8, + I64AtomicStore16, + I64AtomicStore32, + I32AtomicRmwAdd, + I64AtomicRmwAdd, + I32AtomicRmw8AddU, + I32AtomicRmw16AddU, + I64AtomicRmw8AddU, + I64AtomicRmw16AddU, + I64AtomicRmw32AddU, + I32AtomicRmwSub, + I64AtomicRmwSub, + I32AtomicRmw8SubU, + I32AtomicRmw16SubU, + I64AtomicRmw8SubU, + I64AtomicRmw16SubU, + I64AtomicRmw32SubU, + I32AtomicRmwAnd, + I64AtomicRmwAnd, + I32AtomicRmw8AndU, + I32AtomicRmw16AndU, + I64AtomicRmw8AndU, + I64AtomicRmw16AndU, + I64AtomicRmw32AndU, + I32AtomicRmwOr, + I64AtomicRmwOr, + I32AtomicRmw8OrU, + I32AtomicRmw16OrU, + I64AtomicRmw8OrU, + I64AtomicRmw16OrU, + I64AtomicRmw32OrU, + I32AtomicRmwXor, + I64AtomicRmwXor, + I32AtomicRmw8XorU, + I32AtomicRmw16XorU, + I64AtomicRmw8XorU, + I64AtomicRmw16XorU, + I64AtomicRmw32XorU, + I32AtomicRmwXchg, + I64AtomicRmwXchg, + I32AtomicRmw8XchgU, + I32AtomicRmw16XchgU, + I64AtomicRmw8XchgU, + I64AtomicRmw16XchgU, + I64AtomicRmw32XchgU, + I32AtomicRmwCmpxchg, + I64AtomicRmwCmpxchg, + I32AtomicRmw8CmpxchgU, + I32AtomicRmw16CmpxchgU, + I64AtomicRmw8CmpxchgU, + I64AtomicRmw16CmpxchgU, + I64AtomicRmw32CmpxchgU, + V128Load, + V128Store, + V128Const, + I8x16Splat, + I8x16ExtractLaneS, + I8x16ExtractLaneU, + I8x16ReplaceLane, + I16x8Splat, + I16x8ExtractLaneS, + I16x8ExtractLaneU, + I16x8ReplaceLane, + I32x4Splat, + I32x4ExtractLane, + I32x4ReplaceLane, + I64x2Splat, + I64x2ExtractLane, + I64x2ReplaceLane, + F32x4Splat, + F32x4ExtractLane, + F32x4ReplaceLane, + F64x2Splat, + F64x2ExtractLane, + F64x2ReplaceLane, + I8x16Eq, + I8x16Ne, + I8x16LtS, + I8x16LtU, + I8x16GtS, + I8x16GtU, + I8x16LeS, + I8x16LeU, + I8x16GeS, + I8x16GeU, + I16x8Eq, + I16x8Ne, + I16x8LtS, + I16x8LtU, + I16x8GtS, + I16x8GtU, + I16x8LeS, + I16x8LeU, + I16x8GeS, + I16x8GeU, + I32x4Eq, + I32x4Ne, + I32x4LtS, + I32x4LtU, + I32x4GtS, + I32x4GtU, + I32x4LeS, + I32x4LeU, + I32x4GeS, + I32x4GeU, + I64x2Eq, + I64x2Ne, + I64x2LtS, + I64x2GtS, + I64x2LeS, + I64x2GeS, + F32x4Eq, + F32x4Ne, + F32x4Lt, + F32x4Gt, + F32x4Le, + F32x4Ge, + F64x2Eq, + F64x2Ne, + F64x2Lt, + F64x2Gt, + F64x2Le, + F64x2Ge, + V128Not, + V128And, + V128AndNot, + V128Or, + V128Xor, + V128Bitselect, + V128AnyTrue, + I8x16Abs, + I8x16Neg, + I8x16AllTrue, + I8x16Bitmask, + I8x16Shl, + I8x16ShrS, + I8x16ShrU, + I8x16Add, + I8x16AddSatS, + I8x16AddSatU, + I8x16Sub, + I8x16SubSatS, + I8x16SubSatU, + I8x16MinS, + I8x16MinU, + I8x16MaxS, + I8x16MaxU, + I8x16Popcnt, + I16x8Abs, + I16x8Neg, + I16x8AllTrue, + I16x8Bitmask, + I16x8Shl, + I16x8ShrS, + I16x8ShrU, + I16x8Add, + I16x8AddSatS, + I16x8AddSatU, + I16x8Sub, + I16x8SubSatS, + I16x8SubSatU, + I16x8Mul, + I16x8MinS, + I16x8MinU, + I16x8MaxS, + I16x8MaxU, + I16x8ExtAddPairwiseI8x16S, + I16x8ExtAddPairwiseI8x16U, + I32x4Abs, + I32x4Neg, + I32x4AllTrue, + I32x4Bitmask, + I32x4Shl, + I32x4ShrS, + I32x4ShrU, + I32x4Add, + I32x4Sub, + I32x4Mul, + I32x4MinS, + I32x4MinU, + I32x4MaxS, + I32x4MaxU, + I32x4DotI16x8S, + I32x4ExtAddPairwiseI16x8S, + I32x4ExtAddPairwiseI16x8U, + I64x2Abs, + I64x2Neg, + I64x2AllTrue, + I64x2Bitmask, + I64x2Shl, + I64x2ShrS, + I64x2ShrU, + I64x2Add, + I64x2Sub, + I64x2Mul, + F32x4Ceil, + F32x4Floor, + F32x4Trunc, + F32x4Nearest, + F64x2Ceil, + F64x2Floor, + F64x2Trunc, + F64x2Nearest, + F32x4Abs, + F32x4Neg, + F32x4Sqrt, + F32x4Add, + F32x4Sub, + F32x4Mul, + F32x4Div, + F32x4Min, + F32x4Max, + F32x4PMin, + F32x4PMax, + F64x2Abs, + F64x2Neg, + F64x2Sqrt, + F64x2Add, + F64x2Sub, + F64x2Mul, + F64x2Div, + F64x2Min, + F64x2Max, + F64x2PMin, + F64x2PMax, + I32x4TruncSatF32x4S, + I32x4TruncSatF32x4U, + F32x4ConvertI32x4S, + F32x4ConvertI32x4U, + I8x16Swizzle, + I8x16Shuffle, + V128Load8Splat, + V128Load16Splat, + V128Load32Splat, + V128Load32Zero, + V128Load64Splat, + V128Load64Zero, + I8x16NarrowI16x8S, + I8x16NarrowI16x8U, + I16x8NarrowI32x4S, + I16x8NarrowI32x4U, + I16x8ExtendLowI8x16S, + I16x8ExtendHighI8x16S, + I16x8ExtendLowI8x16U, + I16x8ExtendHighI8x16U, + I32x4ExtendLowI16x8S, + I32x4ExtendHighI16x8S, + I32x4ExtendLowI16x8U, + I32x4ExtendHighI16x8U, + I64x2ExtendLowI32x4S, + I64x2ExtendHighI32x4S, + I64x2ExtendLowI32x4U, + I64x2ExtendHighI32x4U, + I16x8ExtMulLowI8x16S, + I16x8ExtMulHighI8x16S, + I16x8ExtMulLowI8x16U, + I16x8ExtMulHighI8x16U, + I32x4ExtMulLowI16x8S, + I32x4ExtMulHighI16x8S, + I32x4ExtMulLowI16x8U, + I32x4ExtMulHighI16x8U, + I64x2ExtMulLowI32x4S, + I64x2ExtMulHighI32x4S, + I64x2ExtMulLowI32x4U, + I64x2ExtMulHighI32x4U, + V128Load8x8S, + V128Load8x8U, + V128Load16x4S, + V128Load16x4U, + V128Load32x2S, + V128Load32x2U, + V128Load8Lane, + V128Load16Lane, + V128Load32Lane, + V128Load64Lane, + V128Store8Lane, + V128Store16Lane, + V128Store32Lane, + V128Store64Lane, + I8x16RoundingAverageU, + I16x8RoundingAverageU, + I16x8Q15MulrSatS, + F32x4DemoteF64x2Zero, + F64x2PromoteLowF32x4, + F64x2ConvertLowI32x4S, + F64x2ConvertLowI32x4U, + I32x4TruncSatF64x2SZero, + I32x4TruncSatF64x2UZero, + I8x16RelaxedSwizzle, + I32x4RelaxedTruncSatF32x4S, + I32x4RelaxedTruncSatF32x4U, + I32x4RelaxedTruncSatF64x2SZero, + I32x4RelaxedTruncSatF64x2UZero, + F32x4Fma, + F32x4Fms, + F64x2Fma, + F64x2Fms, + I8x16LaneSelect, + I16x8LaneSelect, + I32x4LaneSelect, + I64x2LaneSelect, + F32x4RelaxedMin, + F32x4RelaxedMax, + F64x2RelaxedMin, + F64x2RelaxedMax, + I16x8RelaxedQ15mulrS, + I16x8DotI8x16I7x16S, + I32x4DotI8x16I7x16AddS, + F32x4RelaxedDotBf16x8AddF32x4, +} + +impl<'a> From<&Operator<'a>> for wasmer_parser_operator_t { + fn from(operator: &Operator<'a>) -> Self { + use Operator as O; + + match operator { + O::Unreachable => Self::Unreachable, + O::Nop => Self::Nop, + O::Block { .. } => Self::Block, + O::Loop { .. } => Self::Loop, + O::If { .. } => Self::If, + O::Else => Self::Else, + O::Try { .. } => Self::Try, + O::Catch { .. } => Self::Catch, + O::CatchAll => Self::CatchAll, + O::Delegate { .. } => Self::Delegate, + O::Throw { .. } => Self::Throw, + O::Rethrow { .. } => Self::Rethrow, + // O::Unwind removed + O::End => Self::End, + O::Br { .. } => Self::Br, + O::BrIf { .. } => Self::BrIf, + O::BrTable { .. } => Self::BrTable, + O::Return => Self::Return, + O::Call { .. } => Self::Call, + O::CallIndirect { .. } => Self::CallIndirect, + O::ReturnCall { .. } => Self::ReturnCall, + O::ReturnCallIndirect { .. } => Self::ReturnCallIndirect, + O::Drop => Self::Drop, + O::Select => Self::Select, + O::TypedSelect { .. } => Self::TypedSelect, + O::LocalGet { .. } => Self::LocalGet, + O::LocalSet { .. } => Self::LocalSet, + O::LocalTee { .. } => Self::LocalTee, + O::GlobalGet { .. } => Self::GlobalGet, + O::GlobalSet { .. } => Self::GlobalSet, + O::I32Load { .. } => Self::I32Load, + O::I64Load { .. } => Self::I64Load, + O::F32Load { .. } => Self::F32Load, + O::F64Load { .. } => Self::F64Load, + O::I32Load8S { .. } => Self::I32Load8S, + O::I32Load8U { .. } => Self::I32Load8U, + O::I32Load16S { .. } => Self::I32Load16S, + O::I32Load16U { .. } => Self::I32Load16U, + O::I64Load8S { .. } => Self::I64Load8S, + O::I64Load8U { .. } => Self::I64Load8U, + O::I64Load16S { .. } => Self::I64Load16S, + O::I64Load16U { .. } => Self::I64Load16U, + O::I64Load32S { .. } => Self::I64Load32S, + O::I64Load32U { .. } => Self::I64Load32U, + O::I32Store { .. } => Self::I32Store, + O::I64Store { .. } => Self::I64Store, + O::F32Store { .. } => Self::F32Store, + O::F64Store { .. } => Self::F64Store, + O::I32Store8 { .. } => Self::I32Store8, + O::I32Store16 { .. } => Self::I32Store16, + O::I64Store8 { .. } => Self::I64Store8, + O::I64Store16 { .. } => Self::I64Store16, + O::I64Store32 { .. } => Self::I64Store32, + O::MemorySize { .. } => Self::MemorySize, + O::MemoryGrow { .. } => Self::MemoryGrow, + O::I32Const { .. } => Self::I32Const, + O::I64Const { .. } => Self::I64Const, + O::F32Const { .. } => Self::F32Const, + O::F64Const { .. } => Self::F64Const, + O::RefNull { .. } => Self::RefNull, + O::RefIsNull => Self::RefIsNull, + O::RefFunc { .. } => Self::RefFunc, + O::I32Eqz => Self::I32Eqz, + O::I32Eq => Self::I32Eq, + O::I32Ne => Self::I32Ne, + O::I32LtS => Self::I32LtS, + O::I32LtU => Self::I32LtU, + O::I32GtS => Self::I32GtS, + O::I32GtU => Self::I32GtU, + O::I32LeS => Self::I32LeS, + O::I32LeU => Self::I32LeU, + O::I32GeS => Self::I32GeS, + O::I32GeU => Self::I32GeU, + O::I64Eqz => Self::I64Eqz, + O::I64Eq => Self::I64Eq, + O::I64Ne => Self::I64Ne, + O::I64LtS => Self::I64LtS, + O::I64LtU => Self::I64LtU, + O::I64GtS => Self::I64GtS, + O::I64GtU => Self::I64GtU, + O::I64LeS => Self::I64LeS, + O::I64LeU => Self::I64LeU, + O::I64GeS => Self::I64GeS, + O::I64GeU => Self::I64GeU, + O::F32Eq => Self::F32Eq, + O::F32Ne => Self::F32Ne, + O::F32Lt => Self::F32Lt, + O::F32Gt => Self::F32Gt, + O::F32Le => Self::F32Le, + O::F32Ge => Self::F32Ge, + O::F64Eq => Self::F64Eq, + O::F64Ne => Self::F64Ne, + O::F64Lt => Self::F64Lt, + O::F64Gt => Self::F64Gt, + O::F64Le => Self::F64Le, + O::F64Ge => Self::F64Ge, + O::I32Clz => Self::I32Clz, + O::I32Ctz => Self::I32Ctz, + O::I32Popcnt => Self::I32Popcnt, + O::I32Add => Self::I32Add, + O::I32Sub => Self::I32Sub, + O::I32Mul => Self::I32Mul, + O::I32DivS => Self::I32DivS, + O::I32DivU => Self::I32DivU, + O::I32RemS => Self::I32RemS, + O::I32RemU => Self::I32RemU, + O::I32And => Self::I32And, + O::I32Or => Self::I32Or, + O::I32Xor => Self::I32Xor, + O::I32Shl => Self::I32Shl, + O::I32ShrS => Self::I32ShrS, + O::I32ShrU => Self::I32ShrU, + O::I32Rotl => Self::I32Rotl, + O::I32Rotr => Self::I32Rotr, + O::I64Clz => Self::I64Clz, + O::I64Ctz => Self::I64Ctz, + O::I64Popcnt => Self::I64Popcnt, + O::I64Add => Self::I64Add, + O::I64Sub => Self::I64Sub, + O::I64Mul => Self::I64Mul, + O::I64DivS => Self::I64DivS, + O::I64DivU => Self::I64DivU, + O::I64RemS => Self::I64RemS, + O::I64RemU => Self::I64RemU, + O::I64And => Self::I64And, + O::I64Or => Self::I64Or, + O::I64Xor => Self::I64Xor, + O::I64Shl => Self::I64Shl, + O::I64ShrS => Self::I64ShrS, + O::I64ShrU => Self::I64ShrU, + O::I64Rotl => Self::I64Rotl, + O::I64Rotr => Self::I64Rotr, + O::F32Abs => Self::F32Abs, + O::F32Neg => Self::F32Neg, + O::F32Ceil => Self::F32Ceil, + O::F32Floor => Self::F32Floor, + O::F32Trunc => Self::F32Trunc, + O::F32Nearest => Self::F32Nearest, + O::F32Sqrt => Self::F32Sqrt, + O::F32Add => Self::F32Add, + O::F32Sub => Self::F32Sub, + O::F32Mul => Self::F32Mul, + O::F32Div => Self::F32Div, + O::F32Min => Self::F32Min, + O::F32Max => Self::F32Max, + O::F32Copysign => Self::F32Copysign, + O::F64Abs => Self::F64Abs, + O::F64Neg => Self::F64Neg, + O::F64Ceil => Self::F64Ceil, + O::F64Floor => Self::F64Floor, + O::F64Trunc => Self::F64Trunc, + O::F64Nearest => Self::F64Nearest, + O::F64Sqrt => Self::F64Sqrt, + O::F64Add => Self::F64Add, + O::F64Sub => Self::F64Sub, + O::F64Mul => Self::F64Mul, + O::F64Div => Self::F64Div, + O::F64Min => Self::F64Min, + O::F64Max => Self::F64Max, + O::F64Copysign => Self::F64Copysign, + O::I32WrapI64 => Self::I32WrapI64, + O::I32TruncF32S => Self::I32TruncF32S, + O::I32TruncF32U => Self::I32TruncF32U, + O::I32TruncF64S => Self::I32TruncF64S, + O::I32TruncF64U => Self::I32TruncF64U, + O::I64ExtendI32S => Self::I64ExtendI32S, + O::I64ExtendI32U => Self::I64ExtendI32U, + O::I64TruncF32S => Self::I64TruncF32S, + O::I64TruncF32U => Self::I64TruncF32U, + O::I64TruncF64S => Self::I64TruncF64S, + O::I64TruncF64U => Self::I64TruncF64U, + O::F32ConvertI32S => Self::F32ConvertI32S, + O::F32ConvertI32U => Self::F32ConvertI32U, + O::F32ConvertI64S => Self::F32ConvertI64S, + O::F32ConvertI64U => Self::F32ConvertI64U, + O::F32DemoteF64 => Self::F32DemoteF64, + O::F64ConvertI32S => Self::F64ConvertI32S, + O::F64ConvertI32U => Self::F64ConvertI32U, + O::F64ConvertI64S => Self::F64ConvertI64S, + O::F64ConvertI64U => Self::F64ConvertI64U, + O::F64PromoteF32 => Self::F64PromoteF32, + O::I32ReinterpretF32 => Self::I32ReinterpretF32, + O::I64ReinterpretF64 => Self::I64ReinterpretF64, + O::F32ReinterpretI32 => Self::F32ReinterpretI32, + O::F64ReinterpretI64 => Self::F64ReinterpretI64, + O::I32Extend8S => Self::I32Extend8S, + O::I32Extend16S => Self::I32Extend16S, + O::I64Extend8S => Self::I64Extend8S, + O::I64Extend16S => Self::I64Extend16S, + O::I64Extend32S => Self::I64Extend32S, + O::I32TruncSatF32S => Self::I32TruncSatF32S, + O::I32TruncSatF32U => Self::I32TruncSatF32U, + O::I32TruncSatF64S => Self::I32TruncSatF64S, + O::I32TruncSatF64U => Self::I32TruncSatF64U, + O::I64TruncSatF32S => Self::I64TruncSatF32S, + O::I64TruncSatF32U => Self::I64TruncSatF32U, + O::I64TruncSatF64S => Self::I64TruncSatF64S, + O::I64TruncSatF64U => Self::I64TruncSatF64U, + O::MemoryInit { .. } => Self::MemoryInit, + O::DataDrop { .. } => Self::DataDrop, + O::MemoryCopy { .. } => Self::MemoryCopy, + O::MemoryFill { .. } => Self::MemoryFill, + O::TableInit { .. } => Self::TableInit, + O::ElemDrop { .. } => Self::ElemDrop, + O::TableCopy { .. } => Self::TableCopy, + O::TableFill { .. } => Self::TableFill, + O::TableGet { .. } => Self::TableGet, + O::TableSet { .. } => Self::TableSet, + O::TableGrow { .. } => Self::TableGrow, + O::TableSize { .. } => Self::TableSize, + O::MemoryAtomicNotify { .. } => Self::MemoryAtomicNotify, + O::MemoryAtomicWait32 { .. } => Self::MemoryAtomicWait32, + O::MemoryAtomicWait64 { .. } => Self::MemoryAtomicWait64, + O::AtomicFence { .. } => Self::AtomicFence, + O::I32AtomicLoad { .. } => Self::I32AtomicLoad, + O::I64AtomicLoad { .. } => Self::I64AtomicLoad, + O::I32AtomicLoad8U { .. } => Self::I32AtomicLoad8U, + O::I32AtomicLoad16U { .. } => Self::I32AtomicLoad16U, + O::I64AtomicLoad8U { .. } => Self::I64AtomicLoad8U, + O::I64AtomicLoad16U { .. } => Self::I64AtomicLoad16U, + O::I64AtomicLoad32U { .. } => Self::I64AtomicLoad32U, + O::I32AtomicStore { .. } => Self::I32AtomicStore, + O::I64AtomicStore { .. } => Self::I64AtomicStore, + O::I32AtomicStore8 { .. } => Self::I32AtomicStore8, + O::I32AtomicStore16 { .. } => Self::I32AtomicStore16, + O::I64AtomicStore8 { .. } => Self::I64AtomicStore8, + O::I64AtomicStore16 { .. } => Self::I64AtomicStore16, + O::I64AtomicStore32 { .. } => Self::I64AtomicStore32, + O::I32AtomicRmwAdd { .. } => Self::I32AtomicRmwAdd, + O::I64AtomicRmwAdd { .. } => Self::I64AtomicRmwAdd, + O::I32AtomicRmw8AddU { .. } => Self::I32AtomicRmw8AddU, + O::I32AtomicRmw16AddU { .. } => Self::I32AtomicRmw16AddU, + O::I64AtomicRmw8AddU { .. } => Self::I64AtomicRmw8AddU, + O::I64AtomicRmw16AddU { .. } => Self::I64AtomicRmw16AddU, + O::I64AtomicRmw32AddU { .. } => Self::I64AtomicRmw32AddU, + O::I32AtomicRmwSub { .. } => Self::I32AtomicRmwSub, + O::I64AtomicRmwSub { .. } => Self::I64AtomicRmwSub, + O::I32AtomicRmw8SubU { .. } => Self::I32AtomicRmw8SubU, + O::I32AtomicRmw16SubU { .. } => Self::I32AtomicRmw16SubU, + O::I64AtomicRmw8SubU { .. } => Self::I64AtomicRmw8SubU, + O::I64AtomicRmw16SubU { .. } => Self::I64AtomicRmw16SubU, + O::I64AtomicRmw32SubU { .. } => Self::I64AtomicRmw32SubU, + O::I32AtomicRmwAnd { .. } => Self::I32AtomicRmwAnd, + O::I64AtomicRmwAnd { .. } => Self::I64AtomicRmwAnd, + O::I32AtomicRmw8AndU { .. } => Self::I32AtomicRmw8AndU, + O::I32AtomicRmw16AndU { .. } => Self::I32AtomicRmw16AndU, + O::I64AtomicRmw8AndU { .. } => Self::I64AtomicRmw8AndU, + O::I64AtomicRmw16AndU { .. } => Self::I64AtomicRmw16AndU, + O::I64AtomicRmw32AndU { .. } => Self::I64AtomicRmw32AndU, + O::I32AtomicRmwOr { .. } => Self::I32AtomicRmwOr, + O::I64AtomicRmwOr { .. } => Self::I64AtomicRmwOr, + O::I32AtomicRmw8OrU { .. } => Self::I32AtomicRmw8OrU, + O::I32AtomicRmw16OrU { .. } => Self::I32AtomicRmw16OrU, + O::I64AtomicRmw8OrU { .. } => Self::I64AtomicRmw8OrU, + O::I64AtomicRmw16OrU { .. } => Self::I64AtomicRmw16OrU, + O::I64AtomicRmw32OrU { .. } => Self::I64AtomicRmw32OrU, + O::I32AtomicRmwXor { .. } => Self::I32AtomicRmwXor, + O::I64AtomicRmwXor { .. } => Self::I64AtomicRmwXor, + O::I32AtomicRmw8XorU { .. } => Self::I32AtomicRmw8XorU, + O::I32AtomicRmw16XorU { .. } => Self::I32AtomicRmw16XorU, + O::I64AtomicRmw8XorU { .. } => Self::I64AtomicRmw8XorU, + O::I64AtomicRmw16XorU { .. } => Self::I64AtomicRmw16XorU, + O::I64AtomicRmw32XorU { .. } => Self::I64AtomicRmw32XorU, + O::I32AtomicRmwXchg { .. } => Self::I32AtomicRmwXchg, + O::I64AtomicRmwXchg { .. } => Self::I64AtomicRmwXchg, + O::I32AtomicRmw8XchgU { .. } => Self::I32AtomicRmw8XchgU, + O::I32AtomicRmw16XchgU { .. } => Self::I32AtomicRmw16XchgU, + O::I64AtomicRmw8XchgU { .. } => Self::I64AtomicRmw8XchgU, + O::I64AtomicRmw16XchgU { .. } => Self::I64AtomicRmw16XchgU, + O::I64AtomicRmw32XchgU { .. } => Self::I64AtomicRmw32XchgU, + O::I32AtomicRmwCmpxchg { .. } => Self::I32AtomicRmwCmpxchg, + O::I64AtomicRmwCmpxchg { .. } => Self::I64AtomicRmwCmpxchg, + O::I32AtomicRmw8CmpxchgU { .. } => Self::I32AtomicRmw8CmpxchgU, + O::I32AtomicRmw16CmpxchgU { .. } => Self::I32AtomicRmw16CmpxchgU, + O::I64AtomicRmw8CmpxchgU { .. } => Self::I64AtomicRmw8CmpxchgU, + O::I64AtomicRmw16CmpxchgU { .. } => Self::I64AtomicRmw16CmpxchgU, + O::I64AtomicRmw32CmpxchgU { .. } => Self::I64AtomicRmw32CmpxchgU, + O::V128Load { .. } => Self::V128Load, + O::V128Store { .. } => Self::V128Store, + O::V128Const { .. } => Self::V128Const, + O::I8x16Splat => Self::I8x16Splat, + O::I8x16ExtractLaneS { .. } => Self::I8x16ExtractLaneS, + O::I8x16ExtractLaneU { .. } => Self::I8x16ExtractLaneU, + O::I8x16ReplaceLane { .. } => Self::I8x16ReplaceLane, + O::I16x8Splat => Self::I16x8Splat, + O::I16x8ExtractLaneS { .. } => Self::I16x8ExtractLaneS, + O::I16x8ExtractLaneU { .. } => Self::I16x8ExtractLaneU, + O::I16x8ReplaceLane { .. } => Self::I16x8ReplaceLane, + O::I32x4Splat => Self::I32x4Splat, + O::I32x4ExtractLane { .. } => Self::I32x4ExtractLane, + O::I32x4ReplaceLane { .. } => Self::I32x4ReplaceLane, + O::I64x2Splat => Self::I64x2Splat, + O::I64x2ExtractLane { .. } => Self::I64x2ExtractLane, + O::I64x2ReplaceLane { .. } => Self::I64x2ReplaceLane, + O::F32x4Splat => Self::F32x4Splat, + O::F32x4ExtractLane { .. } => Self::F32x4ExtractLane, + O::F32x4ReplaceLane { .. } => Self::F32x4ReplaceLane, + O::F64x2Splat => Self::F64x2Splat, + O::F64x2ExtractLane { .. } => Self::F64x2ExtractLane, + O::F64x2ReplaceLane { .. } => Self::F64x2ReplaceLane, + O::I8x16Eq => Self::I8x16Eq, + O::I8x16Ne => Self::I8x16Ne, + O::I8x16LtS => Self::I8x16LtS, + O::I8x16LtU => Self::I8x16LtU, + O::I8x16GtS => Self::I8x16GtS, + O::I8x16GtU => Self::I8x16GtU, + O::I8x16LeS => Self::I8x16LeS, + O::I8x16LeU => Self::I8x16LeU, + O::I8x16GeS => Self::I8x16GeS, + O::I8x16GeU => Self::I8x16GeU, + O::I16x8Eq => Self::I16x8Eq, + O::I16x8Ne => Self::I16x8Ne, + O::I16x8LtS => Self::I16x8LtS, + O::I16x8LtU => Self::I16x8LtU, + O::I16x8GtS => Self::I16x8GtS, + O::I16x8GtU => Self::I16x8GtU, + O::I16x8LeS => Self::I16x8LeS, + O::I16x8LeU => Self::I16x8LeU, + O::I16x8GeS => Self::I16x8GeS, + O::I16x8GeU => Self::I16x8GeU, + O::I32x4Eq => Self::I32x4Eq, + O::I32x4Ne => Self::I32x4Ne, + O::I32x4LtS => Self::I32x4LtS, + O::I32x4LtU => Self::I32x4LtU, + O::I32x4GtS => Self::I32x4GtS, + O::I32x4GtU => Self::I32x4GtU, + O::I32x4LeS => Self::I32x4LeS, + O::I32x4LeU => Self::I32x4LeU, + O::I32x4GeS => Self::I32x4GeS, + O::I32x4GeU => Self::I32x4GeU, + O::I64x2Eq => Self::I64x2Eq, + O::I64x2Ne => Self::I64x2Ne, + O::I64x2LtS => Self::I64x2LtS, + O::I64x2GtS => Self::I64x2GtS, + O::I64x2LeS => Self::I64x2LeS, + O::I64x2GeS => Self::I64x2GeS, + O::F32x4Eq => Self::F32x4Eq, + O::F32x4Ne => Self::F32x4Ne, + O::F32x4Lt => Self::F32x4Lt, + O::F32x4Gt => Self::F32x4Gt, + O::F32x4Le => Self::F32x4Le, + O::F32x4Ge => Self::F32x4Ge, + O::F64x2Eq => Self::F64x2Eq, + O::F64x2Ne => Self::F64x2Ne, + O::F64x2Lt => Self::F64x2Lt, + O::F64x2Gt => Self::F64x2Gt, + O::F64x2Le => Self::F64x2Le, + O::F64x2Ge => Self::F64x2Ge, + O::V128Not => Self::V128Not, + O::V128And => Self::V128And, + O::V128AndNot => Self::V128AndNot, + O::V128Or => Self::V128Or, + O::V128Xor => Self::V128Xor, + O::V128Bitselect => Self::V128Bitselect, + O::V128AnyTrue => Self::V128AnyTrue, + O::I8x16Popcnt => Self::I8x16Popcnt, + O::I8x16Abs => Self::I8x16Abs, + O::I8x16Neg => Self::I8x16Neg, + O::I8x16AllTrue => Self::I8x16AllTrue, + O::I8x16Bitmask => Self::I8x16Bitmask, + O::I8x16Shl => Self::I8x16Shl, + O::I8x16ShrS => Self::I8x16ShrS, + O::I8x16ShrU => Self::I8x16ShrU, + O::I8x16Add => Self::I8x16Add, + O::I8x16AddSatS => Self::I8x16AddSatS, + O::I8x16AddSatU => Self::I8x16AddSatU, + O::I8x16Sub => Self::I8x16Sub, + O::I8x16SubSatS => Self::I8x16SubSatS, + O::I8x16SubSatU => Self::I8x16SubSatU, + O::I8x16MinS => Self::I8x16MinS, + O::I8x16MinU => Self::I8x16MinU, + O::I8x16MaxS => Self::I8x16MaxS, + O::I8x16MaxU => Self::I8x16MaxU, + O::I16x8Abs => Self::I16x8Abs, + O::I16x8Neg => Self::I16x8Neg, + O::I16x8AllTrue => Self::I16x8AllTrue, + O::I16x8Bitmask => Self::I16x8Bitmask, + O::I16x8Shl => Self::I16x8Shl, + O::I16x8ShrS => Self::I16x8ShrS, + O::I16x8ShrU => Self::I16x8ShrU, + O::I16x8Add => Self::I16x8Add, + O::I16x8AddSatS => Self::I16x8AddSatS, + O::I16x8AddSatU => Self::I16x8AddSatU, + O::I16x8Sub => Self::I16x8Sub, + O::I16x8SubSatS => Self::I16x8SubSatS, + O::I16x8SubSatU => Self::I16x8SubSatU, + O::I16x8Mul => Self::I16x8Mul, + O::I16x8MinS => Self::I16x8MinS, + O::I16x8MinU => Self::I16x8MinU, + O::I16x8MaxS => Self::I16x8MaxS, + O::I16x8MaxU => Self::I16x8MaxU, + O::I16x8ExtAddPairwiseI8x16S => Self::I16x8ExtAddPairwiseI8x16S, + O::I16x8ExtAddPairwiseI8x16U => Self::I16x8ExtAddPairwiseI8x16U, + O::I32x4Abs => Self::I32x4Abs, + O::I32x4Neg => Self::I32x4Neg, + O::I32x4AllTrue => Self::I32x4AllTrue, + O::I32x4Bitmask => Self::I32x4Bitmask, + O::I32x4Shl => Self::I32x4Shl, + O::I32x4ShrS => Self::I32x4ShrS, + O::I32x4ShrU => Self::I32x4ShrU, + O::I32x4Add => Self::I32x4Add, + O::I32x4Sub => Self::I32x4Sub, + O::I32x4Mul => Self::I32x4Mul, + O::I32x4MinS => Self::I32x4MinS, + O::I32x4MinU => Self::I32x4MinU, + O::I32x4MaxS => Self::I32x4MaxS, + O::I32x4MaxU => Self::I32x4MaxU, + O::I32x4DotI16x8S => Self::I32x4DotI16x8S, + O::I32x4ExtAddPairwiseI16x8S => Self::I32x4ExtAddPairwiseI16x8S, + O::I32x4ExtAddPairwiseI16x8U => Self::I32x4ExtAddPairwiseI16x8U, + O::I64x2Abs => Self::I64x2Abs, + O::I64x2Neg => Self::I64x2Neg, + O::I64x2AllTrue => Self::I64x2AllTrue, + O::I64x2Bitmask => Self::I64x2Bitmask, + O::I64x2Shl => Self::I64x2Shl, + O::I64x2ShrS => Self::I64x2ShrS, + O::I64x2ShrU => Self::I64x2ShrU, + O::I64x2Add => Self::I64x2Add, + O::I64x2Sub => Self::I64x2Sub, + O::I64x2Mul => Self::I64x2Mul, + O::F32x4Ceil => Self::F32x4Ceil, + O::F32x4Floor => Self::F32x4Floor, + O::F32x4Trunc => Self::F32x4Trunc, + O::F32x4Nearest => Self::F32x4Nearest, + O::F64x2Ceil => Self::F64x2Ceil, + O::F64x2Floor => Self::F64x2Floor, + O::F64x2Trunc => Self::F64x2Trunc, + O::F64x2Nearest => Self::F64x2Nearest, + O::F32x4Abs => Self::F32x4Abs, + O::F32x4Neg => Self::F32x4Neg, + O::F32x4Sqrt => Self::F32x4Sqrt, + O::F32x4Add => Self::F32x4Add, + O::F32x4Sub => Self::F32x4Sub, + O::F32x4Mul => Self::F32x4Mul, + O::F32x4Div => Self::F32x4Div, + O::F32x4Min => Self::F32x4Min, + O::F32x4Max => Self::F32x4Max, + O::F32x4PMin => Self::F32x4PMin, + O::F32x4PMax => Self::F32x4PMax, + O::F64x2Abs => Self::F64x2Abs, + O::F64x2Neg => Self::F64x2Neg, + O::F64x2Sqrt => Self::F64x2Sqrt, + O::F64x2Add => Self::F64x2Add, + O::F64x2Sub => Self::F64x2Sub, + O::F64x2Mul => Self::F64x2Mul, + O::F64x2Div => Self::F64x2Div, + O::F64x2Min => Self::F64x2Min, + O::F64x2Max => Self::F64x2Max, + O::F64x2PMin => Self::F64x2PMin, + O::F64x2PMax => Self::F64x2PMax, + O::I32x4TruncSatF32x4S => Self::I32x4TruncSatF32x4S, + O::I32x4TruncSatF32x4U => Self::I32x4TruncSatF32x4U, + O::F32x4ConvertI32x4S => Self::F32x4ConvertI32x4S, + O::F32x4ConvertI32x4U => Self::F32x4ConvertI32x4U, + O::I8x16Swizzle => Self::I8x16Swizzle, + O::I8x16Shuffle { .. } => Self::I8x16Shuffle, + O::V128Load8Splat { .. } => Self::V128Load8Splat, + O::V128Load16Splat { .. } => Self::V128Load16Splat, + O::V128Load32Splat { .. } => Self::V128Load32Splat, + O::V128Load32Zero { .. } => Self::V128Load32Zero, + O::V128Load64Splat { .. } => Self::V128Load64Splat, + O::V128Load64Zero { .. } => Self::V128Load64Zero, + O::I8x16NarrowI16x8S => Self::I8x16NarrowI16x8S, + O::I8x16NarrowI16x8U => Self::I8x16NarrowI16x8U, + O::I16x8NarrowI32x4S => Self::I16x8NarrowI32x4S, + O::I16x8NarrowI32x4U => Self::I16x8NarrowI32x4U, + O::I16x8ExtendLowI8x16S => Self::I16x8ExtendLowI8x16S, + O::I16x8ExtendHighI8x16S => Self::I16x8ExtendHighI8x16S, + O::I16x8ExtendLowI8x16U => Self::I16x8ExtendLowI8x16U, + O::I16x8ExtendHighI8x16U => Self::I16x8ExtendHighI8x16U, + O::I32x4ExtendLowI16x8S => Self::I32x4ExtendLowI16x8S, + O::I32x4ExtendHighI16x8S => Self::I32x4ExtendHighI16x8S, + O::I32x4ExtendLowI16x8U => Self::I32x4ExtendLowI16x8U, + O::I32x4ExtendHighI16x8U => Self::I32x4ExtendHighI16x8U, + O::I64x2ExtendLowI32x4S => Self::I64x2ExtendLowI32x4S, + O::I64x2ExtendHighI32x4S => Self::I64x2ExtendHighI32x4S, + O::I64x2ExtendLowI32x4U => Self::I64x2ExtendLowI32x4U, + O::I64x2ExtendHighI32x4U => Self::I64x2ExtendHighI32x4U, + O::I16x8ExtMulLowI8x16S => Self::I16x8ExtMulLowI8x16S, + O::I16x8ExtMulHighI8x16S => Self::I16x8ExtMulHighI8x16S, + O::I16x8ExtMulLowI8x16U => Self::I16x8ExtMulLowI8x16U, + O::I16x8ExtMulHighI8x16U => Self::I16x8ExtMulHighI8x16U, + O::I32x4ExtMulLowI16x8S => Self::I32x4ExtMulLowI16x8S, + O::I32x4ExtMulHighI16x8S => Self::I32x4ExtMulHighI16x8S, + O::I32x4ExtMulLowI16x8U => Self::I32x4ExtMulLowI16x8U, + O::I32x4ExtMulHighI16x8U => Self::I32x4ExtMulHighI16x8U, + O::I64x2ExtMulLowI32x4S => Self::I64x2ExtMulLowI32x4S, + O::I64x2ExtMulHighI32x4S => Self::I64x2ExtMulHighI32x4S, + O::I64x2ExtMulLowI32x4U => Self::I64x2ExtMulLowI32x4U, + O::I64x2ExtMulHighI32x4U => Self::I64x2ExtMulHighI32x4U, + O::V128Load8x8S { .. } => Self::V128Load8x8S, + O::V128Load8x8U { .. } => Self::V128Load8x8U, + O::V128Load16x4S { .. } => Self::V128Load16x4S, + O::V128Load16x4U { .. } => Self::V128Load16x4U, + O::V128Load32x2S { .. } => Self::V128Load32x2S, + O::V128Load32x2U { .. } => Self::V128Load32x2U, + O::V128Load8Lane { .. } => Self::V128Load8Lane, + O::V128Load16Lane { .. } => Self::V128Load16Lane, + O::V128Load32Lane { .. } => Self::V128Load32Lane, + O::V128Load64Lane { .. } => Self::V128Load64Lane, + O::V128Store8Lane { .. } => Self::V128Store8Lane, + O::V128Store16Lane { .. } => Self::V128Store16Lane, + O::V128Store32Lane { .. } => Self::V128Store32Lane, + O::V128Store64Lane { .. } => Self::V128Store64Lane, + O::I8x16AvgrU => Self::I8x16RoundingAverageU, + O::I16x8AvgrU => Self::I16x8RoundingAverageU, + O::I16x8Q15MulrSatS => Self::I16x8Q15MulrSatS, + O::F32x4DemoteF64x2Zero => Self::F32x4DemoteF64x2Zero, + O::F64x2PromoteLowF32x4 => Self::F64x2PromoteLowF32x4, + O::F64x2ConvertLowI32x4S => Self::F64x2ConvertLowI32x4S, + O::F64x2ConvertLowI32x4U => Self::F64x2ConvertLowI32x4U, + O::I32x4TruncSatF64x2SZero => Self::I32x4TruncSatF64x2SZero, + O::I32x4TruncSatF64x2UZero => Self::I32x4TruncSatF64x2UZero, + O::I8x16RelaxedSwizzle => Self::I8x16RelaxedSwizzle, + O::I32x4RelaxedTruncF32x4S => Self::I32x4RelaxedTruncSatF32x4S, + O::I32x4RelaxedTruncF32x4U => Self::I32x4RelaxedTruncSatF32x4U, + O::I32x4RelaxedTruncF64x2SZero => Self::I32x4RelaxedTruncSatF64x2SZero, + O::I32x4RelaxedTruncF64x2UZero => Self::I32x4RelaxedTruncSatF64x2UZero, + O::F32x4RelaxedMadd => Self::F32x4Fma, + O::I8x16RelaxedLaneselect => Self::I8x16LaneSelect, + O::I16x8RelaxedLaneselect => Self::I16x8LaneSelect, + O::I32x4RelaxedLaneselect => Self::I32x4LaneSelect, + O::I64x2RelaxedLaneselect => Self::I64x2LaneSelect, + O::F32x4RelaxedMin => Self::F32x4RelaxedMin, + O::F32x4RelaxedMax => Self::F32x4RelaxedMax, + O::F64x2RelaxedMin => Self::F64x2RelaxedMin, + O::F64x2RelaxedMax => Self::F64x2RelaxedMax, + O::I16x8RelaxedQ15mulrS => Self::I16x8RelaxedQ15mulrS, + _ => { + panic!("unimplemented operator {operator:?}"); + } + } + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/target_lexicon.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/target_lexicon.rs new file mode 100644 index 0000000..b24c3da --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/target_lexicon.rs @@ -0,0 +1,314 @@ +//! Unstable non-standard Wasmer-specific API that contains everything +//! to create a target with a triple and CPU features. +//! +//! This is useful for cross-compilation. +//! +//! # Example +//! +//! ```rust +//! # use wasmer_inline_c::assert_c; +//! # fn main() { +//! # (assert_c! { +//! # #include "tests/wasmer.h" +//! # +//! int main() { +//! // Declare the target triple. +//! wasmer_triple_t* triple; +//! +//! { +//! wasm_name_t triple_name; +//! wasm_name_new_from_string(&triple_name, "x86_64-apple-darwin"); +//! +//! triple = wasmer_triple_new(&triple_name); +//! +//! wasm_name_delete(&triple_name); +//! } +//! +//! assert(triple); +//! +//! // Declare the target CPU features. +//! wasmer_cpu_features_t* cpu_features = wasmer_cpu_features_new(); +//! +//! { +//! wasm_name_t cpu_feature_name; +//! wasm_name_new_from_string(&cpu_feature_name, "sse2"); +//! +//! wasmer_cpu_features_add(cpu_features, &cpu_feature_name); +//! +//! wasm_name_delete(&cpu_feature_name); +//! } +//! +//! assert(cpu_features); +//! +//! // Create the target! +//! wasmer_target_t* target = wasmer_target_new(triple, cpu_features); +//! assert(target); +//! +//! wasmer_target_delete(target); +//! +//! return 0; +//! } +//! # }) +//! # .success(); +//! # } +//! ``` + +use super::super::types::wasm_name_t; +use enumset::EnumSet; +use std::slice; +use std::str::{self, FromStr}; +use wasmer_api::{CpuFeature, Target, Triple}; + +/// Unstable non-standard Wasmer-specific API to represent a triple + +/// CPU features pair. +/// +/// # Example +/// +/// See the module's documentation. +#[derive(Debug)] +#[allow(non_camel_case_types)] +pub struct wasmer_target_t { + #[allow(unused)] + pub(crate) inner: Target, +} + +/// Creates a new [`wasmer_target_t`]. +/// +/// It takes ownership of `triple` and `cpu_features`. +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub extern "C" fn wasmer_target_new( + triple: Option>, + cpu_features: Option>, +) -> Option> { + let triple = triple?; + let cpu_features = cpu_features?; + + Some(Box::new(wasmer_target_t { + inner: Target::new(triple.inner.clone(), cpu_features.inner), + })) +} + +/// Delete a [`wasmer_target_t`]. +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub extern "C" fn wasmer_target_delete(_target: Option>) {} + +/// Unstable non-standard Wasmer-specific API to represent a target +/// “triple”. +/// +/// Historically such things had three fields, though they have added +/// additional fields over time. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// wasm_name_t triple_name; +/// wasm_name_new_from_string(&triple_name, "x86_64-apple-darwin"); +/// +/// wasmer_triple_t* triple = wasmer_triple_new(&triple_name); +/// assert(triple); +/// +/// wasmer_triple_delete(triple); +/// wasm_name_delete(&triple_name); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +/// +/// See also [`wasmer_triple_new_from_host`]. +#[allow(non_camel_case_types)] +pub struct wasmer_triple_t { + inner: Triple, +} + +/// Create a new [`wasmer_triple_t`] based on a triple string. +/// +/// # Example +/// +/// See [`wasmer_triple_t`] or [`wasmer_triple_new_from_host`]. +#[no_mangle] +pub unsafe extern "C" fn wasmer_triple_new( + triple: Option<&wasm_name_t>, +) -> Option> { + let triple = triple?; + let triple = c_try!(str::from_utf8(slice::from_raw_parts( + triple.data, + triple.size + ))); + + Some(Box::new(wasmer_triple_t { + inner: c_try!(Triple::from_str(triple)), + })) +} + +/// Create the [`wasmer_triple_t`] for the current host. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// wasmer_triple_t* triple = wasmer_triple_new_from_host(); +/// assert(triple); +/// +/// wasmer_triple_delete(triple); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +/// +/// See also [`wasmer_triple_new`]. +#[no_mangle] +pub extern "C" fn wasmer_triple_new_from_host() -> Box { + Box::new(wasmer_triple_t { + inner: Triple::host(), + }) +} + +/// Delete a [`wasmer_triple_t`]. +/// +/// # Example +/// +/// See [`wasmer_triple_t`]. +#[no_mangle] +pub extern "C" fn wasmer_triple_delete(_triple: Option>) {} + +/// Unstable non-standard Wasmer-specific API to represent a set of +/// CPU features. +/// +/// CPU features are identified by their stringified names. The +/// reference is the GCC options: +/// +/// * , +/// * , +/// * . +/// +/// At the time of writing this documentation (it might be outdated in +/// the future), the supported features are the following: +/// +/// * `sse2`, +/// * `sse3`, +/// * `ssse3`, +/// * `sse4.1`, +/// * `sse4.2`, +/// * `popcnt`, +/// * `avx`, +/// * `bmi`, +/// * `bmi2`, +/// * `avx2`, +/// * `avx512dq`, +/// * `avx512vl`, +/// * `lzcnt`. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create a new CPU feature set. +/// wasmer_cpu_features_t* cpu_features = wasmer_cpu_features_new(); +/// +/// // Create a new feature name, here `sse2`, and add it to the set. +/// { +/// wasm_name_t cpu_feature_name; +/// wasm_name_new_from_string(&cpu_feature_name, "sse2"); +/// +/// wasmer_cpu_features_add(cpu_features, &cpu_feature_name); +/// +/// wasm_name_delete(&cpu_feature_name); +/// } +/// +/// wasmer_cpu_features_delete(cpu_features); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[allow(non_camel_case_types)] +pub struct wasmer_cpu_features_t { + inner: EnumSet, +} + +/// Create a new [`wasmer_cpu_features_t`]. +/// +/// # Example +/// +/// See [`wasmer_cpu_features_t`]. +#[no_mangle] +pub extern "C" fn wasmer_cpu_features_new() -> Box { + Box::new(wasmer_cpu_features_t { + inner: CpuFeature::set(), + }) +} + +/// Delete a [`wasmer_cpu_features_t`]. +/// +/// # Example +/// +/// See [`wasmer_cpu_features_t`]. +#[no_mangle] +pub extern "C" fn wasmer_cpu_features_delete(_cpu_features: Option>) {} + +/// Add a new CPU feature into the set represented by +/// [`wasmer_cpu_features_t`]. +/// +/// # Example +/// +/// See [`wasmer_cpu_features_t`]. +#[no_mangle] +pub unsafe extern "C" fn wasmer_cpu_features_add( + cpu_features: Option<&mut wasmer_cpu_features_t>, + feature: Option<&wasm_name_t>, +) -> bool { + let cpu_features = match cpu_features { + Some(cpu_features) => cpu_features, + _ => return false, + }; + let feature = match feature { + Some(feature) => feature, + _ => return false, + }; + let feature = c_try!( + str::from_utf8(slice::from_raw_parts( + feature.data, + feature.size, + )); + otherwise false + ); + + cpu_features.inner.insert(c_try!( + CpuFeature::from_str(feature); + otherwise false + )); + + true +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/wasi.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/wasi.rs new file mode 100644 index 0000000..f37a5e5 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/unstable/wasi.rs @@ -0,0 +1,183 @@ +//! Unstable non-standard Wasmer-specific API that contains more WASI +//! API. + +use super::super::{ + externals::wasm_extern_t, module::wasm_module_t, types::wasm_name_t, wasi::wasi_env_t, +}; + +/// Unstable non-standard type wrapping `wasm_extern_t` with the +/// addition of two `wasm_name_t` respectively for the module name and +/// the name of the extern (very likely to be an import). This +/// non-standard type is used by the unstable non-standard +/// `wasi_get_unordered_imports` function. +/// +/// The `module`, `name` and `extern` fields are all owned by this type. +#[allow(non_camel_case_types)] +#[derive(Clone)] +pub struct wasmer_named_extern_t { + module: wasm_name_t, + name: wasm_name_t, + r#extern: Box, +} + +wasm_declare_boxed_vec!(named_extern, wasmer); + +/// So. Let's explain a dirty hack. `cbindgen` reads the code and +/// collects symbols. What symbols do we need? None of the one +/// declared in `wasm.h`, but for non-standard API, we need to collect +/// all of them. The problem is that `wasmer_named_extern_t` is the only +/// non-standard type where extra symbols are generated by a macro +/// (`wasm_declare_boxed_vec!`). If we want those macro-generated +/// symbols to be collected by `cbindgen`, we need to _expand_ the +/// crate (i.e. running something like `rustc -- -Zunstable-options +/// --pretty=expanded`). Expanding code is unstable and available only +/// on nightly compiler. We _don't want_ to use a nightly compiler +/// only for that. So how can we help `cbindgen` to _see_ those +/// symbols? +/// +/// First solution: We write the C code directly in a file, which is +/// then included in the generated header file with the `cbindgen` +/// API. Problem, it's super easy to get it outdated, and it makes the +/// build process more complex. +/// +/// Second solution: We write those symbols in a custom module, that +/// is just here for `cbindgen`, never used by our Rust code +/// (otherwise it's duplicated code), with no particular +/// implementation. +/// +/// And that's why we have the following `cbindgen_hack` +/// module. +/// +/// But this module must not be compiled by `rustc`. How to force +/// `rustc` to ignore a module? With conditional compilation. Because +/// `cbindgen` does not support conditional compilation, it will +/// always _ignore_ the `#[cfg]` attribute, and will always read the +/// content of the module. +/// +/// Sorry. +#[doc(hidden)] +#[cfg(__cbindgen_hack__ = "yes")] +mod __cbindgen_hack__ { + use super::*; + + #[repr(C)] + pub struct wasmer_named_extern_vec_t { + pub size: usize, + pub data: *mut *mut wasmer_named_extern_t, + } + + #[no_mangle] + pub unsafe extern "C" fn wasmer_named_extern_vec_new( + out: *mut wasmer_named_extern_vec_t, + length: usize, + init: *const *mut wasmer_named_extern_t, + ) { + unimplemented!() + } + + #[no_mangle] + pub unsafe extern "C" fn wasmer_named_extern_vec_new_uninitialized( + out: *mut wasmer_named_extern_vec_t, + length: usize, + ) { + unimplemented!() + } + + #[no_mangle] + pub unsafe extern "C" fn wasmer_named_extern_vec_copy( + out_ptr: &mut wasmer_named_extern_vec_t, + in_ptr: &wasmer_named_extern_vec_t, + ) { + unimplemented!() + } + + #[no_mangle] + pub unsafe extern "C" fn wasmer_named_extern_vec_delete( + ptr: Option<&mut wasmer_named_extern_vec_t>, + ) { + unimplemented!() + } + + #[no_mangle] + pub unsafe extern "C" fn wasmer_named_extern_vec_new_empty( + out: *mut wasmer_named_extern_vec_t, + ) { + unimplemented!() + } +} + +/// Non-standard function to get the module name of a +/// `wasmer_named_extern_t`. +/// +/// The returned value isn't owned by the caller. +#[no_mangle] +pub extern "C" fn wasmer_named_extern_module( + named_extern: Option<&wasmer_named_extern_t>, +) -> Option<&wasm_name_t> { + Some(&named_extern?.module) +} + +/// Non-standard function to get the name of a `wasmer_named_extern_t`. +/// +/// The returned value isn't owned by the caller. +#[no_mangle] +pub extern "C" fn wasmer_named_extern_name( + named_extern: Option<&wasmer_named_extern_t>, +) -> Option<&wasm_name_t> { + Some(&named_extern?.name) +} + +/// Non-standard function to get the wrapped extern of a +/// `wasmer_named_extern_t`. +/// +/// The returned value isn't owned by the caller. +#[no_mangle] +pub extern "C" fn wasmer_named_extern_unwrap( + named_extern: Option<&wasmer_named_extern_t>, +) -> Option<&wasm_extern_t> { + Some(named_extern?.r#extern.as_ref()) +} + +/// Non-standard function to get the imports needed for the WASI +/// implementation with no particular order. Each import has its +/// associated module name and name, so that it can be re-order later +/// based on the `wasm_module_t` requirements. +#[no_mangle] +pub unsafe extern "C" fn wasi_get_unordered_imports( + wasi_env: Option<&mut wasi_env_t>, + module: Option<&wasm_module_t>, + imports: &mut wasmer_named_extern_vec_t, +) -> bool { + wasi_get_unordered_imports_inner(wasi_env, module, imports).is_some() +} + +unsafe fn wasi_get_unordered_imports_inner( + wasi_env: Option<&mut wasi_env_t>, + module: Option<&wasm_module_t>, + imports: &mut wasmer_named_extern_vec_t, +) -> Option<()> { + let wasi_env = wasi_env?; + let store = &mut wasi_env.store; + let mut store_mut = store.store_mut(); + let module = module?; + + let import_object = c_try!(wasi_env.inner.import_object(&mut store_mut, &module.inner)); + + imports.set_buffer( + import_object + .into_iter() + .map(move |((module, name), extern_)| { + let module = module.into(); + let name = name.into(); + + Some(Box::new(wasmer_named_extern_t { + module, + name, + r#extern: Box::new(wasm_extern_t::new(store.clone(), extern_)), + })) + }) + .collect::>(), + ); + + Some(()) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/value.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/value.rs new file mode 100644 index 0000000..737975f --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/value.rs @@ -0,0 +1,239 @@ +use super::types::{wasm_ref_t, wasm_valkind_enum}; +use std::convert::{TryFrom, TryInto}; +use wasmer_api::Value; + +/// Represents the kind of values. The variants of this C enum is +/// defined in `wasm.h` to list the following: +/// +/// * `WASM_I32`, a 32-bit integer, +/// * `WASM_I64`, a 64-bit integer, +/// * `WASM_F32`, a 32-bit float, +/// * `WASM_F64`, a 64-bit float, +/// * `WASM_ANYREF`, a WebAssembly reference, +/// * `WASM_FUNCREF`, a WebAssembly reference. +#[allow(non_camel_case_types)] +pub type wasm_valkind_t = u8; + +/// A Rust union, compatible with C, that holds a value of kind +/// [`wasm_valkind_t`] (see [`wasm_val_t`] to get the complete +/// picture). Members of the union are: +/// +/// * `int32_t` if the value is a 32-bit integer, +/// * `int64_t` if the value is a 64-bit integer, +/// * `float32_t` if the value is a 32-bit float, +/// * `float64_t` if the value is a 64-bit float, +/// * `wref` (`wasm_ref_t`) if the value is a WebAssembly reference. +#[allow(non_camel_case_types)] +#[derive(Clone, Copy)] +pub union wasm_val_inner { + pub(crate) int32_t: i32, + pub(crate) int64_t: i64, + pub(crate) float32_t: f32, + pub(crate) float64_t: f64, + pub(crate) wref: *mut wasm_ref_t, +} + +/// A WebAssembly value composed of its type and its value. +/// +/// Note that `wasm.h` defines macros to create Wasm values more +/// easily: `WASM_I32_VAL`, `WASM_I64_VAL`, `WASM_F32_VAL`, +/// `WASM_F64_VAL`, and `WASM_REF_VAL`. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create a 32-bit integer Wasm value. +/// wasm_val_t value1 = { +/// .kind = WASM_I32, +/// .of = { .i32 = 7 }, +/// }; +/// +/// // Create the same value with the `wasm.h` macro. +/// wasm_val_t value2 = WASM_I32_VAL(7); +/// +/// assert(value2.kind == WASM_I32); +/// assert(value1.of.i32 == value2.of.i32); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[allow(non_camel_case_types)] +#[repr(C)] +pub struct wasm_val_t { + /// The kind of the value. + pub kind: wasm_valkind_t, + + /// The real value. + pub of: wasm_val_inner, +} + +impl std::fmt::Debug for wasm_val_t { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let mut ds = f.debug_struct("wasm_val_t"); + ds.field("kind", &self.kind); + + match self.kind.try_into() { + Ok(wasm_valkind_enum::WASM_I32) => { + ds.field("i32", &unsafe { self.of.int32_t }); + } + Ok(wasm_valkind_enum::WASM_I64) => { + ds.field("i64", &unsafe { self.of.int64_t }); + } + Ok(wasm_valkind_enum::WASM_F32) => { + ds.field("f32", &unsafe { self.of.float32_t }); + } + Ok(wasm_valkind_enum::WASM_F64) => { + ds.field("f64", &unsafe { self.of.float64_t }); + } + Ok(wasm_valkind_enum::WASM_ANYREF) => { + ds.field("anyref", &unsafe { self.of.wref }); + } + + Ok(wasm_valkind_enum::WASM_FUNCREF) => { + ds.field("funcref", &unsafe { self.of.wref }); + } + Err(_) => { + ds.field("value", &"Invalid value type"); + } + } + ds.finish() + } +} + +wasm_declare_vec!(val); + +impl Clone for wasm_val_t { + fn clone(&self) -> Self { + wasm_val_t { + kind: self.kind, + of: self.of, + } + } +} + +impl Default for wasm_val_t { + fn default() -> Self { + Self { + kind: wasm_valkind_enum::WASM_I64 as _, + of: wasm_val_inner { int64_t: 0 }, + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_val_copy( + // own + out: &mut wasm_val_t, + val: &wasm_val_t, +) { + out.kind = val.kind; + out.of = c_try!(val.kind.try_into().map(|kind| { + match kind { + wasm_valkind_enum::WASM_I32 => wasm_val_inner { + int32_t: val.of.int32_t, + }, + wasm_valkind_enum::WASM_I64 => wasm_val_inner { + int64_t: val.of.int64_t, + }, + wasm_valkind_enum::WASM_F32 => wasm_val_inner { + float32_t: val.of.float32_t, + }, + wasm_valkind_enum::WASM_F64 => wasm_val_inner { + float64_t: val.of.float64_t, + }, + wasm_valkind_enum::WASM_ANYREF => wasm_val_inner { wref: val.of.wref }, + wasm_valkind_enum::WASM_FUNCREF => wasm_val_inner { wref: val.of.wref }, + } + }); otherwise ()); +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_val_delete(val: Option>) { + if let Some(val) = val { + // TODO: figure out where wasm_val is allocated first... + let _ = val; + } +} + +impl TryFrom for wasm_valkind_enum { + type Error = &'static str; + + fn try_from(item: wasm_valkind_t) -> Result { + Ok(match item { + 0 => wasm_valkind_enum::WASM_I32, + 1 => wasm_valkind_enum::WASM_I64, + 2 => wasm_valkind_enum::WASM_F32, + 3 => wasm_valkind_enum::WASM_F64, + 128 => wasm_valkind_enum::WASM_ANYREF, + 129 => wasm_valkind_enum::WASM_FUNCREF, + _ => return Err("valkind value out of bounds"), + }) + } +} + +impl TryFrom for Value { + type Error = &'static str; + + fn try_from(item: wasm_val_t) -> Result { + (&item).try_into() + } +} + +impl TryFrom<&wasm_val_t> for Value { + type Error = &'static str; + + fn try_from(item: &wasm_val_t) -> Result { + Ok(match item.kind.try_into()? { + wasm_valkind_enum::WASM_I32 => Value::I32(unsafe { item.of.int32_t }), + wasm_valkind_enum::WASM_I64 => Value::I64(unsafe { item.of.int64_t }), + wasm_valkind_enum::WASM_F32 => Value::F32(unsafe { item.of.float32_t }), + wasm_valkind_enum::WASM_F64 => Value::F64(unsafe { item.of.float64_t }), + wasm_valkind_enum::WASM_ANYREF => return Err("ANYREF not supported at this time"), + wasm_valkind_enum::WASM_FUNCREF => return Err("FUNCREF not supported at this time"), + }) + } +} + +impl TryFrom for wasm_val_t { + type Error = &'static str; + + fn try_from(item: Value) -> Result { + wasm_val_t::try_from(&item) + } +} + +impl TryFrom<&Value> for wasm_val_t { + type Error = &'static str; + + fn try_from(item: &Value) -> Result { + Ok(match *item { + Value::I32(v) => wasm_val_t { + of: wasm_val_inner { int32_t: v }, + kind: wasm_valkind_enum::WASM_I32 as _, + }, + Value::I64(v) => wasm_val_t { + of: wasm_val_inner { int64_t: v }, + kind: wasm_valkind_enum::WASM_I64 as _, + }, + Value::F32(v) => wasm_val_t { + of: wasm_val_inner { float32_t: v }, + kind: wasm_valkind_enum::WASM_F32 as _, + }, + Value::F64(v) => wasm_val_t { + of: wasm_val_inner { float64_t: v }, + kind: wasm_valkind_enum::WASM_F64 as _, + }, + Value::V128(_) => return Err("128bit SIMD types not yet supported in Wasm C API"), + _ => todo!("Handle these values in TryFrom for wasm_val_t"), + }) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/version.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/version.rs new file mode 100644 index 0000000..337ad73 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/version.rs @@ -0,0 +1,124 @@ +use lazy_static::lazy_static; +use std::os::raw::c_char; + +const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0"); +const VERSION_PRE: &str = concat!(env!("CARGO_PKG_VERSION_PRE"), "\0"); + +lazy_static! { + static ref VERSION_MAJOR: u8 = env!("CARGO_PKG_VERSION_MAJOR") + .parse() + .expect("Failed to parse value for `VERSION_MAJOR` from `CARGO_PKG_VERSION_MAJOR`"); + static ref VERSION_MINOR: u8 = env!("CARGO_PKG_VERSION_MINOR") + .parse() + .expect("Failed to parse value for `VERSION_MINOR` from `CARGO_PKG_VERSION_MINOR`"); + static ref VERSION_PATCH: u8 = env!("CARGO_PKG_VERSION_PATCH") + .parse() + .expect("Failed to parse value for `VERSION_PATCH` from `CARGO_PKG_VERSION_PATCH`"); +} + +/// Get the version of the Wasmer C API. +/// +/// The `.h` files already define variables like `WASMER_VERSION*`, +/// but if this file is unreachable, one can use this function to +/// retrieve the full semver version of the Wasmer C API. +/// +/// The returned string is statically allocated. It must _not_ be +/// freed! +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub unsafe extern "C" fn wasmer_version() -> *const c_char { + VERSION.as_ptr() as *const _ +} + +/// Get the major version of the Wasmer C API. +/// +/// See [`wasmer_version`] to learn more. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Get and print the version components. +/// uint8_t version_major = wasmer_version_major(); +/// uint8_t version_minor = wasmer_version_minor(); +/// uint8_t version_patch = wasmer_version_patch(); +/// +/// printf("%d.%d.%d", version_major, version_minor, version_patch); +/// +/// return 0; +/// } +/// # }) +/// # .success() +/// # .stdout( +/// # format!( +/// # "{}.{}.{}", +/// # env!("CARGO_PKG_VERSION_MAJOR"), +/// # env!("CARGO_PKG_VERSION_MINOR"), +/// # env!("CARGO_PKG_VERSION_PATCH") +/// # ) +/// # ); +/// # } +/// ``` +#[no_mangle] +pub unsafe extern "C" fn wasmer_version_major() -> u8 { + *VERSION_MAJOR +} + +/// Get the minor version of the Wasmer C API. +/// +/// See [`wasmer_version_major`] to learn more and get an example. +#[no_mangle] +pub unsafe extern "C" fn wasmer_version_minor() -> u8 { + *VERSION_MINOR +} + +/// Get the patch version of the Wasmer C API. +/// +/// See [`wasmer_version_major`] to learn more and get an example. +#[no_mangle] +pub unsafe extern "C" fn wasmer_version_patch() -> u8 { + *VERSION_PATCH +} + +/// Get the minor version of the Wasmer C API. +/// +/// See [`wasmer_version_major`] to learn more. +/// +/// The returned string is statically allocated. It must _not_ be +/// freed! +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Get and print the pre version. +/// const char* version_pre = wasmer_version_pre(); +/// printf("%s", version_pre); +/// +/// // No need to free the string. It's statically allocated on +/// // the Rust side. +/// +/// return 0; +/// } +/// # }) +/// # .success() +/// # .stdout(env!("CARGO_PKG_VERSION_PRE")); +/// # } +/// ``` +#[no_mangle] +pub unsafe extern "C" fn wasmer_version_pre() -> *const c_char { + VERSION_PRE.as_ptr() as *const _ +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/wasi/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/wasi/mod.rs new file mode 100644 index 0000000..f8e52e9 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/wasi/mod.rs @@ -0,0 +1,715 @@ +//! Unofficial API for WASI integrating with the standard Wasm C API. +//! +//! This API will be superseded by a standard WASI API when/if such a standard is created. + +pub use super::unstable::wasi::wasi_get_unordered_imports; +use super::{ + externals::{wasm_extern_t, wasm_extern_vec_t, wasm_func_t, wasm_memory_t}, + instance::wasm_instance_t, + module::wasm_module_t, + store::{wasm_store_t, StoreRef}, + types::wasm_byte_vec_t, +}; +use crate::error::update_last_error; +use std::convert::TryFrom; +use std::ffi::CStr; +use std::os::raw::c_char; +use std::slice; +use std::sync::Arc; +#[cfg(feature = "webc_runner")] +use wasmer_api::{AsStoreMut, Imports, Module}; +use wasmer_wasix::{ + default_fs_backing, get_wasi_version, + runtime::task_manager::{tokio::TokioTaskManager, InlineWaker}, + virtual_fs::AsyncReadExt, + virtual_fs::VirtualFile, + Pipe, PluggableRuntime, WasiEnv, WasiEnvBuilder, WasiFunctionEnv, WasiVersion, +}; + +#[derive(Debug)] +#[allow(non_camel_case_types)] +pub struct wasi_config_t { + inherit_stdout: bool, + inherit_stderr: bool, + inherit_stdin: bool, + builder: WasiEnvBuilder, + runtime: Option, +} + +#[no_mangle] +pub unsafe extern "C" fn wasi_config_new( + program_name: *const c_char, +) -> Option> { + debug_assert!(!program_name.is_null()); + + let name_c_str = CStr::from_ptr(program_name); + let prog_name = c_try!(name_c_str.to_str()); + + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(); + let _guard = runtime.enter(); + + Some(Box::new(wasi_config_t { + inherit_stdout: true, + inherit_stderr: true, + inherit_stdin: true, + builder: WasiEnv::builder(prog_name).fs(default_fs_backing()), + runtime: Some(runtime), + })) +} + +#[no_mangle] +pub unsafe extern "C" fn wasi_config_env( + config: &mut wasi_config_t, + key: *const c_char, + value: *const c_char, +) { + debug_assert!(!key.is_null()); + debug_assert!(!value.is_null()); + + let key_cstr = CStr::from_ptr(key); + let key_bytes = key_cstr.to_bytes(); + let value_cstr = CStr::from_ptr(value); + let value_bytes = value_cstr.to_bytes(); + + config.builder.add_env(key_bytes, value_bytes); +} + +#[no_mangle] +pub unsafe extern "C" fn wasi_config_arg(config: &mut wasi_config_t, arg: *const c_char) { + debug_assert!(!arg.is_null()); + + let arg_cstr = CStr::from_ptr(arg); + let arg_bytes = arg_cstr.to_bytes(); + + config.builder.add_arg(arg_bytes); +} + +#[no_mangle] +pub unsafe extern "C" fn wasi_config_preopen_dir( + config: &mut wasi_config_t, + dir: *const c_char, +) -> bool { + let dir_cstr = CStr::from_ptr(dir); + let dir_bytes = dir_cstr.to_bytes(); + let dir_str = match std::str::from_utf8(dir_bytes) { + Ok(dir_str) => dir_str, + Err(e) => { + update_last_error(e); + return false; + } + }; + + if let Err(e) = config.builder.add_preopen_dir(dir_str) { + update_last_error(e); + return false; + } + + true +} + +#[no_mangle] +pub unsafe extern "C" fn wasi_config_mapdir( + config: &mut wasi_config_t, + alias: *const c_char, + dir: *const c_char, +) -> bool { + let alias_cstr = CStr::from_ptr(alias); + let alias_bytes = alias_cstr.to_bytes(); + let alias_str = match std::str::from_utf8(alias_bytes) { + Ok(alias_str) => alias_str, + Err(e) => { + update_last_error(e); + return false; + } + }; + + let dir_cstr = CStr::from_ptr(dir); + let dir_bytes = dir_cstr.to_bytes(); + let dir_str = match std::str::from_utf8(dir_bytes) { + Ok(dir_str) => dir_str, + Err(e) => { + update_last_error(e); + return false; + } + }; + + if let Err(e) = config.builder.add_map_dir(alias_str, dir_str) { + update_last_error(e); + return false; + } + + true +} + +#[no_mangle] +pub extern "C" fn wasi_config_capture_stdout(config: &mut wasi_config_t) { + config.inherit_stdout = false; +} + +#[no_mangle] +pub extern "C" fn wasi_config_inherit_stdout(config: &mut wasi_config_t) { + config.inherit_stdout = true; +} + +#[no_mangle] +pub extern "C" fn wasi_config_capture_stderr(config: &mut wasi_config_t) { + config.inherit_stderr = false; +} + +#[no_mangle] +pub extern "C" fn wasi_config_inherit_stderr(config: &mut wasi_config_t) { + config.inherit_stderr = true; +} + +//#[no_mangle] +//pub extern "C" fn wasi_config_capture_stdin(config: &mut wasi_config_t) { +// config.inherit_stdin = false; +//} + +#[no_mangle] +pub extern "C" fn wasi_config_inherit_stdin(config: &mut wasi_config_t) { + config.inherit_stdin = true; +} + +#[repr(C)] +pub struct wasi_filesystem_t { + ptr: *const c_char, + size: usize, +} + +#[no_mangle] +pub unsafe extern "C" fn wasi_filesystem_init_static_memory( + volume_bytes: Option<&wasm_byte_vec_t>, +) -> Option> { + let volume_bytes = volume_bytes.as_ref()?; + Some(Box::new(wasi_filesystem_t { + ptr: { + let ptr = (volume_bytes.data.as_ref()?) as *const _ as *const c_char; + if ptr.is_null() { + return None; + } + ptr + }, + size: volume_bytes.size, + })) +} + +#[no_mangle] +pub unsafe extern "C" fn wasi_filesystem_delete(ptr: *mut wasi_filesystem_t) { + let _ = Box::from_raw(ptr); +} + +/// Initializes the `imports` with an import object that links to +/// the custom file system +#[cfg(feature = "webc_runner")] +#[no_mangle] +pub unsafe extern "C" fn wasi_env_with_filesystem( + config: Box, + store: Option<&mut wasm_store_t>, + module: Option<&wasm_module_t>, + fs: Option<&wasi_filesystem_t>, + imports: Option<&mut wasm_extern_vec_t>, + package: *const c_char, +) -> Option> { + wasi_env_with_filesystem_inner(config, store, module, fs, imports, package) +} + +#[cfg(feature = "webc_runner")] +unsafe fn wasi_env_with_filesystem_inner( + config: Box, + store: Option<&mut wasm_store_t>, + module: Option<&wasm_module_t>, + fs: Option<&wasi_filesystem_t>, + imports: Option<&mut wasm_extern_vec_t>, + package: *const c_char, +) -> Option> { + let store = &mut store?.inner; + let fs = fs.as_ref()?; + let package_str = CStr::from_ptr(package); + let package = package_str.to_str().unwrap_or(""); + let module = &module.as_ref()?.inner; + let imports = imports?; + + let (wasi_env, import_object) = prepare_webc_env( + config, + &mut store.store_mut(), + module, + &*(fs.ptr as *const u8), // cast wasi_filesystem_t.ptr as &'static [u8] + fs.size, + package, + )?; + + imports_set_buffer(store, module, import_object, imports)?; + + Some(Box::new(wasi_env_t { + inner: wasi_env, + store: store.clone(), + })) +} + +#[cfg(feature = "webc_runner")] +fn prepare_webc_env( + mut config: Box, + store: &mut impl AsStoreMut, + module: &Module, + bytes: &'static u8, + len: usize, + package_name: &str, +) -> Option<(WasiFunctionEnv, Imports)> { + use virtual_fs::static_fs::StaticFileSystem; + use webc::v1::{FsEntryType, WebC}; + + let store_mut = store.as_store_mut(); + let runtime = config.runtime.take(); + + let runtime = runtime.unwrap_or_else(|| { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + }); + + let handle = runtime.handle().clone(); + let _guard = handle.enter(); + let mut rt = PluggableRuntime::new(Arc::new(TokioTaskManager::new(runtime))); + rt.set_engine(Some(store_mut.engine().clone())); + + let slice = unsafe { std::slice::from_raw_parts(bytes, len) }; + let volumes = WebC::parse_volumes_from_fileblock(slice).ok()?; + let top_level_dirs = volumes + .into_iter() + .flat_map(|(_, volume)| { + volume + .header + .top_level + .iter() + .cloned() + .filter(|e| e.fs_type == FsEntryType::Dir) + .map(|e| e.text.to_string()) + .collect::>() + .into_iter() + }) + .collect::>(); + + let filesystem = Box::new(StaticFileSystem::init(slice, package_name)?); + let mut builder = config.builder.runtime(Arc::new(rt)); + + if !config.inherit_stdout { + builder.set_stdout(Box::new(Pipe::channel().0)); + } + + if !config.inherit_stderr { + builder.set_stderr(Box::new(Pipe::channel().0)); + } + + builder.set_fs(filesystem); + + for f_name in top_level_dirs.iter() { + builder + .add_preopen_build(|p| p.directory(f_name).read(true).write(true).create(true)) + .ok()?; + } + let env = builder.finalize(store).ok()?; + + let import_object = env.import_object(store, module).ok()?; + Some((env, import_object)) +} + +#[allow(non_camel_case_types)] +pub struct wasi_env_t { + /// cbindgen:ignore + pub(super) inner: WasiFunctionEnv, + pub(super) store: StoreRef, +} + +/// Create a new WASI environment. +/// +/// It take ownership over the `wasi_config_t`. +#[no_mangle] +pub unsafe extern "C" fn wasi_env_new( + store: Option<&mut wasm_store_t>, + mut config: Box, +) -> Option> { + let store = &mut store?.inner; + let mut store_mut = store.store_mut(); + + let runtime = config.runtime.take(); + + let runtime = runtime.unwrap_or_else(|| { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + }); + + let handle = runtime.handle().clone(); + let _guard = handle.enter(); + let mut rt = PluggableRuntime::new(Arc::new(TokioTaskManager::new(runtime))); + rt.set_engine(Some(store_mut.engine().clone())); + + if !config.inherit_stdout { + config.builder.set_stdout(Box::new(Pipe::channel().0)); + } + + if !config.inherit_stderr { + config.builder.set_stderr(Box::new(Pipe::channel().0)); + } + + // TODO: impl capturer for stdin + + let env = c_try!(config + .builder + .runtime(Arc::new(rt)) + .finalize(&mut store_mut)); + + Some(Box::new(wasi_env_t { + inner: env, + store: store.clone(), + })) +} + +/// Delete a [`wasi_env_t`]. +#[no_mangle] +pub extern "C" fn wasi_env_delete(state: Option>) { + if let Some(mut env) = state { + env.inner + .on_exit(unsafe { &mut env.store.store_mut() }, None); + } +} + +/// Set the memory on a [`wasi_env_t`]. +// NOTE: Only here to not break the C API. +// This was previosly supported, but is no longer possible due to WASIX changes. +// Customizing memories should be done through the builder or the runtime. +#[no_mangle] +#[deprecated(since = "4.0.0")] +pub unsafe extern "C" fn wasi_env_set_memory(_env: &mut wasi_env_t, _memory: &wasm_memory_t) { + panic!("wasmer_env_set_memory() is not supported"); +} + +#[no_mangle] +pub unsafe extern "C" fn wasi_env_read_stdout( + env: &mut wasi_env_t, + buffer: *mut c_char, + buffer_len: usize, +) -> isize { + let inner_buffer = slice::from_raw_parts_mut(buffer as *mut _, buffer_len); + let store = env.store.store(); + + let stdout = { + let data = env.inner.data(&store); + data.stdout() + }; + + if let Ok(mut stdout) = stdout { + if let Some(stdout) = stdout.as_mut() { + read_inner(stdout, inner_buffer) + } else { + update_last_error("could not find a file handle for `stdout`"); + -1 + } + } else { + update_last_error("could not find a file handle for `stdout`"); + -1 + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasi_env_read_stderr( + env: &mut wasi_env_t, + buffer: *mut c_char, + buffer_len: usize, +) -> isize { + let inner_buffer = slice::from_raw_parts_mut(buffer as *mut _, buffer_len); + let store = env.store.store(); + let stderr = { + let data = env.inner.data(&store); + data.stderr() + }; + if let Ok(mut stderr) = stderr { + if let Some(stderr) = stderr.as_mut() { + read_inner(stderr, inner_buffer) + } else { + update_last_error("could not find a file handle for `stderr`"); + -1 + } + } else { + update_last_error("could not find a file handle for `stderr`"); + -1 + } +} + +fn read_inner( + wasi_file: &mut Box, + inner_buffer: &mut [u8], +) -> isize { + InlineWaker::block_on(async { + match wasi_file.read(inner_buffer).await { + Ok(a) => a as isize, + Err(err) => { + update_last_error(format!("failed to read wasi_file: {}", err)); + -1 + } + } + }) +} + +/// The version of WASI. This is determined by the imports namespace +/// string. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(C)] +#[allow(non_camel_case_types)] +pub enum wasi_version_t { + /// An invalid version. + INVALID_VERSION = -1, + + /// Latest version. + /// + /// It's a “floating” version, i.e. it's an alias to the latest + /// version (for the moment, `Snapshot1`). Using this version is a + /// way to ensure that modules will run only if they come with the + /// latest WASI version (in case of security issues for instance), + /// by just updating the runtime. + /// + /// Note that this version is never returned by an API. It is + /// provided only by the user. + LATEST = 0, + + /// `wasi_unstable`. + SNAPSHOT0 = 1, + + /// `wasi_snapshot_preview1`. + SNAPSHOT1 = 2, + + /// `wasix_32v1`. + WASIX32V1 = 3, + + /// `wasix_64v1`. + WASIX64V1 = 4, +} + +impl From for wasi_version_t { + fn from(other: WasiVersion) -> Self { + match other { + WasiVersion::Snapshot0 => wasi_version_t::SNAPSHOT0, + WasiVersion::Snapshot1 => wasi_version_t::SNAPSHOT1, + WasiVersion::Wasix32v1 => wasi_version_t::WASIX32V1, + WasiVersion::Wasix64v1 => wasi_version_t::WASIX64V1, + WasiVersion::Latest => wasi_version_t::LATEST, + } + } +} + +impl TryFrom for WasiVersion { + type Error = &'static str; + + fn try_from(other: wasi_version_t) -> Result { + Ok(match other { + wasi_version_t::INVALID_VERSION => return Err("Invalid WASI version cannot be used"), + wasi_version_t::SNAPSHOT0 => WasiVersion::Snapshot0, + wasi_version_t::SNAPSHOT1 => WasiVersion::Snapshot1, + wasi_version_t::WASIX32V1 => WasiVersion::Wasix32v1, + wasi_version_t::WASIX64V1 => WasiVersion::Wasix64v1, + wasi_version_t::LATEST => WasiVersion::Latest, + }) + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasi_get_wasi_version(module: &wasm_module_t) -> wasi_version_t { + get_wasi_version(&module.inner, false) + .map(Into::into) + .unwrap_or(wasi_version_t::INVALID_VERSION) +} + +/// Non-standard function to get the imports needed for the WASI +/// implementation ordered as expected by the `wasm_module_t`. +#[no_mangle] +pub unsafe extern "C" fn wasi_get_imports( + _store: Option<&wasm_store_t>, + wasi_env: Option<&mut wasi_env_t>, + module: Option<&wasm_module_t>, + imports: &mut wasm_extern_vec_t, +) -> bool { + wasi_get_imports_inner(wasi_env, module, imports).is_some() +} + +unsafe fn wasi_get_imports_inner( + wasi_env: Option<&mut wasi_env_t>, + module: Option<&wasm_module_t>, + imports: &mut wasm_extern_vec_t, +) -> Option<()> { + let wasi_env = wasi_env?; + let store = &mut wasi_env.store; + let mut store_mut = store.store_mut(); + let module = module?; + + let import_object = c_try!(wasi_env.inner.import_object(&mut store_mut, &module.inner)); + + imports_set_buffer(store, &module.inner, import_object, imports)?; + + Some(()) +} + +pub(crate) fn imports_set_buffer( + store: &StoreRef, + module: &wasmer_api::Module, + import_object: wasmer_api::Imports, + imports: &mut wasm_extern_vec_t, +) -> Option<()> { + imports.set_buffer(c_try!(module + .imports() + .map(|import_type| { + let ext = import_object + .get_export(import_type.module(), import_type.name()) + .ok_or_else(|| { + format!( + "Failed to resolve import \"{}\" \"{}\"", + import_type.module(), + import_type.name() + ) + })?; + + Ok(Some(Box::new(wasm_extern_t::new(store.clone(), ext)))) + }) + .collect::, String>>())); + + Some(()) +} + +#[no_mangle] +pub unsafe extern "C" fn wasi_env_initialize_instance( + wasi_env: &mut wasi_env_t, + store: &mut wasm_store_t, + instance: &mut wasm_instance_t, +) -> bool { + wasi_env + .inner + .initialize(&mut store.inner.store_mut(), instance.inner.clone()) + .unwrap(); + true +} + +#[no_mangle] +pub unsafe extern "C" fn wasi_get_start_function( + instance: &mut wasm_instance_t, +) -> Option> { + let start = c_try!(instance.inner.exports.get_function("_start")); + + Some(Box::new(wasm_func_t { + extern_: wasm_extern_t::new(instance.store.clone(), start.clone().into()), + })) +} + +#[cfg(test)] +mod tests { + #[cfg(not(target_os = "windows"))] + use inline_c::assert_c; + #[cfg(target_os = "windows")] + use wasmer_inline_c::assert_c; + + #[cfg_attr(coverage, ignore)] + #[test] + fn test_wasi_get_wasi_version_snapshot0() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + wasmer_funcenv_t* env = wasmer_funcenv_new(store, 0); + + wasm_byte_vec_t wat; + wasmer_byte_vec_new_from_string(&wat, "(module (import \"wasi_unstable\" \"args_get\" (func (param i32 i32) (result i32))))"); + wasm_byte_vec_t wasm; + wat2wasm(&wat, &wasm); + + wasm_module_t* module = wasm_module_new(store, &wasm); + assert(module); + + assert(wasi_get_wasi_version(module) == SNAPSHOT0); + + wasm_module_delete(module); + wasm_byte_vec_delete(&wasm); + wasm_byte_vec_delete(&wat); + wasmer_funcenv_delete(env); + wasm_store_delete(store); + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } + + #[cfg_attr(coverage, ignore)] + #[test] + fn test_wasi_get_wasi_version_snapshot1() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + wasmer_funcenv_t* env = wasmer_funcenv_new(store, 0); + + wasm_byte_vec_t wat; + wasmer_byte_vec_new_from_string(&wat, "(module (import \"wasi_snapshot_preview1\" \"args_get\" (func (param i32 i32) (result i32))))"); + wasm_byte_vec_t wasm; + wat2wasm(&wat, &wasm); + + wasm_module_t* module = wasm_module_new(store, &wasm); + assert(module); + + assert(wasi_get_wasi_version(module) == SNAPSHOT1); + + wasm_module_delete(module); + wasm_byte_vec_delete(&wasm); + wasm_byte_vec_delete(&wat); + wasmer_funcenv_delete(env); + wasm_store_delete(store); + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } + + #[cfg_attr(coverage, ignore)] + #[test] + fn test_wasi_get_wasi_version_invalid() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + wasmer_funcenv_t* env = wasmer_funcenv_new(store, 0); + + wasm_byte_vec_t wat; + wasmer_byte_vec_new_from_string(&wat, "(module (import \"wasi_snpsht_prvw1\" \"args_get\" (func (param i32 i32) (result i32))))"); + wasm_byte_vec_t wasm; + wat2wasm(&wat, &wasm); + + wasm_module_t* module = wasm_module_new(store, &wasm); + assert(module); + + assert(wasi_get_wasi_version(module) == INVALID_VERSION); + + wasm_module_delete(module); + wasm_byte_vec_delete(&wasm); + wasm_byte_vec_delete(&wat); + wasmer_funcenv_delete(env); + wasm_store_delete(store); + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/wat.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/wat.rs new file mode 100644 index 0000000..4d0c392 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/src/wasm_c_api/wat.rs @@ -0,0 +1,88 @@ +use super::types::wasm_byte_vec_t; + +/// Parses in-memory bytes as either the WAT format, or a binary Wasm +/// module. This is wasmer-specific. +/// +/// In case of failure, `wat2wasm` sets the `out->data = NULL` and `out->size = 0`. +/// +/// # Example +/// +/// See the module's documentation. +/// +/// # Safety +/// This function is unsafe in order to be callable from C. +#[cfg(feature = "wat")] +#[no_mangle] +pub unsafe extern "C" fn wat2wasm(wat: &wasm_byte_vec_t, out: &mut wasm_byte_vec_t) { + match wasmer_api::wat2wasm(wat.as_slice()) { + Ok(val) => out.set_buffer(val.into_owned()), + Err(err) => { + crate::error::update_last_error(err); + out.data = std::ptr::null_mut(); + out.size = 0; + } + }; +} + +#[cfg(test)] +mod tests { + #[cfg(not(target_os = "windows"))] + use inline_c::assert_c; + #[cfg(target_os = "windows")] + use wasmer_inline_c::assert_c; + + #[test] + fn test_wat2wasm() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_byte_vec_t wat; + wasmer_byte_vec_new_from_string(&wat, "(module)"); + wasm_byte_vec_t wasm; + wat2wasm(&wat, &wasm); + + assert(wasm.data); + assert(wasm.size == 8); + assert( + wasm.data[0] == 0 && + wasm.data[1] == 'a' && + wasm.data[2] == 's' && + wasm.data[3] == 'm' && + wasm.data[4] == 1 && + wasm.data[5] == 0 && + wasm.data[6] == 0 && + wasm.data[7] == 0 + ); + + wasm_byte_vec_delete(&wasm); + wasm_byte_vec_delete(&wat); + + return 0; + } + }) + .success(); + } + + #[test] + fn test_wat2wasm_failed() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_byte_vec_t wat; + wasmer_byte_vec_new_from_string(&wat, "(module"); + wasm_byte_vec_t wasm; + wat2wasm(&wat, &wasm); + + assert(!wasm.data); + assert(wasmer_last_error_length() > 0); + + wasm_byte_vec_delete(&wat); + + return 0; + } + }) + .success(); + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/.gitignore b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/.gitignore new file mode 100644 index 0000000..58fbd3c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/.gitignore @@ -0,0 +1,11 @@ +# ignore wasm-c-api binaries +wasm-c-api-* +test-* +wasm-c-api/example/* + +# Unignore files ending with `.c` (i.e. `wasm-c-api-wasi.c`) +!*.c +!wasm-c-api/example/*.c +!wasm-c-api/example/*.cc +!wasm-c-api/example/*.wasm +!wasm-c-api/example/*.wat diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/Makefile b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/Makefile new file mode 100644 index 0000000..596994e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/Makefile @@ -0,0 +1,32 @@ +WASMER_DIR:=$(realpath $(WASMER_DIR)) + +$(info Using provided WASMER_DIR=$(WASMER_DIR)) + +ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) +ROOT_DIR_PARENT:=$(shell dirname $(ROOT_DIR)) + +ifeq (,$(wildcard $(WASMER_DIR)/bin/wasmer)) + CFLAGS = -g -I $(ROOT_DIR) -I $(WASMER_DIR)/include + LDFLAGS = -Wl,-rpath,$(WASMER_DIR)/lib + LDLIBS = -L $(WASMER_DIR)/lib -lwasmer +else + CFLAGS = -g -I $(ROOT_DIR)/wasm-c-api/include/ -I $(shell $(WASMER_DIR)/bin/wasmer config --includedir) + LDFLAGS = -Wl,-rpath,$(shell $(WASMER_DIR)/bin/wasmer config --libdir) + LDLIBS = $(shell $(WASMER_DIR)/bin/wasmer config --libs) +endif + + +$(info * ROOT_DIR: $(ROOT_DIR)) +$(info * WASMER_DIR: $(WASMER_DIR)) +$(info * "") +$(info * CFLAGS: $(CFLAGS)) +$(info * LDFLAGS: $(LDFLAGS)) +$(info * LDLIBS: $(LDLIBS)) + +test: + cargo test --manifest-path="./wasmer-c-api-test-runner/Cargo.toml" -- --nocapture 2>&1 + +.SILENT: clean +.PHONY: clean +clean: + $(foreach file,$(ALL),rm -f $(file).o $(file)) \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/.gitignore b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/.gitignore new file mode 100644 index 0000000..ad0095a --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/.gitignore @@ -0,0 +1,2 @@ +out +v8 diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/Dockerfile b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/Dockerfile new file mode 100644 index 0000000..d9f8de7 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/Dockerfile @@ -0,0 +1,18 @@ +FROM ubuntu:bionic +RUN apt-get update && apt-get install -y \ + apt-utils \ + clang \ + cmake \ + curl \ + git \ + libc++-dev \ + libc++abi-dev \ + libglib2.0-dev \ + libgmp-dev \ + ninja-build \ + python +ADD . /code/wasm-c-api +WORKDIR /code/wasm-c-api +RUN make v8-checkout +RUN make -j v8 +RUN make diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/LICENSE b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/LICENSE new file mode 100644 index 0000000..8f71f43 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/README.md b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/README.md new file mode 100644 index 0000000..1dbcbda --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/README.md @@ -0,0 +1,112 @@ +# WebAssembly C and C++ API + +Work in progress! No docs yet. + + +### Design Goals + +* Provide a "black box" API for embedding a Wasm engine in other C/C++ applications. + + * Be completely agnostic to VM specifics. + + * Non-goal: "white box" interoperability with embedder (such as combined GC instead of mere finalisation) -- *much* more difficult to achieve. + +* Allow creation of bindings for other languages through typical C foreign function interfaces. + + * Support a plain C API. + + * Stick to mostly manual memory management of interface objects. + +* Avoid language features that raise barrier to use. + + * E.g., no exceptions or post-C++11 features in C++ API. + + * E.g., no passing of structs by-value or post-C99 features in C API. + +* Achieve link-time compatibility between different implementations. + + * All implementation-dependent API classes are abstract and can be instantiated through factory methods only. + + +### Interfaces + +* C++ API: + + * See `include/wasm.hh` for interface. + + * See `example/*.cc` for example usages. + +* C API: + + * See `include/wasm.h` for interface. + + * See `example/*.c` for example usages. + +Some random explanations: + +* The VM must be initialised by creating an instance of an *engine* (`wasm::Engine`/`wasm_engine_t`) and is shut down by deleting it. Such an instance may only be created once per process. + +* All runtime objects are tied to a specific *store* (`wasm::Store`/`wasm_store_t`). Multiple stores can be created, but their objects cannot interact. Every store and its objects must only be accessed in a single thread. + +* To exchange module objects between threads, create a *shared* module (`wasm::Shared`/`wasm_shared_module_t`). Other objects cannot be shared in current Wasm. + +* *Vector* structures (`wasm::vec`/`wasm_x_vec_t`) are lightweight abstractions of a pair of a plain array and its length. The C++ API does not use `std::vector` because that does not support adopting pre-existing arrays. + +* *References* point to runtime objects, but may involve internal indirections, which may or may not be cached. Thus, pointer equality on `Ref*` or subclasses cannot be used to compare identity of the underlying objects (`Ref::eq` may be added later). However, `nullptr`/`NULL` uniquely represents null references. + +* The API already encompasses current proposals like [multiple return values](https://github.com/WebAssembly/multi-value/blob/master/proposals/multi-value/Overview.md) and [reference types](https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md), but not yet [threads](https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md). + + +### Prototype Implementation + +* This repo contains a prototype implementation based on V8 is in `src`. + + * Note that this requires adding a module to V8, so it patches V8's build file. + +* The C API is implemented on top of the C++ API. + +* See `Makefile` for build recipe. Canonical steps to run examples: + + 1. `make v8-checkout` + 2. `make v8` + 3. `make all` + + +#### Limitations + +V8 implementation: + +* Currently requires patching V8 by adding a module. + +* Host functions (`Func::make`) create a JavaScript function internally, since V8 cannot handle raw C imports yet. + +* As a consequence, does not support multiple results in external calls or host functions. + +* Host functions and host globals are created through auxiliary modules constructed on the fly, to work around limitations in JS API. + +* `Shared` is currently implemented via serialisation, since V8 does not currently have direct support for cross-isolate sharing. + + +### Other Implementations + +Currently, known implementations of this API are included in + +* V8 natively (both C and C++) +* Wabt (only C?) +* Wasmtime (only C?) +* [Wasmer](https://github.com/wasmerio/wasmer/tree/master/lib/c-api) (only C, C++ coming soon) + + +### TODO + +Possible API tweaks: + + * Add `Ref::eq` (or better, a subclass `EqRef::eq`) for reference equality? + + * Add a way to return error messages from `Module::make` and `Module::validate`. + + * Use `restrict` in C API? + + * Find a way to perform C callbacks through C++ without extra wrapper? + + * Add iterators to `vec` class? diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/callback.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/callback.c new file mode 100644 index 0000000..2fddb16 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/callback.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include + +#include "wasm.h" + +#define own + +// Print a Wasm value +void wasm_val_print(wasm_val_t val) { + switch (val.kind) { + case WASM_I32: { + printf("%" PRIu32, val.of.i32); + } break; + case WASM_I64: { + printf("%" PRIu64, val.of.i64); + } break; + case WASM_F32: { + printf("%f", val.of.f32); + } break; + case WASM_F64: { + printf("%g", val.of.f64); + } break; + case WASM_ANYREF: + case WASM_FUNCREF: { + if (val.of.ref == NULL) { + printf("null"); + } else { + printf("ref(%p)", val.of.ref); + } + } break; + } +} + +// A function to be called from Wasm code. +own wasm_trap_t* print_callback( + const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + printf("Calling back...\n> "); + wasm_val_print(args->data[0]); + printf("\n"); + + wasm_val_copy(&results->data[0], &args->data[0]); + return NULL; +} + + +// A function closure. +own wasm_trap_t* closure_callback( + void* env, const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + int i = *(int*)env; + printf("Calling back closure...\n"); + printf("> %d\n", i); + + results->data[0].kind = WASM_I32; + results->data[0].of.i32 = (int32_t)i; + return NULL; +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); + FILE* file = fopen("callback.wasm", "rb"); + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Create external print functions. + printf("Creating callback...\n"); + own wasm_functype_t* print_type = wasm_functype_new_1_1(wasm_valtype_new_i32(), wasm_valtype_new_i32()); + own wasm_func_t* print_func = wasm_func_new(store, print_type, print_callback); + + int i = 42; + own wasm_functype_t* closure_type = wasm_functype_new_0_1(wasm_valtype_new_i32()); + own wasm_func_t* closure_func = wasm_func_new_with_env(store, closure_type, closure_callback, &i, NULL); + + wasm_functype_delete(print_type); + wasm_functype_delete(closure_type); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_t* externs[] = { + wasm_func_as_extern(print_func), wasm_func_as_extern(closure_func) + }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + wasm_func_delete(print_func); + wasm_func_delete(closure_func); + + // Extract export. + printf("Extracting export...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return 1; + } + const wasm_func_t* run_func = wasm_extern_as_func(exports.data[0]); + if (run_func == NULL) { + printf("> Error accessing export!\n"); + return 1; + } + + wasm_module_delete(module); + wasm_instance_delete(instance); + + // Call. + printf("Calling export...\n"); + wasm_val_t as[2] = { WASM_I32_VAL(3), WASM_I32_VAL(4) }; + wasm_val_t rs[1] = { WASM_INIT_VAL }; + wasm_val_vec_t args = WASM_ARRAY_VEC(as); + wasm_val_vec_t results = WASM_ARRAY_VEC(rs); + if (wasm_func_call(run_func, &args, &results)) { + printf("> Error calling function!\n"); + return 1; + } + + wasm_extern_vec_delete(&exports); + + // Print result. + printf("Printing result...\n"); + printf("> %u\n", rs[0].of.i32); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/callback.cc b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/callback.cc new file mode 100644 index 0000000..957629c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/callback.cc @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include + +#include "wasm.hh" + +// Print a Wasm value +auto operator<<(std::ostream& out, const wasm::Val& val) -> std::ostream& { + switch (val.kind()) { + case wasm::ValKind::I32: { + out << val.i32(); + } break; + case wasm::ValKind::I64: { + out << val.i64(); + } break; + case wasm::ValKind::F32: { + out << val.f32(); + } break; + case wasm::ValKind::F64: { + out << val.f64(); + } break; + case wasm::ValKind::ANYREF: + case wasm::ValKind::FUNCREF: { + if (val.ref() == nullptr) { + out << "null"; + } else { + out << "ref(" << val.ref() << ")"; + } + } break; + } + return out; +} + +// A function to be called from Wasm code. +auto print_callback( + const wasm::vec& args, wasm::vec& results +) -> wasm::own { + std::cout << "Calling back..." << std::endl << "> " << args[0] << std::endl; + results[0] = args[0].copy(); + return nullptr; +} + + +// A function closure. +auto closure_callback( + void* env, const wasm::vec& args, wasm::vec& results +) -> wasm::own { + auto i = *reinterpret_cast(env); + std::cout << "Calling back closure..." << std::endl; + std::cout << "> " << i << std::endl; + results[0] = wasm::Val::i32(static_cast(i)); + return nullptr; +} + + +void run() { + // Initialize. + std::cout << "Initializing..." << std::endl; + auto engine = wasm::Engine::make(); + auto store_ = wasm::Store::make(engine.get()); + auto store = store_.get(); + + // Load binary. + std::cout << "Loading binary..." << std::endl; + std::ifstream file("callback.wasm"); + file.seekg(0, std::ios_base::end); + auto file_size = file.tellg(); + file.seekg(0); + auto binary = wasm::vec::make_uninitialized(file_size); + file.read(binary.get(), file_size); + file.close(); + if (file.fail()) { + std::cout << "> Error loading module!" << std::endl; + exit(1); + } + + // Compile. + std::cout << "Compiling module..." << std::endl; + auto module = wasm::Module::make(store, binary); + if (!module) { + std::cout << "> Error compiling module!" << std::endl; + exit(1); + } + + // Create external print functions. + std::cout << "Creating callback..." << std::endl; + auto print_type = wasm::FuncType::make( + wasm::ownvec::make(wasm::ValType::make(wasm::ValKind::I32)), + wasm::ownvec::make(wasm::ValType::make(wasm::ValKind::I32)) + ); + auto print_func = wasm::Func::make(store, print_type.get(), print_callback); + + // Creating closure. + std::cout << "Creating closure..." << std::endl; + int i = 42; + auto closure_type = wasm::FuncType::make( + wasm::ownvec::make(), + wasm::ownvec::make(wasm::ValType::make(wasm::ValKind::I32)) + ); + auto closure_func = wasm::Func::make(store, closure_type.get(), closure_callback, &i); + + // Instantiate. + std::cout << "Instantiating module..." << std::endl; + auto imports = wasm::vec::make( + print_func.get(), closure_func.get()); + auto instance = wasm::Instance::make(store, module.get(), imports); + if (!instance) { + std::cout << "> Error instantiating module!" << std::endl; + exit(1); + } + + // Extract export. + std::cout << "Extracting export..." << std::endl; + auto exports = instance->exports(); + if (exports.size() == 0 || exports[0]->kind() != wasm::ExternKind::FUNC || !exports[0]->func()) { + std::cout << "> Error accessing export!" << std::endl; + exit(1); + } + auto run_func = exports[0]->func(); + + // Call. + std::cout << "Calling export..." << std::endl; + auto args = wasm::vec::make(wasm::Val::i32(3), wasm::Val::i32(4)); + auto results = wasm::vec::make_uninitialized(1); + if (run_func->call(args, results)) { + std::cout << "> Error calling function!" << std::endl; + exit(1); + } + + // Print result. + std::cout << "Printing result..." << std::endl; + std::cout << "> " << results[0].i32() << std::endl; + + // Shut down. + std::cout << "Shutting down..." << std::endl; +} + + +int main(int argc, const char* argv[]) { + run(); + std::cout << "Done." << std::endl; + return 0; +} + diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/callback.wasm b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/callback.wasm new file mode 100644 index 0000000..7e00b58 Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/callback.wasm differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/callback.wat b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/callback.wat new file mode 100644 index 0000000..d86195f --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/callback.wat @@ -0,0 +1,10 @@ +(module + (func $print (import "" "print") (param i32) (result i32)) + (func $closure (import "" "closure") (result i32)) + (func (export "run") (param $x i32) (param $y i32) (result i32) + (i32.add + (call $print (i32.add (local.get $x) (local.get $y))) + (call $closure) + ) + ) +) diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/finalize.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/finalize.c new file mode 100644 index 0000000..4f2efc5 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/finalize.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include "wasm.h" + +#define own + +const int iterations = 100000; + +int live_count = 0; + +void finalize(void* data) { + intptr_t i = (intptr_t)data; + if (i % (iterations / 10) == 0) printf("Finalizing #%" PRIdPTR "...\n", i); + --live_count; +} + +void run_in_store(wasm_store_t* store) { + // Load binary. + printf("Loading binary...\n"); + FILE* file = fopen("finalize.wasm", "rb"); + if (!file) { + printf("> Error loading module!\n"); + exit(1); + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + exit(1); + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + exit(1); + } + + wasm_byte_vec_delete(&binary); + + // Instantiate. + printf("Instantiating modules...\n"); + for (int i = 0; i <= iterations; ++i) { + if (i % (iterations / 10) == 0) printf("%d\n", i); + wasm_extern_vec_t imports = WASM_EMPTY_VEC; + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module %d!\n", i); + exit(1); + } + void* data = (void*)(intptr_t)i; + wasm_instance_set_host_info_with_finalizer(instance, data, &finalize); + wasm_instance_delete(instance); + ++live_count; + } + + wasm_module_delete(module); +} + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + + printf("Live count %d\n", live_count); + printf("Creating store 1...\n"); + wasm_store_t* store1 = wasm_store_new(engine); + + printf("Running in store 1...\n"); + run_in_store(store1); + printf("Live count %d\n", live_count); + + printf("Creating store 2...\n"); + wasm_store_t* store2 = wasm_store_new(engine); + + printf("Running in store 2...\n"); + run_in_store(store2); + printf("Live count %d\n", live_count); + + printf("Deleting store 2...\n"); + wasm_store_delete(store2); + printf("Live count %d\n", live_count); + + printf("Running in store 1...\n"); + run_in_store(store1); + printf("Live count %d\n", live_count); + + printf("Deleting store 1...\n"); + wasm_store_delete(store1); + printf("Live count %d\n", live_count); + + assert(live_count == 0); + + // Shut down. + printf("Shutting down...\n"); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/finalize.cc b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/finalize.cc new file mode 100644 index 0000000..ce7e972 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/finalize.cc @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include + +#include "wasm.hh" + + +const int iterations = 100000; + +int live_count = 0; + +void finalize(void* data) { + intptr_t i = reinterpret_cast(data); + if (i % (iterations / 10) == 0) { + std::cout << "Finalizing #" << i << "..." << std::endl; + } + --live_count; +} + +void run_in_store(wasm::Store* store) { + // Load binary. + std::cout << "Loading binary..." << std::endl; + std::ifstream file("finalize.wasm"); + file.seekg(0, std::ios_base::end); + auto file_size = file.tellg(); + file.seekg(0); + auto binary = wasm::vec::make_uninitialized(file_size); + file.read(binary.get(), file_size); + file.close(); + if (file.fail()) { + std::cout << "> Error loading module!" << std::endl; + exit(1); + } + + // Compile. + std::cout << "Compiling module..." << std::endl; + auto module = wasm::Module::make(store, binary); + if (!module) { + std::cout << "> Error compiling module!" << std::endl; + exit(1); + } + + // Instantiate. + std::cout << "Instantiating modules..." << std::endl; + for (int i = 0; i <= iterations; ++i) { + if (i % (iterations / 10) == 0) std::cout << i << std::endl; + auto imports = wasm::vec::make(); + auto instance = wasm::Instance::make(store, module.get(), imports); + if (!instance) { + std::cout << "> Error instantiating module " << i << "!" << std::endl; + exit(1); + } + instance->set_host_info(reinterpret_cast(i), &finalize); + ++live_count; + } + + // Shut down. + std::cout << "Shutting down..." << std::endl; +} + + +void run() { + // Initialize. + std::cout << "Initializing..." << std::endl; + auto engine = wasm::Engine::make(); + + std::cout << "Live count " << live_count << std::endl; + std::cout << "Creating store 1..." << std::endl; + auto store1 = wasm::Store::make(engine.get()); + + std::cout << "Running in store 1..." << std::endl; + run_in_store(store1.get()); + std::cout << "Live count " << live_count << std::endl; + + { + std::cout << "Creating store 2..." << std::endl; + auto store2 = wasm::Store::make(engine.get()); + + std::cout << "Running in store 2..." << std::endl; + run_in_store(store2.get()); + std::cout << "Live count " << live_count << std::endl; + + std::cout << "Deleting store 2..." << std::endl; + std::cout << "Live count " << live_count << std::endl; + } + + std::cout << "Running in store 1..." << std::endl; + run_in_store(store1.get()); + std::cout << "Live count " << live_count << std::endl; + + std::cout << "Deleting store 1..." << std::endl; +} + + +int main(int argc, const char* argv[]) { + run(); + std::cout << "Live count " << live_count << std::endl; + assert(live_count == 0); + std::cout << "Done." << std::endl; + return 0; +} + diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/finalize.wasm b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/finalize.wasm new file mode 100644 index 0000000..74f9c56 Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/finalize.wasm differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/finalize.wat b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/finalize.wat new file mode 100644 index 0000000..6237e73 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/finalize.wat @@ -0,0 +1,5 @@ +(module + (func (export "f")) + (func (export "g")) + (func (export "h")) +) diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/global.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/global.c new file mode 100644 index 0000000..5bd4033 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/global.c @@ -0,0 +1,240 @@ +#include +#include +#include +#include + +#include "wasm.h" + +#define own + +wasm_global_t* get_export_global(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_global(exports->data[i])) { + printf("> Error accessing global export %zu!\n", i); + exit(1); + } + return wasm_extern_as_global(exports->data[i]); +} + +wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_func(exports->data[i])) { + printf("> Error accessing function export %zu!\n", i); + exit(1); + } + return wasm_extern_as_func(exports->data[i]); +} + + +#define check(val, type, expected) \ + if (val.of.type != expected) { \ + printf("> Error reading value\n"); \ + exit(1); \ + } + +#define check_global(global, type, expected) \ + { \ + wasm_val_t val; \ + wasm_global_get(global, &val); \ + check(val, type, expected); \ + } + +#define check_call(func, type, expected) \ + { \ + wasm_val_t vs[1]; \ + wasm_val_vec_t args = WASM_EMPTY_VEC; \ + wasm_val_vec_t results = WASM_ARRAY_VEC(vs); \ + wasm_func_call(func, &args, &results); \ + check(vs[0], type, expected); \ + } + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); + FILE* file = fopen("global.wasm", "rb"); + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Create external globals. + printf("Creating globals...\n"); + own wasm_globaltype_t* const_f32_type = wasm_globaltype_new( + wasm_valtype_new(WASM_F32), WASM_CONST); + own wasm_globaltype_t* const_i64_type = wasm_globaltype_new( + wasm_valtype_new(WASM_I64), WASM_CONST); + own wasm_globaltype_t* var_f32_type = wasm_globaltype_new( + wasm_valtype_new(WASM_F32), WASM_VAR); + own wasm_globaltype_t* var_i64_type = wasm_globaltype_new( + wasm_valtype_new(WASM_I64), WASM_VAR); + + wasm_val_t val_f32_1 = WASM_F32_VAL(1); + own wasm_global_t* const_f32_import = + wasm_global_new(store, const_f32_type, &val_f32_1); + wasm_val_t val_i64_2 = WASM_I64_VAL(2); + own wasm_global_t* const_i64_import = + wasm_global_new(store, const_i64_type, &val_i64_2); + wasm_val_t val_f32_3 = WASM_F32_VAL(3); + own wasm_global_t* var_f32_import = + wasm_global_new(store, var_f32_type, &val_f32_3); + wasm_val_t val_i64_4 = WASM_I64_VAL(4); + own wasm_global_t* var_i64_import = + wasm_global_new(store, var_i64_type, &val_i64_4); + + wasm_globaltype_delete(const_f32_type); + wasm_globaltype_delete(const_i64_type); + wasm_globaltype_delete(var_f32_type); + wasm_globaltype_delete(var_i64_type); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_t* externs[] = { + wasm_global_as_extern(const_f32_import), + wasm_global_as_extern(const_i64_import), + wasm_global_as_extern(var_f32_import), + wasm_global_as_extern(var_i64_import) + }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + wasm_module_delete(module); + + // Extract export. + printf("Extracting exports...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + size_t i = 0; + wasm_global_t* const_f32_export = get_export_global(&exports, i++); + wasm_global_t* const_i64_export = get_export_global(&exports, i++); + wasm_global_t* var_f32_export = get_export_global(&exports, i++); + wasm_global_t* var_i64_export = get_export_global(&exports, i++); + wasm_func_t* get_const_f32_import = get_export_func(&exports, i++); + wasm_func_t* get_const_i64_import = get_export_func(&exports, i++); + wasm_func_t* get_var_f32_import = get_export_func(&exports, i++); + wasm_func_t* get_var_i64_import = get_export_func(&exports, i++); + wasm_func_t* get_const_f32_export = get_export_func(&exports, i++); + wasm_func_t* get_const_i64_export = get_export_func(&exports, i++); + wasm_func_t* get_var_f32_export = get_export_func(&exports, i++); + wasm_func_t* get_var_i64_export = get_export_func(&exports, i++); + wasm_func_t* set_var_f32_import = get_export_func(&exports, i++); + wasm_func_t* set_var_i64_import = get_export_func(&exports, i++); + wasm_func_t* set_var_f32_export = get_export_func(&exports, i++); + wasm_func_t* set_var_i64_export = get_export_func(&exports, i++); + + // Try cloning. + own wasm_global_t* copy = wasm_global_copy(var_f32_import); + assert(wasm_global_same(var_f32_import, copy)); + wasm_global_delete(copy); + + // Interact. + printf("Accessing globals...\n"); + + // Check initial values. + check_global(const_f32_import, f32, 1); + check_global(const_i64_import, i64, 2); + check_global(var_f32_import, f32, 3); + check_global(var_i64_import, i64, 4); + check_global(const_f32_export, f32, 5); + check_global(const_i64_export, i64, 6); + check_global(var_f32_export, f32, 7); + check_global(var_i64_export, i64, 8); + + check_call(get_const_f32_import, f32, 1); + check_call(get_const_i64_import, i64, 2); + check_call(get_var_f32_import, f32, 3); + check_call(get_var_i64_import, i64, 4); + check_call(get_const_f32_export, f32, 5); + check_call(get_const_i64_export, i64, 6); + check_call(get_var_f32_export, f32, 7); + check_call(get_var_i64_export, i64, 8); + + // Modify variables through API and check again. + wasm_val_t val33 = WASM_F32_VAL(33); + wasm_global_set(var_f32_import, &val33); + wasm_val_t val34 = WASM_I64_VAL(34); + wasm_global_set(var_i64_import, &val34); + wasm_val_t val37 = WASM_F32_VAL(37); + wasm_global_set(var_f32_export, &val37); + wasm_val_t val38 = WASM_I64_VAL(38); + wasm_global_set(var_i64_export, &val38); + + check_global(var_f32_import, f32, 33); + check_global(var_i64_import, i64, 34); + check_global(var_f32_export, f32, 37); + check_global(var_i64_export, i64, 38); + + check_call(get_var_f32_import, f32, 33); + check_call(get_var_i64_import, i64, 34); + check_call(get_var_f32_export, f32, 37); + check_call(get_var_i64_export, i64, 38); + + // Modify variables through calls and check again. + wasm_val_vec_t res = WASM_EMPTY_VEC; + wasm_val_t vs73[] = { WASM_F32_VAL(73) }; + wasm_val_vec_t args73 = WASM_ARRAY_VEC(vs73); + wasm_func_call(set_var_f32_import, &args73, &res); + wasm_val_t vs74[] = { WASM_I64_VAL(74) }; + wasm_val_vec_t args74 = WASM_ARRAY_VEC(vs74); + wasm_func_call(set_var_i64_import, &args74, &res); + wasm_val_t vs77[] = { WASM_F32_VAL(77) }; + wasm_val_vec_t args77 = WASM_ARRAY_VEC(vs77); + wasm_func_call(set_var_f32_export, &args77, &res); + wasm_val_t vs78[] = { WASM_I64_VAL(78) }; + wasm_val_vec_t args78 = WASM_ARRAY_VEC(vs78); + wasm_func_call(set_var_i64_export, &args78, &res); + + check_global(var_f32_import, f32, 73); + check_global(var_i64_import, i64, 74); + check_global(var_f32_export, f32, 77); + check_global(var_i64_export, i64, 78); + + check_call(get_var_f32_import, f32, 73); + check_call(get_var_i64_import, i64, 74); + check_call(get_var_f32_export, f32, 77); + check_call(get_var_i64_export, i64, 78); + + wasm_global_delete(const_f32_import); + wasm_global_delete(const_i64_import); + wasm_global_delete(var_f32_import); + wasm_global_delete(var_i64_import); + wasm_extern_vec_delete(&exports); + wasm_instance_delete(instance); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/global.cc b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/global.cc new file mode 100644 index 0000000..178eb61 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/global.cc @@ -0,0 +1,198 @@ +#include +#include +#include +#include +#include + +#include "wasm.hh" + + +auto get_export_global(wasm::ownvec& exports, size_t i) -> wasm::Global* { + if (exports.size() <= i || !exports[i]->global()) { + std::cout << "> Error accessing global export " << i << "!" << std::endl; + exit(1); + } + return exports[i]->global(); +} + +auto get_export_func(const wasm::ownvec& exports, size_t i) -> const wasm::Func* { + if (exports.size() <= i || !exports[i]->func()) { + std::cout << "> Error accessing function export " << i << "!" << std::endl; + exit(1); + } + return exports[i]->func(); +} + +template +void check(T actual, U expected) { + if (actual != expected) { + std::cout << "> Error reading value, expected " << expected << ", got " << actual << std::endl; + exit(1); + } +} + +auto call(const wasm::Func* func) -> wasm::Val { + auto args = wasm::vec::make(); + auto results = wasm::vec::make_uninitialized(1); + if (func->call(args, results)) { + std::cout << "> Error calling function!" << std::endl; + exit(1); + } + return results[0].copy(); +} + +void call(const wasm::Func* func, wasm::Val&& arg) { + auto args = wasm::vec::make(std::move(arg)); + auto results = wasm::vec::make(); + if (func->call(args, results)) { + std::cout << "> Error calling function!" << std::endl; + exit(1); + } +} + + +void run() { + // Initialize. + std::cout << "Initializing..." << std::endl; + auto engine = wasm::Engine::make(); + auto store_ = wasm::Store::make(engine.get()); + auto store = store_.get(); + + // Load binary. + std::cout << "Loading binary..." << std::endl; + std::ifstream file("global.wasm"); + file.seekg(0, std::ios_base::end); + auto file_size = file.tellg(); + file.seekg(0); + auto binary = wasm::vec::make_uninitialized(file_size); + file.read(binary.get(), file_size); + file.close(); + if (file.fail()) { + std::cout << "> Error loading module!" << std::endl; + exit(1); + } + + // Compile. + std::cout << "Compiling module..." << std::endl; + auto module = wasm::Module::make(store, binary); + if (!module) { + std::cout << "> Error compiling module!" << std::endl; + exit(1); + } + + // Create external globals. + std::cout << "Creating globals..." << std::endl; + auto const_f32_type = wasm::GlobalType::make( + wasm::ValType::make(wasm::ValKind::F32), wasm::Mutability::CONST); + auto const_i64_type = wasm::GlobalType::make( + wasm::ValType::make(wasm::ValKind::I64), wasm::Mutability::CONST); + auto var_f32_type = wasm::GlobalType::make( + wasm::ValType::make(wasm::ValKind::F32), wasm::Mutability::VAR); + auto var_i64_type = wasm::GlobalType::make( + wasm::ValType::make(wasm::ValKind::I64), wasm::Mutability::VAR); + auto const_f32_import = wasm::Global::make(store, const_f32_type.get(), wasm::Val::f32(1)); + auto const_i64_import = wasm::Global::make(store, const_i64_type.get(), wasm::Val::i64(2)); + auto var_f32_import = wasm::Global::make(store, var_f32_type.get(), wasm::Val::f32(3)); + auto var_i64_import = wasm::Global::make(store, var_i64_type.get(), wasm::Val::i64(4)); + + // Instantiate. + std::cout << "Instantiating module..." << std::endl; + auto imports = wasm::vec::make( + const_f32_import.get(), const_i64_import.get(), + var_f32_import.get(), var_i64_import.get() + ); + auto instance = wasm::Instance::make(store, module.get(), imports); + if (!instance) { + std::cout << "> Error instantiating module!" << std::endl; + exit(1); + } + + // Extract export. + std::cout << "Extracting exports..." << std::endl; + auto exports = instance->exports(); + size_t i = 0; + auto const_f32_export = get_export_global(exports, i++); + auto const_i64_export = get_export_global(exports, i++); + auto var_f32_export = get_export_global(exports, i++); + auto var_i64_export = get_export_global(exports, i++); + auto get_const_f32_import = get_export_func(exports, i++); + auto get_const_i64_import = get_export_func(exports, i++); + auto get_var_f32_import = get_export_func(exports, i++); + auto get_var_i64_import = get_export_func(exports, i++); + auto get_const_f32_export = get_export_func(exports, i++); + auto get_const_i64_export = get_export_func(exports, i++); + auto get_var_f32_export = get_export_func(exports, i++); + auto get_var_i64_export = get_export_func(exports, i++); + auto set_var_f32_import = get_export_func(exports, i++); + auto set_var_i64_import = get_export_func(exports, i++); + auto set_var_f32_export = get_export_func(exports, i++); + auto set_var_i64_export = get_export_func(exports, i++); + + // Try cloning. + assert(var_f32_import->copy()->same(var_f32_import.get())); + + // Interact. + std::cout << "Accessing globals..." << std::endl; + + // Check initial values. + check(const_f32_import->get().f32(), 1); + check(const_i64_import->get().i64(), 2); + check(var_f32_import->get().f32(), 3); + check(var_i64_import->get().i64(), 4); + check(const_f32_export->get().f32(), 5); + check(const_i64_export->get().i64(), 6); + check(var_f32_export->get().f32(), 7); + check(var_i64_export->get().i64(), 8); + + check(call(get_const_f32_import).f32(), 1); + check(call(get_const_i64_import).i64(), 2); + check(call(get_var_f32_import).f32(), 3); + check(call(get_var_i64_import).i64(), 4); + check(call(get_const_f32_export).f32(), 5); + check(call(get_const_i64_export).i64(), 6); + check(call(get_var_f32_export).f32(), 7); + check(call(get_var_i64_export).i64(), 8); + + // Modify variables through API and check again. + var_f32_import->set(wasm::Val::f32(33)); + var_i64_import->set(wasm::Val::i64(34)); + var_f32_export->set(wasm::Val::f32(37)); + var_i64_export->set(wasm::Val::i64(38)); + + check(var_f32_import->get().f32(), 33); + check(var_i64_import->get().i64(), 34); + check(var_f32_export->get().f32(), 37); + check(var_i64_export->get().i64(), 38); + + check(call(get_var_f32_import).f32(), 33); + check(call(get_var_i64_import).i64(), 34); + check(call(get_var_f32_export).f32(), 37); + check(call(get_var_i64_export).i64(), 38); + + // Modify variables through calls and check again. + call(set_var_f32_import, wasm::Val::f32(73)); + call(set_var_i64_import, wasm::Val::i64(74)); + call(set_var_f32_export, wasm::Val::f32(77)); + call(set_var_i64_export, wasm::Val::i64(78)); + + check(var_f32_import->get().f32(), 73); + check(var_i64_import->get().i64(), 74); + check(var_f32_export->get().f32(), 77); + check(var_i64_export->get().i64(), 78); + + check(call(get_var_f32_import).f32(), 73); + check(call(get_var_i64_import).i64(), 74); + check(call(get_var_f32_export).f32(), 77); + check(call(get_var_i64_export).i64(), 78); + + // Shut down. + std::cout << "Shutting down..." << std::endl; +} + + +int main(int argc, const char* argv[]) { + run(); + std::cout << "Done." << std::endl; + return 0; +} + diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/global.wasm b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/global.wasm new file mode 100644 index 0000000..0e76863 Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/global.wasm differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/global.wat b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/global.wat new file mode 100644 index 0000000..dea0857 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/global.wat @@ -0,0 +1,27 @@ +(module + (global $f32_import (import "" "const f32") f32) + (global $i64_import (import "" "const i64") i64) + (global $mut_f32_import (import "" "var f32") (mut f32)) + (global $mut_i64_import (import "" "var i64") (mut i64)) + + (global $f32_export (export "const f32") f32 (f32.const 5)) + (global $i64_export (export "const i64") i64 (i64.const 6)) + (global $mut_f32_export (export "var f32") (mut f32) (f32.const 7)) + (global $mut_i64_export (export "var i64") (mut i64) (i64.const 8)) + + (func (export "get const f32 import") (result f32) (global.get $f32_import)) + (func (export "get const i64 import") (result i64) (global.get $i64_import)) + (func (export "get var f32 import") (result f32) (global.get $mut_f32_import)) + (func (export "get var i64 import") (result i64) (global.get $mut_i64_import)) + + (func (export "get const f32 export") (result f32) (global.get $f32_export)) + (func (export "get const i64 export") (result i64) (global.get $i64_export)) + (func (export "get var f32 export") (result f32) (global.get $mut_f32_export)) + (func (export "get var i64 export") (result i64) (global.get $mut_i64_export)) + + (func (export "set var f32 import") (param f32) (global.set $mut_f32_import (local.get 0))) + (func (export "set var i64 import") (param i64) (global.set $mut_i64_import (local.get 0))) + + (func (export "set var f32 export") (param f32) (global.set $mut_f32_export (local.get 0))) + (func (export "set var f64 export") (param i64) (global.set $mut_i64_export (local.get 0))) +) diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hello.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hello.c new file mode 100644 index 0000000..712f459 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hello.c @@ -0,0 +1,118 @@ +#include +#include +#include +#include + +#include "wasm.h" + +#define own + +// A function to be called from Wasm code. +own wasm_trap_t* hello_callback( + const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + printf("Calling back...\n"); + printf("> Hello World!\n"); + return NULL; +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); + FILE* file = fopen("hello.wasm", "rb"); + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Validate. + printf("Validating module...\n"); + if (!wasm_module_validate(store, &binary)) { + printf("> Error validating module!\n"); + return 1; + } + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Create external print functions. + printf("Creating callback...\n"); + own wasm_functype_t* hello_type = wasm_functype_new_0_0(); + own wasm_func_t* hello_func = + wasm_func_new(store, hello_type, hello_callback); + + wasm_functype_delete(hello_type); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_t* externs[] = { wasm_func_as_extern(hello_func) }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + wasm_func_delete(hello_func); + + // Extract export. + printf("Extracting export...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return 1; + } + const wasm_func_t* run_func = wasm_extern_as_func(exports.data[0]); + if (run_func == NULL) { + printf("> Error accessing export!\n"); + return 1; + } + + wasm_module_delete(module); + wasm_instance_delete(instance); + + // Call. + printf("Calling export...\n"); + wasm_val_vec_t args = WASM_EMPTY_VEC; + wasm_val_vec_t results = WASM_EMPTY_VEC; + if (wasm_func_call(run_func, &args, &results)) { + printf("> Error calling function!\n"); + return 1; + } + + wasm_extern_vec_delete(&exports); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hello.cc b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hello.cc new file mode 100644 index 0000000..c2a73c8 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hello.cc @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include + +#include "wasm.hh" + + +// A function to be called from Wasm code. +auto hello_callback( + const wasm::vec& args, wasm::vec& results +) -> wasm::own { + std::cout << "Calling back..." << std::endl; + std::cout << "> Hello world!" << std::endl; + return nullptr; +} + + +void run() { + // Initialize. + std::cout << "Initializing..." << std::endl; + auto engine = wasm::Engine::make(); + auto store_ = wasm::Store::make(engine.get()); + auto store = store_.get(); + + // Load binary. + std::cout << "Loading binary..." << std::endl; + std::ifstream file("hello.wasm"); + file.seekg(0, std::ios_base::end); + auto file_size = file.tellg(); + file.seekg(0); + auto binary = wasm::vec::make_uninitialized(file_size); + file.read(binary.get(), file_size); + file.close(); + if (file.fail()) { + std::cout << "> Error loading module!" << std::endl; + exit(1); + } + + // Validate. + std::cout << "Validating module..." << std::endl; + if (!wasm::Module::validate(store, binary)) { + std::cout << "> Error validating module!" << std::endl; + exit(1); + } + + // Compile. + std::cout << "Compiling module..." << std::endl; + auto module = wasm::Module::make(store, binary); + if (!module) { + std::cout << "> Error compiling module!" << std::endl; + exit(1); + } + + // Create external print functions. + std::cout << "Creating callback..." << std::endl; + auto hello_type = wasm::FuncType::make( + wasm::ownvec::make(), wasm::ownvec::make() + ); + auto hello_func = wasm::Func::make(store, hello_type.get(), hello_callback); + + // Instantiate. + std::cout << "Instantiating module..." << std::endl; + auto imports = wasm::vec::make(hello_func.get()); + auto instance = wasm::Instance::make(store, module.get(), imports); + if (!instance) { + std::cout << "> Error instantiating module!" << std::endl; + exit(1); + } + + // Extract export. + std::cout << "Extracting export..." << std::endl; + auto exports = instance->exports(); + if (exports.size() == 0 || exports[0]->kind() != wasm::ExternKind::FUNC || !exports[0]->func()) { + std::cout << "> Error accessing export!" << std::endl; + exit(1); + } + auto run_func = exports[0]->func(); + + // Call. + std::cout << "Calling export..." << std::endl; + auto args = wasm::vec::make(); + auto results = wasm::vec::make(); + if (run_func->call(args, results)) { + std::cout << "> Error calling function!" << std::endl; + exit(1); + } + + // Shut down. + std::cout << "Shutting down..." << std::endl; +} + + +int main(int argc, const char* argv[]) { + run(); + std::cout << "Done." << std::endl; + return 0; +} + diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hello.wasm b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hello.wasm new file mode 100644 index 0000000..2207c03 Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hello.wasm differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hello.wat b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hello.wat new file mode 100644 index 0000000..1c56c55 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hello.wat @@ -0,0 +1,4 @@ +(module + (func $hello (import "" "hello")) + (func (export "run") (call $hello)) +) diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hostref.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hostref.c new file mode 100644 index 0000000..1e787ab --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hostref.c @@ -0,0 +1,270 @@ +#include +#include +#include +#include + +#include "wasm.h" + +#define own + + +// A function to be called from Wasm code. +own wasm_trap_t* callback( + const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + printf("Calling back...\n> "); + printf("> %p\n", + args->data[0].of.ref ? wasm_ref_get_host_info(args->data[0].of.ref) : NULL); + wasm_val_copy(&results->data[0], &args->data[0]); + return NULL; +} + + +wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_func(exports->data[i])) { + printf("> Error accessing function export %zu!\n", i); + exit(1); + } + return wasm_extern_as_func(exports->data[i]); +} + +wasm_global_t* get_export_global(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_global(exports->data[i])) { + printf("> Error accessing global export %zu!\n", i); + exit(1); + } + return wasm_extern_as_global(exports->data[i]); +} + +wasm_table_t* get_export_table(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_table(exports->data[i])) { + printf("> Error accessing table export %zu!\n", i); + exit(1); + } + return wasm_extern_as_table(exports->data[i]); +} + + +own wasm_ref_t* call_v_r(const wasm_func_t* func) { + printf("call_v_r... "); fflush(stdout); + wasm_val_t rs[] = { WASM_INIT_VAL }; + wasm_val_vec_t args = WASM_EMPTY_VEC; + wasm_val_vec_t results = WASM_ARRAY_VEC(rs); + if (wasm_func_call(func, &args, &results)) { + printf("> Error calling function!\n"); + exit(1); + } + printf("okay\n"); + return rs[0].of.ref; +} + +void call_r_v(const wasm_func_t* func, wasm_ref_t* ref) { + printf("call_r_v... "); fflush(stdout); + wasm_val_t vs[1] = { WASM_REF_VAL(ref) }; + wasm_val_vec_t args = WASM_ARRAY_VEC(vs); + wasm_val_vec_t results = WASM_EMPTY_VEC; + if (wasm_func_call(func, &args, &results)) { + printf("> Error calling function!\n"); + exit(1); + } + printf("okay\n"); +} + +own wasm_ref_t* call_r_r(const wasm_func_t* func, wasm_ref_t* ref) { + printf("call_r_r... "); fflush(stdout); + wasm_val_t vs[1] = { WASM_REF_VAL(ref) }; + wasm_val_t rs[1] = { WASM_INIT_VAL }; + wasm_val_vec_t args = WASM_ARRAY_VEC(vs); + wasm_val_vec_t results = WASM_ARRAY_VEC(rs); + if (wasm_func_call(func, &args, &results)) { + printf("> Error calling function!\n"); + exit(1); + } + printf("okay\n"); + return rs[0].of.ref; +} + +void call_ir_v(const wasm_func_t* func, int32_t i, wasm_ref_t* ref) { + printf("call_ir_v... "); fflush(stdout); + wasm_val_t vs[2] = { WASM_I32_VAL(i), WASM_REF_VAL(ref) }; + wasm_val_vec_t args = WASM_ARRAY_VEC(vs); + wasm_val_vec_t results = WASM_EMPTY_VEC; + if (wasm_func_call(func, &args, &results)) { + printf("> Error calling function!\n"); + exit(1); + } + printf("okay\n"); +} + +own wasm_ref_t* call_i_r(const wasm_func_t* func, int32_t i) { + printf("call_i_r... "); fflush(stdout); + wasm_val_t vs[1] = { WASM_I32_VAL(i) }; + wasm_val_t rs[1] = { WASM_INIT_VAL }; + wasm_val_vec_t args = WASM_ARRAY_VEC(vs); + wasm_val_vec_t results = WASM_ARRAY_VEC(rs); + if (wasm_func_call(func, &args, &results)) { + printf("> Error calling function!\n"); + exit(1); + } + printf("okay\n"); + return rs[0].of.ref; +} + +void check(own wasm_ref_t* actual, const wasm_ref_t* expected) { + if (actual != expected && + !(actual && expected && wasm_ref_same(actual, expected))) { + printf("> Error reading reference, expected %p, got %p\n", + expected ? wasm_ref_get_host_info(expected) : NULL, + actual ? wasm_ref_get_host_info(actual) : NULL); + exit(1); + } + if (actual) wasm_ref_delete(actual); +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); + FILE* file = fopen("hostref.wasm", "rb"); + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Create external callback function. + printf("Creating callback...\n"); + own wasm_functype_t* callback_type = wasm_functype_new_1_1( + wasm_valtype_new(WASM_ANYREF), wasm_valtype_new(WASM_ANYREF)); + own wasm_func_t* callback_func = + wasm_func_new(store, callback_type, callback); + + wasm_functype_delete(callback_type); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_t* externs[] = { wasm_func_as_extern(callback_func) }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + wasm_func_delete(callback_func); + wasm_module_delete(module); + + // Extract export. + printf("Extracting exports...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + size_t i = 0; + wasm_global_t* global = get_export_global(&exports, i++); + wasm_table_t* table = get_export_table(&exports, i++); + wasm_func_t* global_set = get_export_func(&exports, i++); + wasm_func_t* global_get = get_export_func(&exports, i++); + wasm_func_t* table_set = get_export_func(&exports, i++); + wasm_func_t* table_get = get_export_func(&exports, i++); + wasm_func_t* func_call = get_export_func(&exports, i++); + + wasm_instance_delete(instance); + + // Create host references. + printf("Creating host references...\n"); + own wasm_ref_t* host1 = wasm_foreign_as_ref(wasm_foreign_new(store)); + own wasm_ref_t* host2 = wasm_foreign_as_ref(wasm_foreign_new(store)); + wasm_ref_set_host_info(host1, (void*)1); + wasm_ref_set_host_info(host2, (void*)2); + + // Some sanity checks. + check(NULL, NULL); + check(wasm_ref_copy(host1), host1); + check(wasm_ref_copy(host2), host2); + + own wasm_val_t val; + val.kind = WASM_ANYREF; + val.of.ref = wasm_ref_copy(host1); + check(wasm_ref_copy(val.of.ref), host1); + own wasm_ref_t* ref = val.of.ref; + check(wasm_ref_copy(ref), host1); + wasm_val_delete(&val); + + // Interact. + printf("Accessing global...\n"); + check(call_v_r(global_get), NULL); + call_r_v(global_set, host1); + check(call_v_r(global_get), host1); + call_r_v(global_set, host2); + check(call_v_r(global_get), host2); + call_r_v(global_set, NULL); + check(call_v_r(global_get), NULL); + + wasm_global_get(global, &val); + assert(val.kind == WASM_ANYREF); + check(val.of.ref, NULL); + val.of.ref = host2; + wasm_global_set(global, &val); + check(call_v_r(global_get), host2); + wasm_global_get(global, &val); + assert(val.kind == WASM_ANYREF); + check(val.of.ref, host2); + + printf("Accessing table...\n"); + check(call_i_r(table_get, 0), NULL); + check(call_i_r(table_get, 1), NULL); + call_ir_v(table_set, 0, host1); + call_ir_v(table_set, 1, host2); + check(call_i_r(table_get, 0), host1); + check(call_i_r(table_get, 1), host2); + call_ir_v(table_set, 0, NULL); + check(call_i_r(table_get, 0), NULL); + + check(wasm_table_get(table, 2), NULL); + wasm_table_set(table, 2, host1); + check(call_i_r(table_get, 2), host1); + check(wasm_table_get(table, 2), host1); + + printf("Accessing function...\n"); + check(call_r_r(func_call, NULL), NULL); + check(call_r_r(func_call, host1), host1); + check(call_r_r(func_call, host2), host2); + + wasm_ref_delete(host1); + wasm_ref_delete(host2); + + wasm_extern_vec_delete(&exports); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hostref.cc b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hostref.cc new file mode 100644 index 0000000..09c239e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hostref.cc @@ -0,0 +1,236 @@ +#include +#include +#include +#include +#include + +#include "wasm.hh" + + +// A function to be called from Wasm code. +auto callback( + const wasm::vec& args, wasm::vec& results +) -> wasm::own { + std::cout << "Calling back..." << std::endl; + std::cout << "> " << (args[0].ref() ? args[0].ref()->get_host_info() : nullptr) << std::endl; + results[0] = args[0].copy(); + return nullptr; +} + + +auto get_export_func(const wasm::ownvec& exports, size_t i) -> const wasm::Func* { + if (exports.size() <= i || !exports[i]->func()) { + std::cout << "> Error accessing function export " << i << "/" << exports.size() << "!" << std::endl; + exit(1); + } + return exports[i]->func(); +} + +auto get_export_global(wasm::ownvec& exports, size_t i) -> wasm::Global* { + if (exports.size() <= i || !exports[i]->global()) { + std::cout << "> Error accessing global export " << i << "!" << std::endl; + exit(1); + } + return exports[i]->global(); +} + +auto get_export_table(wasm::ownvec& exports, size_t i) -> wasm::Table* { + if (exports.size() <= i || !exports[i]->table()) { + std::cout << "> Error accessing table export " << i << "!" << std::endl; + exit(1); + } + return exports[i]->table(); +} + + +void call_r_v(const wasm::Func* func, const wasm::Ref* ref) { + std::cout << "call_r_v... " << std::flush; + auto args = wasm::vec::make(wasm::Val::ref(ref ? ref->copy() : wasm::own())); + auto results = wasm::vec::make(); + if (func->call(args, results)) { + std::cout << "> Error calling function!" << std::endl; + exit(1); + } + std::cout << "okay" << std::endl; +} + +auto call_v_r(const wasm::Func* func) -> wasm::own { + std::cout << "call_v_r... " << std::flush; + auto args = wasm::vec::make(); + auto results = wasm::vec::make_uninitialized(1); + if (func->call(args, results)) { + std::cout << "> Error calling function!" << std::endl; + exit(1); + } + std::cout << "okay" << std::endl; + return results[0].release_ref(); +} + +auto call_r_r(const wasm::Func* func, const wasm::Ref* ref) -> wasm::own { + std::cout << "call_r_r... " << std::flush; + auto args = wasm::vec::make(wasm::Val::ref(ref ? ref->copy() : wasm::own())); + auto results = wasm::vec::make_uninitialized(1); + if (func->call(args, results)) { + std::cout << "> Error calling function!" << std::endl; + exit(1); + } + std::cout << "okay" << std::endl; + return results[0].release_ref(); +} + +void call_ir_v(const wasm::Func* func, int32_t i, const wasm::Ref* ref) { + std::cout << "call_ir_v... " << std::flush; + auto args = wasm::vec::make( + wasm::Val::i32(i), wasm::Val::ref(ref ? ref->copy() : wasm::own())); + auto results = wasm::vec::make(); + if (func->call(args, results)) { + std::cout << "> Error calling function!" << std::endl; + exit(1); + } + std::cout << "okay" << std::endl; +} + +auto call_i_r(const wasm::Func* func, int32_t i) -> wasm::own { + std::cout << "call_i_r... " << std::flush; + auto args = wasm::vec::make(wasm::Val::i32(i)); + auto results = wasm::vec::make_uninitialized(1); + if (func->call(args, results)) { + std::cout << "> Error calling function!" << std::endl; + exit(1); + } + std::cout << "okay" << std::endl; + return results[0].release_ref(); +} + +void check(wasm::own actual, const wasm::Ref* expected) { + if (actual.get() != expected && + !(actual && expected && actual->same(expected))) { + std::cout << "> Error reading reference, expected " + << (expected ? expected->get_host_info() : nullptr) << ", got " + << (actual ? actual->get_host_info() : nullptr) << std::endl; + exit(1); + } +} + +void run() { + // Initialize. + std::cout << "Initializing..." << std::endl; + auto engine = wasm::Engine::make(); + auto store_ = wasm::Store::make(engine.get()); + auto store = store_.get(); + + // Load binary. + std::cout << "Loading binary..." << std::endl; + std::ifstream file("hostref.wasm"); + file.seekg(0, std::ios_base::end); + auto file_size = file.tellg(); + file.seekg(0); + auto binary = wasm::vec::make_uninitialized(file_size); + file.read(binary.get(), file_size); + file.close(); + if (file.fail()) { + std::cout << "> Error loading module!" << std::endl; + return; + } + + // Compile. + std::cout << "Compiling module..." << std::endl; + auto module = wasm::Module::make(store, binary); + if (!module) { + std::cout << "> Error compiling module!" << std::endl; + return; + } + + // Create external callback function. + std::cout << "Creating callback..." << std::endl; + auto callback_type = wasm::FuncType::make( + wasm::ownvec::make(wasm::ValType::make(wasm::ValKind::ANYREF)), + wasm::ownvec::make(wasm::ValType::make(wasm::ValKind::ANYREF)) + ); + auto callback_func = wasm::Func::make(store, callback_type.get(), callback); + + // Instantiate. + std::cout << "Instantiating module..." << std::endl; + auto imports = wasm::vec::make(callback_func.get()); + auto instance = wasm::Instance::make(store, module.get(), imports); + if (!instance) { + std::cout << "> Error instantiating module!" << std::endl; + return; + } + + // Extract export. + std::cout << "Extracting exports..." << std::endl; + auto exports = instance->exports(); + size_t i = 0; + auto global = get_export_global(exports, i++); + auto table = get_export_table(exports, i++); + auto global_set = get_export_func(exports, i++); + auto global_get = get_export_func(exports, i++); + auto table_set = get_export_func(exports, i++); + auto table_get = get_export_func(exports, i++); + auto func_call = get_export_func(exports, i++); + + // Create host references. + std::cout << "Creating host references..." << std::endl; + auto host1 = wasm::Foreign::make(store); + auto host2 = wasm::Foreign::make(store); + host1->set_host_info(reinterpret_cast(1)); + host2->set_host_info(reinterpret_cast(2)); + + // Some sanity checks. + check(nullptr, nullptr); + check(host1->copy(), host1.get()); + check(host2->copy(), host2.get()); + + wasm::Val val = wasm::Val::ref(host1->copy()); + check(val.ref()->copy(), host1.get()); + auto ref = val.release_ref(); + assert(val.ref() == nullptr); + check(ref->copy(), host1.get()); + + // Interact. + std::cout << "Accessing global..." << std::endl; + check(call_v_r(global_get), nullptr); + call_r_v(global_set, host1.get()); + check(call_v_r(global_get), host1.get()); + call_r_v(global_set, host2.get()); + check(call_v_r(global_get), host2.get()); + call_r_v(global_set, nullptr); + check(call_v_r(global_get), nullptr); + + check(global->get().release_ref(), nullptr); + global->set(wasm::Val(host2->copy())); + check(call_v_r(global_get), host2.get()); + check(global->get().release_ref(), host2.get()); + + std::cout << "Accessing table..." << std::endl; + check(call_i_r(table_get, 0), nullptr); + check(call_i_r(table_get, 1), nullptr); + call_ir_v(table_set, 0, host1.get()); + call_ir_v(table_set, 1, host2.get()); + check(call_i_r(table_get, 0), host1.get()); + check(call_i_r(table_get, 1), host2.get()); + call_ir_v(table_set, 0, nullptr); + check(call_i_r(table_get, 0), nullptr); + + check(table->get(2), nullptr); + table->set(2, host1.get()); + check(call_i_r(table_get, 2), host1.get()); + check(table->get(2), host1.get()); + + std::cout << "Accessing function..." << std::endl; + check(call_r_r(func_call, nullptr), nullptr); + check(call_r_r(func_call, host1.get()), host1.get()); + check(call_r_r(func_call, host2.get()), host2.get()); + + // Shut down. + std::cout << "Shutting down..." << std::endl; +} + + +int main(int argc, const char* argv[]) { + run(); + std::cout << "Done." << std::endl; + return 0; +} + diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hostref.wasm b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hostref.wasm new file mode 100644 index 0000000..7bfc728 Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hostref.wasm differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hostref.wat b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hostref.wat new file mode 100644 index 0000000..4d14ba6 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/hostref.wat @@ -0,0 +1,24 @@ +(module + (import "" "f" (func $fun (param anyref) (result anyref))) + + (global $glob (export "global") (mut anyref) (ref.null)) + (table $tab (export "table") 10 anyref) + + (func (export "global.set") (param $r anyref) + (global.set $glob (local.get $r)) + ) + (func (export "global.get") (result anyref) + (global.get $glob) + ) + + (func (export "table.set") (param $i i32) (param $r anyref) + (table.set $tab (local.get $i) (local.get $r)) + ) + (func (export "table.get") (param $i i32) (result anyref) + (table.get $tab (local.get $i)) + ) + + (func (export "func.call") (param $r anyref) (result anyref) + (call $fun (local.get $r)) + ) +) diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/memory.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/memory.c new file mode 100644 index 0000000..edd4eba --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/memory.c @@ -0,0 +1,221 @@ +#include +#include +#include +#include + +#include "wasm.h" + +#define own + + +wasm_memory_t* get_export_memory(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_memory(exports->data[i])) { + printf("> Error accessing memory export %zu!\n", i); + exit(1); + } + return wasm_extern_as_memory(exports->data[i]); +} + +wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_func(exports->data[i])) { + printf("> Error accessing function export %zu!\n", i); + exit(1); + } + return wasm_extern_as_func(exports->data[i]); +} + + +void check(bool success) { + if (!success) { + printf("> Error, expected success\n"); + exit(1); + } +} + +void check_call(wasm_func_t* func, int i, wasm_val_t args[], int32_t expected) { + wasm_val_t r = WASM_INIT_VAL; + wasm_val_vec_t args_ = {i, args}; + wasm_val_vec_t results = {1, &r}; + if (wasm_func_call(func, &args_, &results) || r.of.i32 != expected) { + printf("> Error on result\n"); + exit(1); + } +} + +void check_call0(wasm_func_t* func, int32_t expected) { + check_call(func, 0, NULL, expected); +} + +void check_call1(wasm_func_t* func, int32_t arg, int32_t expected) { + wasm_val_t args[] = { WASM_I32_VAL(arg) }; + check_call(func, 1, args, expected); +} + +void check_call2(wasm_func_t* func, int32_t arg1, int32_t arg2, int32_t expected) { + wasm_val_t args[] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) }; + check_call(func, 2, args, expected); +} + +void check_ok(wasm_func_t* func, int i, wasm_val_t args[]) { + wasm_val_vec_t args_ = {i, args}; + wasm_val_vec_t results = {0, NULL}; + if (wasm_func_call(func, &args_, &results)) { + printf("> Error on result, expected empty\n"); + exit(1); + } +} + +void check_ok2(wasm_func_t* func, int32_t arg1, int32_t arg2) { + wasm_val_t args[] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) }; + check_ok(func, 2, args); +} + +void check_trap(wasm_func_t* func, int i, wasm_val_t args[]) { + wasm_val_t r = WASM_INIT_VAL; + wasm_val_vec_t args_ = {i, args}; + wasm_val_vec_t results = {1, &r}; + own wasm_trap_t* trap = wasm_func_call(func, &args_, &results); + if (! trap) { + printf("> Error on result, expected trap\n"); + exit(1); + } + wasm_trap_delete(trap); +} + +void check_trap1(wasm_func_t* func, int32_t arg) { + wasm_val_t args[] = { WASM_I32_VAL(arg) }; + check_trap(func, 1, args); +} + +void check_trap2(wasm_func_t* func, int32_t arg1, int32_t arg2) { + wasm_val_t args[] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) }; + check_trap(func, 2, args); +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); + FILE* file = fopen("memory.wasm", "rb"); + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_vec_t imports = WASM_EMPTY_VEC; + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + // Extract export. + printf("Extracting exports...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + size_t i = 0; + wasm_memory_t* memory = get_export_memory(&exports, i++); + wasm_func_t* size_func = get_export_func(&exports, i++); + wasm_func_t* load_func = get_export_func(&exports, i++); + wasm_func_t* store_func = get_export_func(&exports, i++); + + wasm_module_delete(module); + + // Try cloning. + own wasm_memory_t* copy = wasm_memory_copy(memory); + assert(wasm_memory_same(memory, copy)); + wasm_memory_delete(copy); + + // Check initial memory. + printf("Checking memory...\n"); + check(wasm_memory_size(memory) == 2); + check(wasm_memory_data_size(memory) == 0x20000); + check(wasm_memory_data(memory)[0] == 0); + check(wasm_memory_data(memory)[0x1000] == 1); + check(wasm_memory_data(memory)[0x1003] == 4); + + check_call0(size_func, 2); + check_call1(load_func, 0, 0); + check_call1(load_func, 0x1000, 1); + check_call1(load_func, 0x1003, 4); + check_call1(load_func, 0x1ffff, 0); + check_trap1(load_func, 0x20000); + + // Mutate memory. + printf("Mutating memory...\n"); + wasm_memory_data(memory)[0x1003] = 5; + check_ok2(store_func, 0x1002, 6); + check_trap2(store_func, 0x20000, 0); + + check(wasm_memory_data(memory)[0x1002] == 6); + check(wasm_memory_data(memory)[0x1003] == 5); + check_call1(load_func, 0x1002, 6); + check_call1(load_func, 0x1003, 5); + + // Grow memory. + printf("Growing memory...\n"); + check(wasm_memory_grow(memory, 1)); + check(wasm_memory_size(memory) == 3); + check(wasm_memory_data_size(memory) == 0x30000); + + check_call1(load_func, 0x20000, 0); + check_ok2(store_func, 0x20000, 0); + check_trap1(load_func, 0x30000); + check_trap2(store_func, 0x30000, 0); + + check(! wasm_memory_grow(memory, 1)); + check(wasm_memory_grow(memory, 0)); + + wasm_extern_vec_delete(&exports); + wasm_instance_delete(instance); + + // Create stand-alone memory. + // TODO(wasm+): Once Wasm allows multiple memories, turn this into import. + printf("Creating stand-alone memory...\n"); + wasm_limits_t limits = {5, 5}; + own wasm_memorytype_t* memorytype = wasm_memorytype_new(&limits); + own wasm_memory_t* memory2 = wasm_memory_new(store, memorytype); + check(wasm_memory_size(memory2) == 5); + check(! wasm_memory_grow(memory2, 1)); + check(wasm_memory_grow(memory2, 0)); + + wasm_memorytype_delete(memorytype); + wasm_memory_delete(memory2); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/memory.cc b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/memory.cc new file mode 100644 index 0000000..6cc3619 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/memory.cc @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include + +#include "wasm.hh" + + +auto get_export_memory(wasm::ownvec& exports, size_t i) -> wasm::Memory* { + if (exports.size() <= i || !exports[i]->memory()) { + std::cout << "> Error accessing memory export " << i << "!" << std::endl; + exit(1); + } + return exports[i]->memory(); +} + +auto get_export_func(const wasm::ownvec& exports, size_t i) -> const wasm::Func* { + if (exports.size() <= i || !exports[i]->func()) { + std::cout << "> Error accessing function export " << i << "!" << std::endl; + exit(1); + } + return exports[i]->func(); +} + +template +void check(T actual, U expected) { + if (actual != expected) { + std::cout << "> Error on result, expected " << expected << ", got " << actual << std::endl; + exit(1); + } +} + +template +void check_ok(const wasm::Func* func, Args... xs) { + auto args = wasm::vec::make(wasm::Val::i32(xs)...); + auto results = wasm::vec::make(); + if (func->call(args, results)) { + std::cout << "> Error on result, expected return" << std::endl; + exit(1); + } +} + +template +void check_trap(const wasm::Func* func, Args... xs) { + auto args = wasm::vec::make(wasm::Val::i32(xs)...); + auto results = wasm::vec::make(); + if (! func->call(args, results)) { + std::cout << "> Error on result, expected trap" << std::endl; + exit(1); + } +} + +template +auto call(const wasm::Func* func, Args... xs) -> int32_t { + auto args = wasm::vec::make(wasm::Val::i32(xs)...); + auto results = wasm::vec::make_uninitialized(1); + if (func->call(args, results)) { + std::cout << "> Error on result, expected return" << std::endl; + exit(1); + } + return results[0].i32(); +} + + +void run() { + // Initialize. + std::cout << "Initializing..." << std::endl; + auto engine = wasm::Engine::make(); + auto store_ = wasm::Store::make(engine.get()); + auto store = store_.get(); + + // Load binary. + std::cout << "Loading binary..." << std::endl; + std::ifstream file("memory.wasm"); + file.seekg(0, std::ios_base::end); + auto file_size = file.tellg(); + file.seekg(0); + auto binary = wasm::vec::make_uninitialized(file_size); + file.read(binary.get(), file_size); + file.close(); + if (file.fail()) { + std::cout << "> Error loading module!" << std::endl; + exit(1); + } + + // Compile. + std::cout << "Compiling module..." << std::endl; + auto module = wasm::Module::make(store, binary); + if (!module) { + std::cout << "> Error compiling module!" << std::endl; + exit(1); + } + + // Instantiate. + std::cout << "Instantiating module..." << std::endl; + auto imports = wasm::vec::make(); + auto instance = wasm::Instance::make(store, module.get(), imports); + if (!instance) { + std::cout << "> Error instantiating module!" << std::endl; + exit(1); + } + + // Extract export. + std::cout << "Extracting exports..." << std::endl; + auto exports = instance->exports(); + size_t i = 0; + auto memory = get_export_memory(exports, i++); + auto size_func = get_export_func(exports, i++); + auto load_func = get_export_func(exports, i++); + auto store_func = get_export_func(exports, i++); + + // Try cloning. + assert(memory->copy()->same(memory)); + + // Check initial memory. + std::cout << "Checking memory..." << std::endl; + check(memory->size(), 2u); + check(memory->data_size(), 0x20000u); + check(memory->data()[0], 0); + check(memory->data()[0x1000], 1); + check(memory->data()[0x1003], 4); + + check(call(size_func), 2); + check(call(load_func, 0), 0); + check(call(load_func, 0x1000), 1); + check(call(load_func, 0x1003), 4); + check(call(load_func, 0x1ffff), 0); + check_trap(load_func, 0x20000); + + // Mutate memory. + std::cout << "Mutating memory..." << std::endl; + memory->data()[0x1003] = 5; + check_ok(store_func, 0x1002, 6); + check_trap(store_func, 0x20000, 0); + + check(memory->data()[0x1002], 6); + check(memory->data()[0x1003], 5); + check(call(load_func, 0x1002), 6); + check(call(load_func, 0x1003), 5); + + // Grow memory. + std::cout << "Growing memory..." << std::endl; + check(memory->grow(1), true); + check(memory->size(), 3u); + check(memory->data_size(), 0x30000u); + + check(call(load_func, 0x20000), 0); + check_ok(store_func, 0x20000, 0); + check_trap(load_func, 0x30000); + check_trap(store_func, 0x30000, 0); + + check(memory->grow(1), false); + check(memory->grow(0), true); + + // Create stand-alone memory. + // TODO(wasm+): Once Wasm allows multiple memories, turn this into import. + std::cout << "Creating stand-alone memory..." << std::endl; + auto memorytype = wasm::MemoryType::make(wasm::Limits(5, 5)); + auto memory2 = wasm::Memory::make(store, memorytype.get()); + check(memory2->size(), 5u); + check(memory2->grow(1), false); + check(memory2->grow(0), true); + + // Shut down. + std::cout << "Shutting down..." << std::endl; +} + + +int main(int argc, const char* argv[]) { + run(); + std::cout << "Done." << std::endl; + return 0; +} + diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/memory.wasm b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/memory.wasm new file mode 100644 index 0000000..6f6518b Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/memory.wasm differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/memory.wat b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/memory.wat new file mode 100644 index 0000000..4cf43e2 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/memory.wat @@ -0,0 +1,11 @@ +(module + (memory (export "memory") 2 3) + + (func (export "size") (result i32) (memory.size)) + (func (export "load") (param i32) (result i32) (i32.load8_s (local.get 0))) + (func (export "store") (param i32 i32) + (i32.store8 (local.get 0) (local.get 1)) + ) + + (data (i32.const 0x1000) "\01\02\03\04") +) diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/multi.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/multi.c new file mode 100644 index 0000000..4a31e7e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/multi.c @@ -0,0 +1,156 @@ +#include +#include +#include +#include + +#include "wasm.h" + +#define own + +// A function to be called from Wasm code. +own wasm_trap_t* callback( + const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + printf("Calling back...\n> "); + printf("> %"PRIu32" %"PRIu64" %"PRIu64" %"PRIu32"\n", + args->data[0].of.i32, args->data[1].of.i64, + args->data[2].of.i64, args->data[3].of.i32); + printf("\n"); + + wasm_val_copy(&results->data[0], &args->data[3]); + wasm_val_copy(&results->data[1], &args->data[1]); + wasm_val_copy(&results->data[2], &args->data[2]); + wasm_val_copy(&results->data[3], &args->data[0]); + return NULL; +} + + +// A function closure. +own wasm_trap_t* closure_callback( + void* env, const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + int i = *(int*)env; + printf("Calling back closure...\n"); + printf("> %d\n", i); + + results->data[0].kind = WASM_I32; + results->data[0].of.i32 = (int32_t)i; + return NULL; +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); + FILE* file = fopen("multi.wasm", "rb"); + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Create external print functions. + printf("Creating callback...\n"); + wasm_valtype_t* types[4] = { + wasm_valtype_new_i32(), wasm_valtype_new_i64(), + wasm_valtype_new_i64(), wasm_valtype_new_i32() + }; + own wasm_valtype_vec_t tuple1, tuple2; + wasm_valtype_vec_new(&tuple1, 4, types); + wasm_valtype_vec_copy(&tuple2, &tuple1); + own wasm_functype_t* callback_type = wasm_functype_new(&tuple1, &tuple2); + own wasm_func_t* callback_func = + wasm_func_new(store, callback_type, callback); + + wasm_functype_delete(callback_type); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_t* externs[] = { wasm_func_as_extern(callback_func) }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + wasm_func_delete(callback_func); + + // Extract export. + printf("Extracting export...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return 1; + } + const wasm_func_t* run_func = wasm_extern_as_func(exports.data[0]); + if (run_func == NULL) { + printf("> Error accessing export!\n"); + return 1; + } + + wasm_module_delete(module); + wasm_instance_delete(instance); + + // Call. + printf("Calling export...\n"); + wasm_val_t vals[4] = { + WASM_I32_VAL(1), WASM_I64_VAL(2), WASM_I64_VAL(3), WASM_I32_VAL(4) + }; + wasm_val_t res[4] = { + WASM_INIT_VAL, WASM_INIT_VAL, WASM_INIT_VAL, WASM_INIT_VAL + }; + wasm_val_vec_t args = WASM_ARRAY_VEC(vals); + wasm_val_vec_t results = WASM_ARRAY_VEC(res); + if (wasm_func_call(run_func, &args, &results)) { + printf("> Error calling function!\n"); + return 1; + } + + wasm_extern_vec_delete(&exports); + + // Print result. + printf("Printing result...\n"); + printf("> %"PRIu32" %"PRIu64" %"PRIu64" %"PRIu32"\n", + res[0].of.i32, res[1].of.i64, res[2].of.i64, res[3].of.i32); + + assert(res[0].of.i32 == 4); + assert(res[1].of.i64 == 3); + assert(res[2].of.i64 == 2); + assert(res[3].of.i32 == 1); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/multi.cc b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/multi.cc new file mode 100644 index 0000000..6d08355 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/multi.cc @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include + +#include "wasm.hh" + +// A function to be called from Wasm code. +auto callback( + const wasm::vec& args, wasm::vec& results +) -> wasm::own { + std::cout << "Calling back..." << std::endl; + std::cout << "> " << args[0].i32(); + std::cout << " " << args[1].i64(); + std::cout << " " << args[2].i64(); + std::cout << " " << args[3].i32() << std::endl; + results[0] = args[3].copy(); + results[1] = args[1].copy(); + results[2] = args[2].copy(); + results[3] = args[0].copy(); + return nullptr; +} + + +void run() { + // Initialize. + std::cout << "Initializing..." << std::endl; + auto engine = wasm::Engine::make(); + auto store_ = wasm::Store::make(engine.get()); + auto store = store_.get(); + + // Load binary. + std::cout << "Loading binary..." << std::endl; + std::ifstream file("multi.wasm"); + file.seekg(0, std::ios_base::end); + auto file_size = file.tellg(); + file.seekg(0); + auto binary = wasm::vec::make_uninitialized(file_size); + file.read(binary.get(), file_size); + file.close(); + if (file.fail()) { + std::cout << "> Error loading module!" << std::endl; + exit(1); + } + + // Compile. + std::cout << "Compiling module..." << std::endl; + auto module = wasm::Module::make(store, binary); + if (!module) { + std::cout << "> Error compiling module!" << std::endl; + exit(1); + } + + // Create external print functions. + std::cout << "Creating callback..." << std::endl; + auto tuple = wasm::ownvec::make( + wasm::ValType::make(wasm::ValKind::I32), + wasm::ValType::make(wasm::ValKind::I64), + wasm::ValType::make(wasm::ValKind::I64), + wasm::ValType::make(wasm::ValKind::I32) + ); + auto callback_type = + wasm::FuncType::make(tuple.deep_copy(), tuple.deep_copy()); + auto callback_func = wasm::Func::make(store, callback_type.get(), callback); + + // Instantiate. + std::cout << "Instantiating module..." << std::endl; + auto imports = wasm::vec::make(callback_func.get()); + auto instance = wasm::Instance::make(store, module.get(), imports); + if (!instance) { + std::cout << "> Error instantiating module!" << std::endl; + exit(1); + } + + // Extract export. + std::cout << "Extracting export..." << std::endl; + auto exports = instance->exports(); + if (exports.size() == 0 || exports[0]->kind() != wasm::ExternKind::FUNC || !exports[0]->func()) { + std::cout << "> Error accessing export!" << std::endl; + exit(1); + } + auto run_func = exports[0]->func(); + + // Call. + std::cout << "Calling export..." << std::endl; + auto args = wasm::vec::make( + wasm::Val::i32(1), wasm::Val::i64(2), wasm::Val::i64(3), wasm::Val::i32(4) + ); + auto results = wasm::vec::make_uninitialized(4); + if (wasm::own trap = run_func->call(args, results)) { + std::cout << "> Error calling function! " << trap->message().get() << std::endl; + exit(1); + } + + // Print result. + std::cout << "Printing result..." << std::endl; + std::cout << "> " << results[0].i32(); + std::cout << " " << results[1].i64(); + std::cout << " " << results[2].i64(); + std::cout << " " << results[3].i32() << std::endl; + + assert(results[0].i32() == 4); + assert(results[1].i64() == 3); + assert(results[2].i64() == 2); + assert(results[3].i32() == 1); + + // Shut down. + std::cout << "Shutting down..." << std::endl; +} + + +int main(int argc, const char* argv[]) { + run(); + std::cout << "Done." << std::endl; + return 0; +} + diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/multi.wasm b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/multi.wasm new file mode 100644 index 0000000..bff0143 Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/multi.wasm differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/multi.wat b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/multi.wat new file mode 100644 index 0000000..e7fb331 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/multi.wat @@ -0,0 +1,7 @@ +(module + (func $f (import "" "f") (param i32 i64 i64 i32) (result i32 i64 i64 i32)) + + (func $g (export "g") (param i32 i64 i64 i32) (result i32 i64 i64 i32) + (call $f (local.get 0) (local.get 2) (local.get 1) (local.get 3)) + ) +) diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/reflect.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/reflect.c new file mode 100644 index 0000000..d438318 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/reflect.c @@ -0,0 +1,171 @@ +#include +#include +#include +#include + +#include "wasm.h" + +#define own + +void print_mutability(wasm_mutability_t mut) { + switch (mut) { + case WASM_VAR: printf("var"); break; + case WASM_CONST: printf("const"); break; + } +} + +void print_limits(const wasm_limits_t* limits) { + printf("%ud", limits->min); + if (limits->max < wasm_limits_max_default) printf(" %ud", limits->max); +} + +void print_valtype(const wasm_valtype_t* type) { + switch (wasm_valtype_kind(type)) { + case WASM_I32: printf("i32"); break; + case WASM_I64: printf("i64"); break; + case WASM_F32: printf("f32"); break; + case WASM_F64: printf("f64"); break; + case WASM_ANYREF: printf("anyref"); break; + case WASM_FUNCREF: printf("funcref"); break; + } +} + +void print_valtypes(const wasm_valtype_vec_t* types) { + bool first = true; + for (size_t i = 0; i < types->size; ++i) { + if (first) { + first = false; + } else { + printf(" "); + } + print_valtype(types->data[i]); + } +} + +void print_externtype(const wasm_externtype_t* type) { + switch (wasm_externtype_kind(type)) { + case WASM_EXTERN_FUNC: { + const wasm_functype_t* functype = + wasm_externtype_as_functype_const(type); + printf("func "); + print_valtypes(wasm_functype_params(functype)); + printf(" -> "); + print_valtypes(wasm_functype_results(functype)); + } break; + case WASM_EXTERN_GLOBAL: { + const wasm_globaltype_t* globaltype = + wasm_externtype_as_globaltype_const(type); + printf("global "); + print_mutability(wasm_globaltype_mutability(globaltype)); + printf(" "); + print_valtype(wasm_globaltype_content(globaltype)); + } break; + case WASM_EXTERN_TABLE: { + const wasm_tabletype_t* tabletype = + wasm_externtype_as_tabletype_const(type); + printf("table "); + print_limits(wasm_tabletype_limits(tabletype)); + printf(" "); + print_valtype(wasm_tabletype_element(tabletype)); + } break; + case WASM_EXTERN_MEMORY: { + const wasm_memorytype_t* memorytype = + wasm_externtype_as_memorytype_const(type); + printf("memory "); + print_limits(wasm_memorytype_limits(memorytype)); + } break; + } +} + +void print_name(const wasm_name_t* name) { + printf("\"%.*s\"", (int)name->size, name->data); +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); + FILE* file = fopen("reflect.wasm", "rb"); + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_vec_t imports = WASM_EMPTY_VEC; + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + // Extract export. + printf("Extracting export...\n"); + own wasm_exporttype_vec_t export_types; + own wasm_extern_vec_t exports; + wasm_module_exports(module, &export_types); + wasm_instance_exports(instance, &exports); + assert(exports.size == export_types.size); + + for (size_t i = 0; i < exports.size; ++i) { + assert(wasm_extern_kind(exports.data[i]) == + wasm_externtype_kind(wasm_exporttype_type(export_types.data[i]))); + printf("> export %zu ", i); + print_name(wasm_exporttype_name(export_types.data[i])); + printf("\n"); + printf(">> initial: "); + print_externtype(wasm_exporttype_type(export_types.data[i])); + printf("\n"); + printf(">> current: "); + own wasm_externtype_t* current = wasm_extern_type(exports.data[i]); + print_externtype(current); + wasm_externtype_delete(current); + printf("\n"); + if (wasm_extern_kind(exports.data[i]) == WASM_EXTERN_FUNC) { + wasm_func_t* func = wasm_extern_as_func(exports.data[i]); + printf(">> in-arity: %zu", wasm_func_param_arity(func)); + printf(", out-arity: %zu\n", wasm_func_result_arity(func)); + } + } + + wasm_module_delete(module); + wasm_instance_delete(instance); + wasm_extern_vec_delete(&exports); + wasm_exporttype_vec_delete(&export_types); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/reflect.cc b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/reflect.cc new file mode 100644 index 0000000..9820d81 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/reflect.cc @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include + +#include "wasm.hh" + + +auto operator<<(std::ostream& out, wasm::Mutability mut) -> std::ostream& { + switch (mut) { + case wasm::Mutability::VAR: return out << "var"; + case wasm::Mutability::CONST: return out << "const"; + } + return out; +} + +auto operator<<(std::ostream& out, wasm::Limits limits) -> std::ostream& { + out << limits.min; + if (limits.max < wasm::Limits(0).max) out << " " << limits.max; + return out; +} + +auto operator<<(std::ostream& out, const wasm::ValType& type) -> std::ostream& { + switch (type.kind()) { + case wasm::ValKind::I32: return out << "i32"; + case wasm::ValKind::I64: return out << "i64"; + case wasm::ValKind::F32: return out << "f32"; + case wasm::ValKind::F64: return out << "f64"; + case wasm::ValKind::ANYREF: return out << "anyref"; + case wasm::ValKind::FUNCREF: return out << "funcref"; + } + return out; +} + +auto operator<<(std::ostream& out, const wasm::ownvec& types) -> std::ostream& { + bool first = true; + for (size_t i = 0; i < types.size(); ++i) { + if (first) { + first = false; + } else { + out << " "; + } + out << *types[i].get(); + } + return out; +} + +auto operator<<(std::ostream& out, const wasm::ExternType& type) -> std::ostream& { + switch (type.kind()) { + case wasm::ExternKind::FUNC: { + out << "func " << type.func()->params() << " -> " << type.func()->results(); + } break; + case wasm::ExternKind::GLOBAL: { + out << "global " << type.global()->mutability() << " " << *type.global()->content(); + } break; + case wasm::ExternKind::TABLE: { + out << "table " << type.table()->limits() << " " << *type.table()->element(); + } break; + case wasm::ExternKind::MEMORY: { + out << "memory " << type.memory()->limits(); + } break; + } + return out; +} + +auto operator<<(std::ostream& out, const wasm::Name& name) -> std::ostream& { + out << "\"" << std::string(name.get(), name.size()) << "\""; + return out; +} + + +void run() { + // Initialize. + std::cout << "Initializing..." << std::endl; + auto engine = wasm::Engine::make(); + auto store_ = wasm::Store::make(engine.get()); + auto store = store_.get(); + + // Load binary. + std::cout << "Loading binary..." << std::endl; + std::ifstream file("reflect.wasm"); + file.seekg(0, std::ios_base::end); + auto file_size = file.tellg(); + file.seekg(0); + auto binary = wasm::vec::make_uninitialized(file_size); + file.read(binary.get(), file_size); + file.close(); + if (file.fail()) { + std::cout << "> Error loading module!" << std::endl; + exit(1); + } + + // Compile. + std::cout << "Compiling module..." << std::endl; + auto module = wasm::Module::make(store, binary); + if (!module) { + std::cout << "> Error compiling module!" << std::endl; + exit(1); + } + + // Instantiate. + std::cout << "Instantiating module..." << std::endl; + auto imports = wasm::vec::make(); + auto instance = wasm::Instance::make(store, module.get(), imports); + if (!instance) { + std::cout << "> Error instantiating module!" << std::endl; + exit(1); + } + + // Extract exports. + std::cout << "Extracting export..." << std::endl; + auto export_types = module->exports(); + auto exports = instance->exports(); + assert(exports.size() == export_types.size()); + + for (size_t i = 0; i < exports.size(); ++i) { + assert(exports[i]->kind() == export_types[i]->type()->kind()); + std::cout << "> export " << i << " " << export_types[i]->name() << std::endl; + std::cout << ">> initial: " << *export_types[i]->type() << std::endl; + std::cout << ">> current: " << *exports[i]->type() << std::endl; + if (exports[i]->kind() == wasm::ExternKind::FUNC) { + auto func = exports[i]->func(); + std::cout << ">> in-arity: " << func->param_arity(); + std::cout << ", out-arity: " << func->result_arity() << std::endl; + } + } + + // Shut down. + std::cout << "Shutting down..." << std::endl; +} + + +int main(int argc, const char* argv[]) { + run(); + std::cout << "Done." << std::endl; + return 0; +} + diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/reflect.wasm b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/reflect.wasm new file mode 100644 index 0000000..15a68fe Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/reflect.wasm differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/reflect.wat b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/reflect.wat new file mode 100644 index 0000000..261dfd3 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/reflect.wat @@ -0,0 +1,6 @@ +(module + (func (export "func") (param i32 f64 f32) (result i32) (unreachable)) + (global (export "global") f64 (f64.const 0)) + (table (export "table") 0 50 anyfunc) + (memory (export "memory") 1) +) diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/serialize.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/serialize.c new file mode 100644 index 0000000..8ea1a37 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/serialize.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include + +#include "wasm.h" + +#define own + +// A function to be called from Wasm code. +own wasm_trap_t* hello_callback( + const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + printf("Calling back...\n"); + printf("> Hello World!\n"); + return NULL; +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); + FILE* file = fopen("serialize.wasm", "rb"); + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Serialize module. + printf("Serializing module...\n"); + own wasm_byte_vec_t serialized; + wasm_module_serialize(module, &serialized); + + wasm_module_delete(module); + + // Deserialize module. + printf("Deserializing module...\n"); + own wasm_module_t* deserialized = wasm_module_deserialize(store, &serialized); + if (!deserialized) { + printf("> Error deserializing module!\n"); + return 1; + } + + wasm_byte_vec_delete(&serialized); + + // Create external print functions. + printf("Creating callback...\n"); + own wasm_functype_t* hello_type = wasm_functype_new_0_0(); + own wasm_func_t* hello_func = + wasm_func_new(store, hello_type, hello_callback); + + wasm_functype_delete(hello_type); + + // Instantiate. + printf("Instantiating deserialized module...\n"); + wasm_extern_t* externs[] = { wasm_func_as_extern(hello_func) }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + own wasm_instance_t* instance = + wasm_instance_new(store, deserialized, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + wasm_func_delete(hello_func); + + // Extract export. + printf("Extracting export...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return 1; + } + const wasm_func_t* run_func = wasm_extern_as_func(exports.data[0]); + if (run_func == NULL) { + printf("> Error accessing export!\n"); + return 1; + } + + wasm_module_delete(deserialized); + wasm_instance_delete(instance); + + // Call. + printf("Calling export...\n"); + wasm_val_vec_t empty = WASM_EMPTY_VEC; + if (wasm_func_call(run_func, &empty, &empty)) { + printf("> Error calling function!\n"); + return 1; + } + + wasm_extern_vec_delete(&exports); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/serialize.cc b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/serialize.cc new file mode 100644 index 0000000..f63baeb --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/serialize.cc @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include + +#include "wasm.hh" + + +// A function to be called from Wasm code. +auto hello_callback( + const wasm::vec& args, wasm::vec& results +) -> wasm::own { + std::cout << "Calling back..." << std::endl; + std::cout << "> Hello world!" << std::endl; + return nullptr; +} + + +void run() { + // Initialize. + std::cout << "Initializing..." << std::endl; + auto engine = wasm::Engine::make(); + auto store_ = wasm::Store::make(engine.get()); + auto store = store_.get(); + + // Load binary. + std::cout << "Loading binary..." << std::endl; + std::ifstream file("serialize.wasm"); + file.seekg(0, std::ios_base::end); + auto file_size = file.tellg(); + file.seekg(0); + auto binary = wasm::vec::make_uninitialized(file_size); + file.read(binary.get(), file_size); + file.close(); + if (file.fail()) { + std::cout << "> Error loading module!" << std::endl; + exit(1); + } + + // Compile. + std::cout << "Compiling module..." << std::endl; + auto module = wasm::Module::make(store, binary); + if (!module) { + std::cout << "> Error compiling module!" << std::endl; + exit(1); + } + + // Serialize module. + std::cout << "Serializing module..." << std::endl; + auto serialized = module->serialize(); + + // Deserialize module. + std::cout << "Deserializing module..." << std::endl; + auto deserialized = wasm::Module::deserialize(store, serialized); + if (!deserialized) { + std::cout << "> Error deserializing module!" << std::endl; + exit(1); + } + + // Create external print functions. + std::cout << "Creating callback..." << std::endl; + auto hello_type = wasm::FuncType::make( + wasm::ownvec::make(), wasm::ownvec::make() + ); + auto hello_func = wasm::Func::make(store, hello_type.get(), hello_callback); + + // Instantiate. + std::cout << "Instantiating deserialized module..." << std::endl; + auto imports = wasm::vec::make(hello_func.get()); + auto instance = wasm::Instance::make(store, deserialized.get(), imports); + if (!instance) { + std::cout << "> Error instantiating module!" << std::endl; + exit(1); + } + + // Extract export. + std::cout << "Extracting export..." << std::endl; + auto exports = instance->exports(); + if (exports.size() == 0 || exports[0]->kind() != wasm::ExternKind::FUNC || !exports[0]->func()) { + std::cout << "> Error accessing export!" << std::endl; + exit(1); + } + auto run_func = exports[0]->func(); + + // Call. + std::cout << "Calling export..." << std::endl; + auto args = wasm::vec::make(); + auto results = wasm::vec::make(); + if (run_func->call(args, results)) { + std::cout << "> Error calling function!" << std::endl; + exit(1); + } + + // Shut down. + std::cout << "Shutting down..." << std::endl; +} + + +int main(int argc, const char* argv[]) { + run(); + std::cout << "Done." << std::endl; + return 0; +} + diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/serialize.wasm b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/serialize.wasm new file mode 100644 index 0000000..2207c03 Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/serialize.wasm differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/serialize.wat b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/serialize.wat new file mode 100644 index 0000000..1c56c55 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/serialize.wat @@ -0,0 +1,4 @@ +(module + (func $hello (import "" "hello")) + (func (export "run") (call $hello)) +) diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/start.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/start.c new file mode 100644 index 0000000..f60c03c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/start.c @@ -0,0 +1,106 @@ +#include +#include +#include +#include + +#include "wasm.h" + +#define own + + +void print_frame(wasm_frame_t* frame) { + printf("> %p @ 0x%zx = %"PRIu32".0x%zx\n", + wasm_frame_instance(frame), + wasm_frame_module_offset(frame), + wasm_frame_func_index(frame), + wasm_frame_func_offset(frame) + ); +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); + FILE* file = fopen("start.wasm", "rb"); + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_vec_t imports = WASM_EMPTY_VEC; + own wasm_trap_t* trap = NULL; + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, &trap); + if (instance || !trap) { + printf("> Error instantiating module, expected trap!\n"); + return 1; + } + + wasm_module_delete(module); + + // Print result. + printf("Printing message...\n"); + own wasm_name_t message; + wasm_trap_message(trap, &message); + printf("> %s\n", message.data); + + printf("Printing origin...\n"); + own wasm_frame_t* frame = wasm_trap_origin(trap); + if (frame) { + print_frame(frame); + wasm_frame_delete(frame); + } else { + printf("> Empty origin.\n"); + } + + printf("Printing trace...\n"); + own wasm_frame_vec_t trace; + wasm_trap_trace(trap, &trace); + if (trace.size > 0) { + for (size_t i = 0; i < trace.size; ++i) { + print_frame(trace.data[i]); + } + } else { + printf("> Empty trace.\n"); + } + + wasm_frame_vec_delete(&trace); + wasm_trap_delete(trap); + wasm_name_delete(&message); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/start.cc b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/start.cc new file mode 100644 index 0000000..1643b9f --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/start.cc @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include + +#include "wasm.hh" + + +void print_frame(const wasm::Frame* frame) { + std::cout << "> " << frame->instance(); + std::cout << " @ 0x" << std::hex << frame->module_offset(); + std::cout << " = " << frame->func_index(); + std::cout << ".0x" << std::hex << frame->func_offset() << std::endl; +} + + +void run() { + // Initialize. + std::cout << "Initializing..." << std::endl; + auto engine = wasm::Engine::make(); + auto store_ = wasm::Store::make(engine.get()); + auto store = store_.get(); + + // Load binary. + std::cout << "Loading binary..." << std::endl; + std::ifstream file("start.wasm"); + file.seekg(0, std::ios_base::end); + auto file_size = file.tellg(); + file.seekg(0); + auto binary = wasm::vec::make_uninitialized(file_size); + file.read(binary.get(), file_size); + file.close(); + if (file.fail()) { + std::cout << "> Error loading module!" << std::endl; + exit(1); + } + + // Compile. + std::cout << "Compiling module..." << std::endl; + auto module = wasm::Module::make(store, binary); + if (!module) { + std::cout << "> Error compiling module!" << std::endl; + exit(1); + } + + // Instantiate. + std::cout << "Instantiating module..." << std::endl; + wasm::own trap; + auto imports = wasm::vec::make(); + auto instance = wasm::Instance::make(store, module.get(), imports, &trap); + if (instance || !trap) { + std::cout << "> Error instantiating module, expected trap!" << std::endl; + exit(1); + } + + // Print result. + std::cout << "Printing message..." << std::endl; + std::cout << "> " << trap->message().get() << std::endl; + + std::cout << "Printing origin..." << std::endl; + auto frame = trap->origin(); + if (frame) { + print_frame(frame.get()); + } else { + std::cout << "> Empty origin." << std::endl; + } + + std::cout << "Printing trace..." << std::endl; + auto trace = trap->trace(); + if (trace.size() > 0) { + for (size_t i = 0; i < trace.size(); ++i) { + print_frame(trace[i].get()); + } + } else { + std::cout << "> Empty trace." << std::endl; + } + + // Shut down. + std::cout << "Shutting down..." << std::endl; +} + + +int main(int argc, const char* argv[]) { + run(); + std::cout << "Done." << std::endl; + return 0; +} + diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/start.wasm b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/start.wasm new file mode 100644 index 0000000..90cba21 Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/start.wasm differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/start.wat b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/start.wat new file mode 100644 index 0000000..eb95116 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/start.wat @@ -0,0 +1,4 @@ +(module + (func $start (unreachable)) + (start $start) +) diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/table.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/table.c new file mode 100644 index 0000000..5e91a34 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/table.c @@ -0,0 +1,214 @@ +#include +#include +#include +#include + +#include "wasm.h" + +#define own + +// A function to be called from Wasm code. +own wasm_trap_t* neg_callback( + const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + printf("Calling back...\n"); + results->data[0].kind = WASM_I32; + results->data[0].of.i32 = -args->data[0].of.i32; + return NULL; +} + + +wasm_table_t* get_export_table(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_table(exports->data[i])) { + printf("> Error accessing table export %zu!\n", i); + exit(1); + } + return wasm_extern_as_table(exports->data[i]); +} + +wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_func(exports->data[i])) { + printf("> Error accessing function export %zu!\n", i); + exit(1); + } + return wasm_extern_as_func(exports->data[i]); +} + + +void check(bool success) { + if (!success) { + printf("> Error, expected success\n"); + exit(1); + } +} + +void check_table(wasm_table_t* table, int32_t i, bool expect_set) { + own wasm_ref_t* ref = wasm_table_get(table, i); + check((ref != NULL) == expect_set); + if (ref) wasm_ref_delete(ref); +} + +void check_call(wasm_func_t* func, int32_t arg1, int32_t arg2, int32_t expected) { + wasm_val_t vs[2] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) }; + wasm_val_t r[1] = { WASM_INIT_VAL }; + wasm_val_vec_t args = WASM_ARRAY_VEC(vs); + wasm_val_vec_t results = WASM_ARRAY_VEC(r); + if (wasm_func_call(func, &args, &results) || r[0].of.i32 != expected) { + printf("> Error on result\n"); + exit(1); + } +} + +void check_trap(wasm_func_t* func, int32_t arg1, int32_t arg2) { + wasm_val_t vs[2] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) }; + wasm_val_t r[1] = { WASM_INIT_VAL }; + wasm_val_vec_t args = WASM_ARRAY_VEC(vs); + wasm_val_vec_t results = WASM_ARRAY_VEC(r); + own wasm_trap_t* trap = wasm_func_call(func, &args, &results); + if (! trap) { + printf("> Error on result, expected trap\n"); + exit(1); + } + wasm_trap_delete(trap); +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); + FILE* file = fopen("table.wasm", "rb"); + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_vec_t imports = WASM_EMPTY_VEC; + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + // Extract export. + printf("Extracting exports...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + size_t i = 0; + wasm_table_t* table = get_export_table(&exports, i++); + wasm_func_t* call_indirect = get_export_func(&exports, i++); + wasm_func_t* f = get_export_func(&exports, i++); + wasm_func_t* g = get_export_func(&exports, i++); + + wasm_module_delete(module); + + // Create external function. + printf("Creating callback...\n"); + own wasm_functype_t* neg_type = wasm_functype_new_1_1(wasm_valtype_new_i32(), wasm_valtype_new_i32()); + own wasm_func_t* h = wasm_func_new(store, neg_type, neg_callback); + + wasm_functype_delete(neg_type); + + // Try cloning. + own wasm_table_t* copy = wasm_table_copy(table); + assert(wasm_table_same(table, copy)); + wasm_table_delete(copy); + + // Check initial table. + printf("Checking table...\n"); + check(wasm_table_size(table) == 2); + check_table(table, 0, false); + check_table(table, 1, true); + check_trap(call_indirect, 0, 0); + check_call(call_indirect, 7, 1, 7); + check_trap(call_indirect, 0, 2); + + // Mutate table. + printf("Mutating table...\n"); + check(wasm_table_set(table, 0, wasm_func_as_ref(g))); + check(wasm_table_set(table, 1, NULL)); + check(! wasm_table_set(table, 2, wasm_func_as_ref(f))); + check_table(table, 0, true); + check_table(table, 1, false); + check_call(call_indirect, 7, 0, 666); + check_trap(call_indirect, 0, 1); + check_trap(call_indirect, 0, 2); + + // Grow table. + printf("Growing table...\n"); + check(wasm_table_grow(table, 3, NULL)); + check(wasm_table_size(table) == 5); + check(wasm_table_set(table, 2, wasm_func_as_ref(f))); + check(wasm_table_set(table, 3, wasm_func_as_ref(h))); + check(! wasm_table_set(table, 5, NULL)); + check_table(table, 2, true); + check_table(table, 3, true); + check_table(table, 4, false); + check_call(call_indirect, 5, 2, 5); + check_call(call_indirect, 6, 3, -6); + check_trap(call_indirect, 0, 4); + check_trap(call_indirect, 0, 5); + + check(wasm_table_grow(table, 2, wasm_func_as_ref(f))); + check(wasm_table_size(table) == 7); + check_table(table, 5, true); + check_table(table, 6, true); + + check(! wasm_table_grow(table, 5, NULL)); + check(wasm_table_grow(table, 3, NULL)); + check(wasm_table_grow(table, 0, NULL)); + + wasm_func_delete(h); + wasm_extern_vec_delete(&exports); + wasm_instance_delete(instance); + + // Create stand-alone table. + // TODO(wasm+): Once Wasm allows multiple tables, turn this into import. + printf("Creating stand-alone table...\n"); + wasm_limits_t limits = {5, 5}; + own wasm_tabletype_t* tabletype = + wasm_tabletype_new(wasm_valtype_new(WASM_FUNCREF), &limits); + own wasm_table_t* table2 = wasm_table_new(store, tabletype, NULL); + check(wasm_table_size(table2) == 5); + check(! wasm_table_grow(table2, 1, NULL)); + check(wasm_table_grow(table2, 0, NULL)); + + wasm_tabletype_delete(tabletype); + wasm_table_delete(table2); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/table.cc b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/table.cc new file mode 100644 index 0000000..9ddf1d9 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/table.cc @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#include + +#include "wasm.hh" + + +// A function to be called from Wasm code. +auto neg_callback( + const wasm::vec& args, wasm::vec& results +) -> wasm::own { + std::cout << "Calling back..." << std::endl; + results[0] = wasm::Val(-args[0].i32()); + return nullptr; +} + + +auto get_export_table(wasm::ownvec& exports, size_t i) -> wasm::Table* { + if (exports.size() <= i || !exports[i]->table()) { + std::cout << "> Error accessing table export " << i << "!" << std::endl; + exit(1); + } + return exports[i]->table(); +} + +auto get_export_func(const wasm::ownvec& exports, size_t i) -> const wasm::Func* { + if (exports.size() <= i || !exports[i]->func()) { + std::cout << "> Error accessing function export " << i << "!" << std::endl; + exit(1); + } + return exports[i]->func(); +} + +template +void check(T actual, U expected) { + if (actual != expected) { + std::cout << "> Error on result, expected " << expected << ", got " << actual << std::endl; + exit(1); + } +} + +void check(bool success) { + if (! success) { + std::cout << "> Error, expected success" << std::endl; + exit(1); + } +} + +auto call( + const wasm::Func* func, wasm::Val&& arg1, wasm::Val&& arg2 +) -> wasm::Val { + auto args = wasm::vec::make(std::move(arg1), std::move(arg2)); + auto results = wasm::vec::make_uninitialized(1); + if (func->call(args, results)) { + std::cout << "> Error on result, expected return" << std::endl; + exit(1); + } + return results[0].copy(); +} + +void check_trap(const wasm::Func* func, wasm::Val&& arg1, wasm::Val&& arg2) { + auto args = wasm::vec::make(std::move(arg1), std::move(arg2)); + auto results = wasm::vec::make_uninitialized(1); + if (! func->call(args, results)) { + std::cout << "> Error on result, expected trap" << std::endl; + exit(1); + } +} + +void run() { + // Initialize. + std::cout << "Initializing..." << std::endl; + auto engine = wasm::Engine::make(); + auto store_ = wasm::Store::make(engine.get()); + auto store = store_.get(); + + // Load binary. + std::cout << "Loading binary..." << std::endl; + std::ifstream file("table.wasm"); + file.seekg(0, std::ios_base::end); + auto file_size = file.tellg(); + file.seekg(0); + auto binary = wasm::vec::make_uninitialized(file_size); + file.read(binary.get(), file_size); + file.close(); + if (file.fail()) { + std::cout << "> Error loading module!" << std::endl; + exit(1); + } + + // Compile. + std::cout << "Compiling module..." << std::endl; + auto module = wasm::Module::make(store, binary); + if (!module) { + std::cout << "> Error compiling module!" << std::endl; + exit(1); + } + + // Instantiate. + std::cout << "Instantiating module..." << std::endl; + auto imports = wasm::vec::make(); + auto instance = wasm::Instance::make(store, module.get(), imports); + if (!instance) { + std::cout << "> Error instantiating module!" << std::endl; + exit(1); + } + + // Extract export. + std::cout << "Extracting exports..." << std::endl; + auto exports = instance->exports(); + size_t i = 0; + auto table = get_export_table(exports, i++); + auto call_indirect = get_export_func(exports, i++); + auto f = get_export_func(exports, i++); + auto g = get_export_func(exports, i++); + + // Create external function. + std::cout << "Creating callback..." << std::endl; + auto neg_type = wasm::FuncType::make( + wasm::ownvec::make(wasm::ValType::make(wasm::ValKind::I32)), + wasm::ownvec::make(wasm::ValType::make(wasm::ValKind::I32)) + ); + auto h = wasm::Func::make(store, neg_type.get(), neg_callback); + + // Try cloning. + assert(table->copy()->same(table)); + + // Check initial table. + std::cout << "Checking table..." << std::endl; + check(table->size(), 2u); + check(table->get(0) == nullptr); + check(table->get(1) != nullptr); + check_trap(call_indirect, wasm::Val::i32(0), wasm::Val::i32(0)); + check(call(call_indirect, wasm::Val::i32(7), wasm::Val::i32(1)).i32(), 7); + check_trap(call_indirect, wasm::Val::i32(0), wasm::Val::i32(2)); + + // Mutate table. + std::cout << "Mutating table..." << std::endl; + check(table->set(0, g)); + check(table->set(1, nullptr)); + check(! table->set(2, f)); + check(table->get(0) != nullptr); + check(table->get(1) == nullptr); + check(call(call_indirect, wasm::Val::i32(7), wasm::Val::i32(0)).i32(), 666); + check_trap(call_indirect, wasm::Val::i32(0), wasm::Val::i32(1)); + check_trap(call_indirect, wasm::Val::i32(0), wasm::Val::i32(2)); + + // Grow table. + std::cout << "Growing table..." << std::endl; + check(table->grow(3)); + check(table->size(), 5u); + check(table->set(2, f)); + check(table->set(3, h.get())); + check(! table->set(5, nullptr)); + check(table->get(2) != nullptr); + check(table->get(3) != nullptr); + check(table->get(4) == nullptr); + check(call(call_indirect, wasm::Val::i32(5), wasm::Val::i32(2)).i32(), 5); + check(call(call_indirect, wasm::Val::i32(6), wasm::Val::i32(3)).i32(), -6); + check_trap(call_indirect, wasm::Val::i32(0), wasm::Val::i32(4)); + check_trap(call_indirect, wasm::Val::i32(0), wasm::Val::i32(5)); + + check(table->grow(2, f)); + check(table->size(), 7u); + check(table->get(5) != nullptr); + check(table->get(6) != nullptr); + + check(! table->grow(5)); + check(table->grow(3)); + check(table->grow(0)); + + // Create stand-alone table. + // TODO(wasm+): Once Wasm allows multiple tables, turn this into import. + std::cout << "Creating stand-alone table..." << std::endl; + auto tabletype = wasm::TableType::make( + wasm::ValType::make(wasm::ValKind::FUNCREF), wasm::Limits(5, 5)); + auto table2 = wasm::Table::make(store, tabletype.get()); + check(table2->size() == 5); + check(! table2->grow(1)); + check(table2->grow(0)); + + // Shut down. + std::cout << "Shutting down..." << std::endl; +} + + +int main(int argc, const char* argv[]) { + run(); + std::cout << "Done." << std::endl; + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/table.wasm b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/table.wasm new file mode 100644 index 0000000..cdc0d8c Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/table.wasm differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/table.wat b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/table.wat new file mode 100644 index 0000000..d3e3a94 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/table.wat @@ -0,0 +1,12 @@ +(module + (table (export "table") 2 10 funcref) + + (func (export "call_indirect") (param i32 i32) (result i32) + (call_indirect (param i32) (result i32) (local.get 0) (local.get 1)) + ) + + (func $f (export "f") (param i32) (result i32) (local.get 0)) + (func (export "g") (param i32) (result i32) (i32.const 666)) + + (elem (i32.const 1) $f) +) diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/threads.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/threads.c new file mode 100644 index 0000000..d70bd1a --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/threads.c @@ -0,0 +1,155 @@ +#include +#include +#include +#include +#include +#include + +#include "wasm.h" + +#define own + +const int N_THREADS = 10; +const int N_REPS = 3; + +// A function to be called from Wasm code. +own wasm_trap_t* callback(const wasm_val_vec_t* args, wasm_val_vec_t* results) { + assert(args->data[0].kind == WASM_I32); + printf("> Thread %d running\n", args->data[0].of.i32); + return NULL; +} + + +typedef struct { + wasm_engine_t* engine; + wasm_shared_module_t* module; + int id; +} thread_args; + +void* run(void* args_abs) { + thread_args* args = (thread_args*)args_abs; + + // Rereate store and module. + own wasm_store_t* store = wasm_store_new(args->engine); + own wasm_module_t* module = wasm_module_obtain(store, args->module); + + // Run the example N times. + for (int i = 0; i < N_REPS; ++i) { + usleep(100000); + + // Create imports. + own wasm_functype_t* func_type = wasm_functype_new_1_0(wasm_valtype_new_i32()); + own wasm_func_t* func = wasm_func_new(store, func_type, callback); + wasm_functype_delete(func_type); + + wasm_val_t val = WASM_I32_VAL((int32_t)args->id); + own wasm_globaltype_t* global_type = + wasm_globaltype_new(wasm_valtype_new_i32(), WASM_CONST); + own wasm_global_t* global = wasm_global_new(store, global_type, &val); + wasm_globaltype_delete(global_type); + + // Instantiate. + wasm_extern_t* externs[] = { + wasm_func_as_extern(func), wasm_global_as_extern(global), + }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return NULL; + } + + wasm_func_delete(func); + wasm_global_delete(global); + + // Extract export. + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return NULL; + } + const wasm_func_t *run_func = wasm_extern_as_func(exports.data[0]); + if (run_func == NULL) { + printf("> Error accessing export!\n"); + return NULL; + } + + wasm_instance_delete(instance); + + // Call. + wasm_val_vec_t empty = WASM_EMPTY_VEC; + if (wasm_func_call(run_func, &empty, &empty)) { + printf("> Error calling function!\n"); + return NULL; + } + + wasm_extern_vec_delete(&exports); + } + + wasm_module_delete(module); + wasm_store_delete(store); + + free(args_abs); + + return NULL; +} + +int main(int argc, const char *argv[]) { + // Initialize. + wasm_engine_t* engine = wasm_engine_new(); + + // Load binary. + FILE* file = fopen("threads.wasm", "rb"); + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Compile and share. + own wasm_store_t* store = wasm_store_new(engine); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + own wasm_shared_module_t* shared = wasm_module_share(module); + + wasm_module_delete(module); + wasm_store_delete(store); + + // Spawn threads. + pthread_t threads[N_THREADS]; + for (int i = 0; i < N_THREADS; i++) { + thread_args* args = malloc(sizeof(thread_args)); + args->id = i; + args->engine = engine; + args->module = shared; + printf("Initializing thread %d...\n", i); + pthread_create(&threads[i], NULL, &run, args); + } + + for (int i = 0; i < N_THREADS; i++) { + printf("Waiting for thread: %d\n", i); + pthread_join(threads[i], NULL); + } + + wasm_shared_module_delete(shared); + wasm_engine_delete(engine); + + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/threads.cc b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/threads.cc new file mode 100644 index 0000000..e130717 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/threads.cc @@ -0,0 +1,125 @@ +#include +#include +#include +#include + +#include "wasm.hh" + +const int N_THREADS = 10; +const int N_REPS = 3; + +// A function to be called from Wasm code. +auto callback( + void* env, const wasm::vec& args, wasm::vec& results +) -> wasm::own { + assert(args[0].kind() == wasm::ValKind::I32); + std::lock_guard lock(*reinterpret_cast(env)); + std::cout << "Thread " << args[0].i32() << " running..." << std::endl; + std::cout.flush(); + return nullptr; +} + + +void run( + wasm::Engine* engine, const wasm::Shared* shared, + std::mutex* mutex, int id +) { + // Create store. + auto store_ = wasm::Store::make(engine); + auto store = store_.get(); + + // Obtain. + auto module = wasm::Module::obtain(store, shared); + if (!module) { + std::lock_guard lock(*mutex); + std::cout << "> Error compiling module!" << std::endl; + exit(1); + } + + // Run the example N times. + for (int i = 0; i < N_REPS; ++i) { + std::this_thread::sleep_for(std::chrono::nanoseconds(100000)); + + // Create imports. + auto func_type = wasm::FuncType::make( + wasm::ownvec::make(wasm::ValType::make(wasm::ValKind::I32)), + wasm::ownvec::make() + ); + auto func = wasm::Func::make(store, func_type.get(), callback, mutex); + + auto global_type = wasm::GlobalType::make( + wasm::ValType::make(wasm::ValKind::I32), wasm::Mutability::CONST); + auto global = wasm::Global::make( + store, global_type.get(), wasm::Val::i32(i)); + + // Instantiate. + auto imports = wasm::vec::make(func.get(), global.get()); + auto instance = wasm::Instance::make(store, module.get(), imports); + if (!instance) { + std::lock_guard lock(*mutex); + std::cout << "> Error instantiating module!" << std::endl; + exit(1); + } + + // Extract export. + auto exports = instance->exports(); + if (exports.size() == 0 || exports[0]->kind() != wasm::ExternKind::FUNC || !exports[0]->func()) { + std::lock_guard lock(*mutex); + std::cout << "> Error accessing export!" << std::endl; + exit(1); + } + auto run_func = exports[0]->func(); + + // Call. + auto empty = wasm::vec::make(); + run_func->call(empty, empty); + } +} + +int main(int argc, const char *argv[]) { + // Initialize. + std::cout << "Initializing..." << std::endl; + auto engine = wasm::Engine::make(); + + // Load binary. + std::cout << "Loading binary..." << std::endl; + std::ifstream file("threads.wasm"); + file.seekg(0, std::ios_base::end); + auto file_size = file.tellg(); + file.seekg(0); + auto binary = wasm::vec::make_uninitialized(file_size); + file.read(binary.get(), file_size); + file.close(); + if (file.fail()) { + std::cout << "> Error loading module!" << std::endl; + return 1; + } + + // Compile and share. + std::cout << "Compiling and sharing module..." << std::endl; + auto store = wasm::Store::make(engine.get()); + auto module = wasm::Module::make(store.get(), binary); + auto shared = module->share(); + + // Spawn threads. + std::cout << "Spawning threads..." << std::endl; + std::mutex mutex; + std::thread threads[N_THREADS]; + for (int i = 0; i < N_THREADS; ++i) { + { + std::lock_guard lock(mutex); + std::cout << "Initializing thread " << i << "..." << std::endl; + } + threads[i] = std::thread(run, engine.get(), shared.get(), &mutex, i); + } + + for (int i = 0; i < N_THREADS; ++i) { + { + std::lock_guard lock(mutex); + std::cout << "Waiting for thread " << i << "..." << std::endl; + } + threads[i].join(); + } + + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/threads.wasm b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/threads.wasm new file mode 100644 index 0000000..9a5c19d Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/threads.wasm differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/threads.wat b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/threads.wat new file mode 100644 index 0000000..29a3bbc --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/threads.wat @@ -0,0 +1,5 @@ +(module + (func $message (import "" "hello") (param i32)) + (global $id (import "" "id") i32) + (func (export "run") (call $message (global.get $id))) +) diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/trap.c b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/trap.c new file mode 100644 index 0000000..e53e29b --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/trap.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include + +#include "wasm.h" + +#define own + +// A function to be called from Wasm code. +own wasm_trap_t* fail_callback( + void* env, const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + printf("Calling back...\n"); + own wasm_name_t message; + wasm_name_new_from_string_nt(&message, "callback abort"); + own wasm_trap_t* trap = wasm_trap_new((wasm_store_t*)env, &message); + wasm_name_delete(&message); + return trap; +} + + +void print_frame(wasm_frame_t* frame) { + printf("> %p @ 0x%zx = %"PRIu32".0x%zx\n", + wasm_frame_instance(frame), + wasm_frame_module_offset(frame), + wasm_frame_func_index(frame), + wasm_frame_func_offset(frame) + ); +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); + FILE* file = fopen("trap.wasm", "rb"); + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Create external print functions. + printf("Creating callback...\n"); + own wasm_functype_t* fail_type = + wasm_functype_new_0_1(wasm_valtype_new_i32()); + own wasm_func_t* fail_func = + wasm_func_new_with_env(store, fail_type, fail_callback, store, NULL); + + if (!fail_func) { + printf("> Error compiling fail_func!\n"); + return 1; + } + + wasm_functype_delete(fail_type); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_t* externs[] = { wasm_func_as_extern(fail_func) }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + wasm_func_delete(fail_func); + + // Extract export. + printf("Extracting exports...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size < 2) { + printf("> Error accessing exports!\n"); + return 1; + } + + wasm_module_delete(module); + wasm_instance_delete(instance); + + // Call. + for (int i = 0; i < 2; ++i) { + const wasm_func_t* func = wasm_extern_as_func(exports.data[i]); + if (func == NULL) { + printf("> Error accessing export!\n"); + return 1; + } + + printf("Calling export %d...\n", i); + wasm_val_vec_t args = WASM_EMPTY_VEC; + wasm_val_vec_t results = WASM_EMPTY_VEC; + own wasm_trap_t* trap = wasm_func_call(func, &args, &results); + if (!trap) { + printf("> Error calling function, expected trap!\n"); + return 1; + } + + printf("Printing message...\n"); + own wasm_name_t message; + wasm_trap_message(trap, &message); + printf("> %s\n", message.data); + + printf("Printing origin...\n"); + own wasm_frame_t* frame = wasm_trap_origin(trap); + if (frame) { + print_frame(frame); + wasm_frame_delete(frame); + } else { + printf("> Empty origin.\n"); + } + + printf("Printing trace...\n"); + own wasm_frame_vec_t trace; + wasm_trap_trace(trap, &trace); + if (trace.size > 0) { + for (size_t i = 0; i < trace.size; ++i) { + print_frame(trace.data[i]); + } + } else { + printf("> Empty trace.\n"); + } + + wasm_frame_vec_delete(&trace); + wasm_trap_delete(trap); + wasm_name_delete(&message); + } + + wasm_extern_vec_delete(&exports); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/trap.cc b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/trap.cc new file mode 100644 index 0000000..2e70c76 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/trap.cc @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include + +#include "wasm.hh" + +// A function to be called from Wasm code. +auto fail_callback( + void* env, const wasm::vec& args, wasm::vec& results +) -> wasm::own { + std::cout << "Calling back..." << std::endl; + auto store = reinterpret_cast(env); + auto message = wasm::Name::make_nt(std::string("callback abort")); + return wasm::Trap::make(store, message); +} + + +void print_frame(const wasm::Frame* frame) { + std::cout << "> " << frame->instance(); + std::cout << " @ 0x" << std::hex << frame->module_offset(); + std::cout << " = " << frame->func_index(); + std::cout << ".0x" << std::hex << frame->func_offset() << std::endl; +} + + +void run() { + // Initialize. + std::cout << "Initializing..." << std::endl; + auto engine = wasm::Engine::make(); + auto store_ = wasm::Store::make(engine.get()); + auto store = store_.get(); + + // Load binary. + std::cout << "Loading binary..." << std::endl; + std::ifstream file("trap.wasm"); + file.seekg(0, std::ios_base::end); + auto file_size = file.tellg(); + file.seekg(0); + auto binary = wasm::vec::make_uninitialized(file_size); + file.read(binary.get(), file_size); + file.close(); + if (file.fail()) { + std::cout << "> Error loading module!" << std::endl; + exit(1); + } + + // Compile. + std::cout << "Compiling module..." << std::endl; + auto module = wasm::Module::make(store, binary); + if (!module) { + std::cout << "> Error compiling module!" << std::endl; + exit(1); + } + + // Create external print functions. + std::cout << "Creating callback..." << std::endl; + auto fail_type = wasm::FuncType::make( + wasm::ownvec::make(), + wasm::ownvec::make(wasm::ValType::make(wasm::ValKind::I32)) + ); + auto fail_func = + wasm::Func::make(store, fail_type.get(), fail_callback, store); + + // Instantiate. + std::cout << "Instantiating module..." << std::endl; + auto imports = wasm::vec::make(fail_func.get()); + auto instance = wasm::Instance::make(store, module.get(), imports); + if (!instance) { + std::cout << "> Error instantiating module!" << std::endl; + exit(1); + } + + // Extract export. + std::cout << "Extracting exports..." << std::endl; + auto exports = instance->exports(); + if (exports.size() < 2 || + exports[0]->kind() != wasm::ExternKind::FUNC || !exports[0]->func() || + exports[1]->kind() != wasm::ExternKind::FUNC || !exports[1]->func()) { + std::cout << "> Error accessing exports!" << std::endl; + exit(1); + } + + // Call. + for (size_t i = 0; i < 2; ++i) { + std::cout << "Calling export " << i << "..." << std::endl; + auto args = wasm::vec::make(); + auto results = wasm::vec::make(); + auto trap = exports[i]->func()->call(args, results); + if (!trap) { + std::cout << "> Error calling function, expected trap!" << std::endl; + exit(1); + } + + std::cout << "Printing message..." << std::endl; + std::cout << "> " << trap->message().get() << std::endl; + + std::cout << "Printing origin..." << std::endl; + auto frame = trap->origin(); + if (frame) { + print_frame(frame.get()); + } else { + std::cout << "> Empty origin." << std::endl; + } + + std::cout << "Printing trace..." << std::endl; + auto trace = trap->trace(); + if (trace.size() > 0) { + for (size_t i = 0; i < trace.size(); ++i) { + print_frame(trace[i].get()); + } + } else { + std::cout << "> Empty trace." << std::endl; + } + } + + // Shut down. + std::cout << "Shutting down..." << std::endl; +} + + +int main(int argc, const char* argv[]) { + run(); + std::cout << "Done." << std::endl; + return 0; +} + diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/trap.wasm b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/trap.wasm new file mode 100644 index 0000000..eeed14c Binary files /dev/null and b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/trap.wasm differ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/trap.wat b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/trap.wat new file mode 100644 index 0000000..dfd20fb --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/example/trap.wat @@ -0,0 +1,5 @@ +(module + (func $callback (import "" "callback") (result i32)) + (func (export "callback") (result i32) (call $callback)) + (func (export "unreachable") (result i32) (unreachable) (i32.const 1)) +) diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/include/wasm.h b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/include/wasm.h new file mode 100644 index 0000000..7b38fe3 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/include/wasm.h @@ -0,0 +1,727 @@ +// WebAssembly C API + +#ifndef WASM_H +#define WASM_H + +#include +#include +#include +#include +#include + +#ifndef WASM_API_EXTERN +#if defined(_WIN32) && !defined(__MINGW32__) && !defined(LIBWASM_STATIC) +#define WASM_API_EXTERN __declspec(dllimport) +#else +#define WASM_API_EXTERN +#endif +#endif + +#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; \ + \ + WASM_API_EXTERN 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; \ + \ + WASM_API_EXTERN void wasm_##name##_vec_new_empty(own wasm_##name##_vec_t* out); \ + WASM_API_EXTERN void wasm_##name##_vec_new_uninitialized( \ + own wasm_##name##_vec_t* out, size_t); \ + WASM_API_EXTERN void wasm_##name##_vec_new( \ + own wasm_##name##_vec_t* out, \ + size_t, own wasm_##name##_t ptr_or_none const[]); \ + WASM_API_EXTERN void wasm_##name##_vec_copy( \ + own wasm_##name##_vec_t* out, const wasm_##name##_vec_t*); \ + WASM_API_EXTERN 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, own const char* s +) { + wasm_name_new(out, strlen(s), s); +} + +static inline void wasm_name_new_from_string_nt( + own wasm_name_t* out, own const char* s +) { + wasm_name_new(out, strlen(s) + 1, s); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Runtime Environment + +// Configuration + +WASM_DECLARE_OWN(config) + +WASM_API_EXTERN own wasm_config_t* wasm_config_new(); + +// Embedders may provide custom functions for manipulating configs. + + +// Engine + +WASM_DECLARE_OWN(engine) + +// During testing, we use a custom implementation of wasm_engine_new +#if defined(TEST_WASM) || defined(TEST_WASMER) +wasm_engine_t* wasm_engine_new(); +#else +WASM_API_EXTERN own wasm_engine_t* wasm_engine_new(); +#endif +WASM_API_EXTERN own wasm_engine_t* wasm_engine_new_with_config(own wasm_config_t*); + + +// Store + +WASM_DECLARE_OWN(store) + +WASM_API_EXTERN 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, *) \ + \ + WASM_API_EXTERN 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, +}; + +WASM_API_EXTERN own wasm_valtype_t* wasm_valtype_new(wasm_valkind_t); + +WASM_API_EXTERN 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) + +WASM_API_EXTERN own wasm_functype_t* wasm_functype_new( + own wasm_valtype_vec_t* params, own wasm_valtype_vec_t* results); + +WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_params(const wasm_functype_t*); +WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_results(const wasm_functype_t*); + + +// Global Types + +WASM_DECLARE_TYPE(globaltype) + +WASM_API_EXTERN own wasm_globaltype_t* wasm_globaltype_new( + own wasm_valtype_t*, wasm_mutability_t); + +WASM_API_EXTERN const wasm_valtype_t* wasm_globaltype_content(const wasm_globaltype_t*); +WASM_API_EXTERN wasm_mutability_t wasm_globaltype_mutability(const wasm_globaltype_t*); + + +// Table Types + +WASM_DECLARE_TYPE(tabletype) + +WASM_API_EXTERN own wasm_tabletype_t* wasm_tabletype_new( + own wasm_valtype_t*, const wasm_limits_t*); + +WASM_API_EXTERN const wasm_valtype_t* wasm_tabletype_element(const wasm_tabletype_t*); +WASM_API_EXTERN const wasm_limits_t* wasm_tabletype_limits(const wasm_tabletype_t*); + + +// Memory Types + +WASM_DECLARE_TYPE(memorytype) + +WASM_API_EXTERN own wasm_memorytype_t* wasm_memorytype_new(const wasm_limits_t*); + +WASM_API_EXTERN 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_API_EXTERN wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t*); + +WASM_API_EXTERN wasm_externtype_t* wasm_functype_as_externtype(wasm_functype_t*); +WASM_API_EXTERN wasm_externtype_t* wasm_globaltype_as_externtype(wasm_globaltype_t*); +WASM_API_EXTERN wasm_externtype_t* wasm_tabletype_as_externtype(wasm_tabletype_t*); +WASM_API_EXTERN wasm_externtype_t* wasm_memorytype_as_externtype(wasm_memorytype_t*); + +WASM_API_EXTERN wasm_functype_t* wasm_externtype_as_functype(wasm_externtype_t*); +WASM_API_EXTERN wasm_globaltype_t* wasm_externtype_as_globaltype(wasm_externtype_t*); +WASM_API_EXTERN wasm_tabletype_t* wasm_externtype_as_tabletype(wasm_externtype_t*); +WASM_API_EXTERN wasm_memorytype_t* wasm_externtype_as_memorytype(wasm_externtype_t*); + +WASM_API_EXTERN const wasm_externtype_t* wasm_functype_as_externtype_const(const wasm_functype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_globaltype_as_externtype_const(const wasm_globaltype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_tabletype_as_externtype_const(const wasm_tabletype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_memorytype_as_externtype_const(const wasm_memorytype_t*); + +WASM_API_EXTERN const wasm_functype_t* wasm_externtype_as_functype_const(const wasm_externtype_t*); +WASM_API_EXTERN const wasm_globaltype_t* wasm_externtype_as_globaltype_const(const wasm_externtype_t*); +WASM_API_EXTERN const wasm_tabletype_t* wasm_externtype_as_tabletype_const(const wasm_externtype_t*); +WASM_API_EXTERN const wasm_memorytype_t* wasm_externtype_as_memorytype_const(const wasm_externtype_t*); + + +// Import Types + +WASM_DECLARE_TYPE(importtype) + +WASM_API_EXTERN own wasm_importtype_t* wasm_importtype_new( + own wasm_name_t* module, own wasm_name_t* name, own wasm_externtype_t*); + +WASM_API_EXTERN const wasm_name_t* wasm_importtype_module(const wasm_importtype_t*); +WASM_API_EXTERN const wasm_name_t* wasm_importtype_name(const wasm_importtype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_importtype_type(const wasm_importtype_t*); + + +// Export Types + +WASM_DECLARE_TYPE(exporttype) + +WASM_API_EXTERN own wasm_exporttype_t* wasm_exporttype_new( + own wasm_name_t*, own wasm_externtype_t*); + +WASM_API_EXTERN const wasm_name_t* wasm_exporttype_name(const wasm_exporttype_t*); +WASM_API_EXTERN 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; + +WASM_API_EXTERN void wasm_val_delete(own wasm_val_t* v); +WASM_API_EXTERN 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) \ + \ + WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t*); \ + WASM_API_EXTERN bool wasm_##name##_same(const wasm_##name##_t*, const wasm_##name##_t*); \ + \ + WASM_API_EXTERN void* wasm_##name##_get_host_info(const wasm_##name##_t*); \ + WASM_API_EXTERN void wasm_##name##_set_host_info(wasm_##name##_t*, void*); \ + WASM_API_EXTERN 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_API_EXTERN wasm_ref_t* wasm_##name##_as_ref(wasm_##name##_t*); \ + WASM_API_EXTERN wasm_##name##_t* wasm_ref_as_##name(wasm_ref_t*); \ + WASM_API_EXTERN const wasm_ref_t* wasm_##name##_as_ref_const(const wasm_##name##_t*); \ + WASM_API_EXTERN 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) \ + \ + WASM_API_EXTERN own wasm_shared_##name##_t* wasm_##name##_share(const wasm_##name##_t*); \ + WASM_API_EXTERN 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, *) +WASM_API_EXTERN own wasm_frame_t* wasm_frame_copy(const wasm_frame_t*); + +WASM_API_EXTERN struct wasm_instance_t* wasm_frame_instance(const wasm_frame_t*); +WASM_API_EXTERN uint32_t wasm_frame_func_index(const wasm_frame_t*); +WASM_API_EXTERN size_t wasm_frame_func_offset(const wasm_frame_t*); +WASM_API_EXTERN size_t wasm_frame_module_offset(const wasm_frame_t*); + + +// Traps + +typedef wasm_name_t wasm_message_t; // null terminated + +WASM_DECLARE_REF(trap) + +WASM_API_EXTERN own wasm_trap_t* wasm_trap_new(wasm_store_t* store, const wasm_message_t*); + +WASM_API_EXTERN void wasm_trap_message(const wasm_trap_t*, own wasm_message_t* out); +WASM_API_EXTERN own wasm_frame_t* wasm_trap_origin(const wasm_trap_t*); +WASM_API_EXTERN void wasm_trap_trace(const wasm_trap_t*, own wasm_frame_vec_t* out); + + +// Foreign Objects + +WASM_DECLARE_REF(foreign) + +WASM_API_EXTERN own wasm_foreign_t* wasm_foreign_new(wasm_store_t*); + + +// Modules + +WASM_DECLARE_SHARABLE_REF(module) + +WASM_API_EXTERN own wasm_module_t* wasm_module_new( + wasm_store_t*, const wasm_byte_vec_t* binary); + +WASM_API_EXTERN bool wasm_module_validate(wasm_store_t*, const wasm_byte_vec_t* binary); + +WASM_API_EXTERN void wasm_module_imports(const wasm_module_t*, own wasm_importtype_vec_t* out); +WASM_API_EXTERN void wasm_module_exports(const wasm_module_t*, own wasm_exporttype_vec_t* out); + +WASM_API_EXTERN void wasm_module_serialize(const wasm_module_t*, own wasm_byte_vec_t* out); +WASM_API_EXTERN 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_vec_t* args, own wasm_val_vec_t* results); +typedef own wasm_trap_t* (*wasm_func_callback_with_env_t)( + void* env, const wasm_val_vec_t* args, wasm_val_vec_t* results); + +WASM_API_EXTERN own wasm_func_t* wasm_func_new( + wasm_store_t*, const wasm_functype_t*, wasm_func_callback_t); +WASM_API_EXTERN 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*)); + +WASM_API_EXTERN own wasm_functype_t* wasm_func_type(const wasm_func_t*); +WASM_API_EXTERN size_t wasm_func_param_arity(const wasm_func_t*); +WASM_API_EXTERN size_t wasm_func_result_arity(const wasm_func_t*); + +WASM_API_EXTERN own wasm_trap_t* wasm_func_call( + const wasm_func_t*, const wasm_val_vec_t* args, wasm_val_vec_t* results); + + +// Global Instances + +WASM_DECLARE_REF(global) + +WASM_API_EXTERN own wasm_global_t* wasm_global_new( + wasm_store_t*, const wasm_globaltype_t*, const wasm_val_t*); + +WASM_API_EXTERN own wasm_globaltype_t* wasm_global_type(const wasm_global_t*); + +WASM_API_EXTERN void wasm_global_get(const wasm_global_t*, own wasm_val_t* out); +WASM_API_EXTERN void wasm_global_set(wasm_global_t*, const wasm_val_t*); + + +// Table Instances + +WASM_DECLARE_REF(table) + +typedef uint32_t wasm_table_size_t; + +WASM_API_EXTERN own wasm_table_t* wasm_table_new( + wasm_store_t*, const wasm_tabletype_t*, wasm_ref_t* init); + +WASM_API_EXTERN own wasm_tabletype_t* wasm_table_type(const wasm_table_t*); + +WASM_API_EXTERN own wasm_ref_t* wasm_table_get(const wasm_table_t*, wasm_table_size_t index); +WASM_API_EXTERN bool wasm_table_set(wasm_table_t*, wasm_table_size_t index, wasm_ref_t*); + +WASM_API_EXTERN wasm_table_size_t wasm_table_size(const wasm_table_t*); +WASM_API_EXTERN 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; + +WASM_API_EXTERN own wasm_memory_t* wasm_memory_new(wasm_store_t*, const wasm_memorytype_t*); + +WASM_API_EXTERN own wasm_memorytype_t* wasm_memory_type(const wasm_memory_t*); + +WASM_API_EXTERN byte_t* wasm_memory_data(wasm_memory_t*); +WASM_API_EXTERN size_t wasm_memory_data_size(const wasm_memory_t*); + +WASM_API_EXTERN wasm_memory_pages_t wasm_memory_size(const wasm_memory_t*); +WASM_API_EXTERN bool wasm_memory_grow(wasm_memory_t*, wasm_memory_pages_t delta); + + +// Externals + +WASM_DECLARE_REF(extern) +WASM_DECLARE_VEC(extern, *) + +WASM_API_EXTERN wasm_externkind_t wasm_extern_kind(const wasm_extern_t*); +WASM_API_EXTERN own wasm_externtype_t* wasm_extern_type(const wasm_extern_t*); + +WASM_API_EXTERN wasm_extern_t* wasm_func_as_extern(wasm_func_t*); +WASM_API_EXTERN wasm_extern_t* wasm_global_as_extern(wasm_global_t*); +WASM_API_EXTERN wasm_extern_t* wasm_table_as_extern(wasm_table_t*); +WASM_API_EXTERN wasm_extern_t* wasm_memory_as_extern(wasm_memory_t*); + +WASM_API_EXTERN wasm_func_t* wasm_extern_as_func(wasm_extern_t*); +WASM_API_EXTERN wasm_global_t* wasm_extern_as_global(wasm_extern_t*); +WASM_API_EXTERN wasm_table_t* wasm_extern_as_table(wasm_extern_t*); +WASM_API_EXTERN wasm_memory_t* wasm_extern_as_memory(wasm_extern_t*); + +WASM_API_EXTERN const wasm_extern_t* wasm_func_as_extern_const(const wasm_func_t*); +WASM_API_EXTERN const wasm_extern_t* wasm_global_as_extern_const(const wasm_global_t*); +WASM_API_EXTERN const wasm_extern_t* wasm_table_as_extern_const(const wasm_table_t*); +WASM_API_EXTERN const wasm_extern_t* wasm_memory_as_extern_const(const wasm_memory_t*); + +WASM_API_EXTERN const wasm_func_t* wasm_extern_as_func_const(const wasm_extern_t*); +WASM_API_EXTERN const wasm_global_t* wasm_extern_as_global_const(const wasm_extern_t*); +WASM_API_EXTERN const wasm_table_t* wasm_extern_as_table_const(const wasm_extern_t*); +WASM_API_EXTERN const wasm_memory_t* wasm_extern_as_memory_const(const wasm_extern_t*); + + +// Module Instances + +WASM_DECLARE_REF(instance) + +WASM_API_EXTERN own wasm_instance_t* wasm_instance_new( + wasm_store_t*, const wasm_module_t*, const wasm_extern_vec_t* imports, + own wasm_trap_t** +); + +WASM_API_EXTERN void wasm_instance_exports(const wasm_instance_t*, own wasm_extern_vec_t* out); + + +/////////////////////////////////////////////////////////////////////////////// +// Convenience + +// Vectors + +#define WASM_EMPTY_VEC {0, NULL} +#define WASM_ARRAY_VEC(array) {sizeof(array)/sizeof(*(array)), array} + + +// 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 +} + +#define WASM_I32_VAL(i) {.kind = WASM_I32, .of = {.i32 = i}} +#define WASM_I64_VAL(i) {.kind = WASM_I64, .of = {.i64 = i}} +#define WASM_F32_VAL(z) {.kind = WASM_F32, .of = {.f32 = z}} +#define WASM_F64_VAL(z) {.kind = WASM_F64, .of = {.f64 = z}} +#define WASM_REF_VAL(r) {.kind = WASM_ANYREF, .of = {.ref = r}} +#define WASM_INIT_VAL {.kind = WASM_ANYREF, .of = {.ref = NULL}} + + +/////////////////////////////////////////////////////////////////////////////// + +#undef own + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // #ifdef WASM_H diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/include/wasm.hh b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/include/wasm.hh new file mode 100644 index 0000000..8a45099 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/include/wasm.hh @@ -0,0 +1,752 @@ +// WebAssembly C++ API + +#ifndef WASM_HH +#define WASM_HH + +#include +#include +#include +#include +#include +#include +#include + +#ifndef WASM_API_EXTERN +#if defined(_WIN32) && !defined(__MINGW32__) && !defined(LIBWASM_STATIC) +#define WASM_API_EXTERN __declspec(dllimport) +#else +#define WASM_API_EXTERN +#endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// Auxiliaries + +// Machine types + +static_assert(sizeof(float) == sizeof(int32_t), "incompatible float type"); +static_assert(sizeof(double) == sizeof(int64_t), "incompatible double type"); +static_assert(sizeof(intptr_t) == sizeof(int32_t) || + sizeof(intptr_t) == sizeof(int64_t), "incompatible pointer type"); + +using byte_t = char; +using float32_t = float; +using float64_t = double; + + +namespace wasm { + +// Vectors + +template +class vec { + static const size_t invalid_size = SIZE_MAX; + + size_t size_; + std::unique_ptr data_; + +#ifdef WASM_API_DEBUG + WASM_API_EXTERN void make_data(); + WASM_API_EXTERN void free_data(); +#else + void make_data() {} + void free_data() {} +#endif + + vec(size_t size) : vec(size, size ? new(std::nothrow) T[size] : nullptr) { + make_data(); + } + + vec(size_t size, T* data) : size_(size), data_(data) { + assert(!!size_ == !!data_ || size_ == invalid_size); + } + +public: + using elem_type = T; + + vec(vec&& that) : vec(that.size_, that.data_.release()) { + that.size_ = invalid_size; + } + + ~vec() { + free_data(); + } + + operator bool() const { + return bool(size_ != invalid_size); + } + + auto size() const -> size_t { + return size_; + } + + auto get() const -> const T* { + return data_.get(); + } + + auto get() -> T* { + return data_.get(); + } + + auto release() -> T* { + size_ = invalid_size; + return data_.release(); + } + + void reset() { + free_data(); + size_ = invalid_size; + data_.reset(); + } + + void reset(vec& that) { + free_data(); + size_ = that.size_; + data_.reset(that.data_.release()); + that.size_ = invalid_size; + } + + auto operator=(vec&& that) -> vec& { + reset(that); + return *this; + } + + auto operator[](size_t i) -> T& { + assert(i < size_); + return data_[i]; + } + + auto operator[](size_t i) const -> const T& { + assert(i < size_); + return data_[i]; + } + + auto copy() const -> vec { + auto v = vec(size_); + if (v) for (size_t i = 0; i < size_; ++i) v.data_[i] = data_[i]; + return v; + } + + // TODO: This can't be used for e.g. vec + auto deep_copy() const -> vec { + auto v = vec(size_); + if (v) for (size_t i = 0; i < size_; ++i) v.data_[i] = data_[i]->copy(); + return v; + } + + static auto make_uninitialized(size_t size = 0) -> vec { + return vec(size); + } + + static auto make(size_t size, T init[]) -> vec { + auto v = vec(size); + if (v) for (size_t i = 0; i < size; ++i) v.data_[i] = std::move(init[i]); + return v; + } + + static auto make(std::string s) -> vec { + auto v = vec(s.length()); + if (v) std::strncpy(v.get(), s.data(), s.length()); + return v; + } + + static auto make_nt(std::string s) -> vec { + auto v = vec(s.length() + 1); + if (v) std::strcpy(v.get(), s.data()); + return v; + } + + // TODO(mvsc): MVSC requires this special case: + static auto make() -> vec { + return vec(0); + } + + template + static auto make(Ts&&... args) -> vec { + T data[] = { std::forward(args)... }; + return make(sizeof...(Ts), data); + } + + static auto adopt(size_t size, T data[]) -> vec { + return vec(size, data); + } + + static auto invalid() -> vec { + return vec(invalid_size, nullptr); + } +}; + + +// Ownership + +template using own = std::unique_ptr; +template using ownvec = vec>; + +template +auto make_own(T* x) -> own { return own(x); } + + +/////////////////////////////////////////////////////////////////////////////// +// Runtime Environment + +// Configuration + +class WASM_API_EXTERN Config { +public: + Config() = delete; + ~Config(); + void operator delete(void*); + + static auto make() -> own; + + // Implementations may provide custom methods for manipulating Configs. +}; + + +// Engine + +class WASM_API_EXTERN Engine { +public: + Engine() = delete; + ~Engine(); + void operator delete(void*); + + static auto make(own&& = Config::make()) -> own; +}; + + +// Store + +class WASM_API_EXTERN Store { +public: + Store() = delete; + ~Store(); + void operator delete(void*); + + static auto make(Engine*) -> own; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Type Representations + +// Type attributes + +enum class Mutability : uint8_t { CONST, VAR }; + +struct Limits { + uint32_t min; + uint32_t max; + + Limits(uint32_t min, uint32_t max = std::numeric_limits::max()) : + min(min), max(max) {} +}; + + +// Value Types + +enum class ValueKind : uint8_t { + I32, I64, F32, F64, + ANYREF = 128, FUNCREF, +}; + +inline bool is_num(ValueKind k) { return k < ValueKind::ANYREF; } +inline bool is_ref(ValueKind k) { return k >= ValueKind::ANYREF; } + + +class WASM_API_EXTERN ValueType { +public: + ValueType() = delete; + ~ValueType(); + void operator delete(void*); + + static auto make(ValueKind) -> own; + auto copy() const -> own; + + auto kind() const -> ValueKind; + auto is_num() const -> bool { return wasm::is_num(kind()); } + auto is_ref() const -> bool { return wasm::is_ref(kind()); } +}; + + +// External Types + +enum class ExternKind : uint8_t { + FUNC, GLOBAL, TABLE, MEMORY +}; + +class FuncType; +class GlobalType; +class TableType; +class MemoryType; + +class WASM_API_EXTERN ExternType { +public: + ExternType() = delete; + ~ExternType(); + void operator delete(void*); + + auto copy() const-> own; + + auto kind() const -> ExternKind; + + auto func() -> FuncType*; + auto global() -> GlobalType*; + auto table() -> TableType*; + auto memory() -> MemoryType*; + + auto func() const -> const FuncType*; + auto global() const -> const GlobalType*; + auto table() const -> const TableType*; + auto memory() const -> const MemoryType*; +}; + + +// Function Types + +class WASM_API_EXTERN FuncType : public ExternType { +public: + FuncType() = delete; + ~FuncType(); + + static auto make( + ownvec&& params = ownvec::make(), + ownvec&& results = ownvec::make() + ) -> own; + + auto copy() const -> own; + + auto params() const -> const ownvec&; + auto results() const -> const ownvec&; +}; + + +// Global Types + +class WASM_API_EXTERN GlobalType : public ExternType { +public: + GlobalType() = delete; + ~GlobalType(); + + static auto make(own&&, Mutability) -> own; + auto copy() const -> own; + + auto content() const -> const ValueType*; + auto mutability() const -> Mutability; +}; + + +// Table Types + +class WASM_API_EXTERN TableType : public ExternType { +public: + TableType() = delete; + ~TableType(); + + static auto make(own&&, Limits) -> own; + auto copy() const -> own; + + auto element() const -> const ValueType*; + auto limits() const -> const Limits&; +}; + + +// Memory Types + +class WASM_API_EXTERN MemoryType : public ExternType { +public: + MemoryType() = delete; + ~MemoryType(); + + static auto make(Limits) -> own; + auto copy() const -> own; + + auto limits() const -> const Limits&; +}; + + +// Import Types + +using Name = vec; + +class WASM_API_EXTERN ImportType { +public: + ImportType() = delete; + ~ImportType(); + void operator delete(void*); + + static auto make(Name&& module, Name&& name, own&&) -> + own; + auto copy() const -> own; + + auto module() const -> const Name&; + auto name() const -> const Name&; + auto type() const -> const ExternType*; +}; + + +// Export Types + +class WASM_API_EXTERN ExportType { +public: + ExportType() = delete; + ~ExportType(); + void operator delete(void*); + + static auto make(Name&&, own&&) -> own; + auto copy() const -> own; + + auto name() const -> const Name&; + auto type() const -> const ExternType*; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Runtime Objects + +// References + +class WASM_API_EXTERN Ref { +public: + Ref() = delete; + ~Ref(); + void operator delete(void*); + + auto copy() const -> own; + auto same(const Ref*) const -> bool; + + auto get_host_info() const -> void*; + void set_host_info(void* info, void (*finalizer)(void*) = nullptr); +}; + + +// Values + +class Value { + ValueKind kind_; + union impl { + int32_t i32; + int64_t i64; + float32_t f32; + float64_t f64; + Ref* ref; + } impl_; + + Value(ValueKind kind, impl impl) : kind_(kind), impl_(impl) {} + +public: + Value() : kind_(ValueKind::ANYREF) { impl_.ref = nullptr; } + explicit Value(int32_t i) : kind_(ValueKind::I32) { impl_.i32 = i; } + explicit Value(int64_t i) : kind_(ValueKind::I64) { impl_.i64 = i; } + explicit Value(float32_t z) : kind_(ValueKind::F32) { impl_.f32 = z; } + explicit Value(float64_t z) : kind_(ValueKind::F64) { impl_.f64 = z; } + explicit Value(own&& r) : kind_(ValueKind::ANYREF) { impl_.ref = r.release(); } + + Value(Value&& that) : kind_(that.kind_), impl_(that.impl_) { + if (is_ref()) that.impl_.ref = nullptr; + } + + ~Value() { + reset(); + } + + auto is_num() const -> bool { return wasm::is_num(kind_); } + auto is_ref() const -> bool { return wasm::is_ref(kind_); } + + static auto i32(int32_t x) -> Value { return Value(x); } + static auto i64(int64_t x) -> Value { return Value(x); } + static auto f32(float32_t x) -> Value { return Value(x); } + static auto f64(float64_t x) -> Value { return Value(x); } + static auto ref(own&& x) -> Value { return Value(std::move(x)); } + template inline static auto make(T x) -> Value; + template inline static auto make(own&& x) -> Value; + + void reset() { + if (is_ref() && impl_.ref) { + delete impl_.ref; + impl_.ref = nullptr; + } + } + + void reset(Value& that) { + reset(); + kind_ = that.kind_; + impl_ = that.impl_; + if (is_ref()) that.impl_.ref = nullptr; + } + + auto operator=(Value&& that) -> Value& { + reset(that); + return *this; + } + + auto kind() const -> ValueKind { return kind_; } + auto i32() const -> int32_t { assert(kind_ == ValueKind::I32); return impl_.i32; } + auto i64() const -> int64_t { assert(kind_ == ValueKind::I64); return impl_.i64; } + auto f32() const -> float32_t { assert(kind_ == ValueKind::F32); return impl_.f32; } + auto f64() const -> float64_t { assert(kind_ == ValueKind::F64); return impl_.f64; } + auto ref() const -> Ref* { assert(is_ref()); return impl_.ref; } + template inline auto get() const -> T; + + auto release_ref() -> own { + assert(is_ref()); + auto ref = impl_.ref; + impl_.ref = nullptr; + return own(ref); + } + + auto copy() const -> Value { + if (is_ref() && impl_.ref != nullptr) { + // TODO(mvsc): MVSC cannot handle this: + // impl impl = {.ref = impl_.ref->copy().release()}; + impl impl; + impl.ref = impl_.ref->copy().release(); + return Value(kind_, impl); + } else { + return Value(kind_, impl_); + } + } +}; + + +template<> inline auto Value::make(int32_t x) -> Value { return Value(x); } +template<> inline auto Value::make(int64_t x) -> Value { return Value(x); } +template<> inline auto Value::make(float32_t x) -> Value { return Value(x); } +template<> inline auto Value::make(float64_t x) -> Value { return Value(x); } +template<> inline auto Value::make(own&& x) -> Value { + return Value(std::move(x)); +} + +template<> inline auto Value::make(uint32_t x) -> Value { + return Value(static_cast(x)); +} +template<> inline auto Value::make(uint64_t x) -> Value { + return Value(static_cast(x)); +} + +template<> inline auto Value::get() const -> int32_t { return i32(); } +template<> inline auto Value::get() const -> int64_t { return i64(); } +template<> inline auto Value::get() const -> float32_t { return f32(); } +template<> inline auto Value::get() const -> float64_t { return f64(); } +template<> inline auto Value::get() const -> Ref* { return ref(); } + +template<> inline auto Value::get() const -> uint32_t { + return static_cast(i32()); +} +template<> inline auto Value::get() const -> uint64_t { + return static_cast(i64()); +} + + +// Traps + +using Message = vec; // null terminated + +class Instance; + +class WASM_API_EXTERN Frame { +public: + Frame() = delete; + ~Frame(); + void operator delete(void*); + + auto copy() const -> own; + + auto instance() const -> Instance*; + auto func_index() const -> uint32_t; + auto func_offset() const -> size_t; + auto module_offset() const -> size_t; +}; + +class WASM_API_EXTERN Trap : public Ref { +public: + Trap() = delete; + ~Trap(); + + static auto make(Store*, const Message& msg) -> own; + auto copy() const -> own; + + auto message() const -> Message; + auto origin() const -> own; // may be null + auto trace() const -> ownvec; // may be empty, origin first +}; + + +// Shared objects + +template +class WASM_API_EXTERN Shared { +public: + Shared() = delete; + ~Shared(); + void operator delete(void*); +}; + + +// Modules + +class WASM_API_EXTERN Module : public Ref { +public: + Module() = delete; + ~Module(); + + static auto validate(Store*, const vec& binary) -> bool; + static auto make(Store*, const vec& binary) -> own; + auto copy() const -> own; + + auto imports() const -> ownvec; + auto exports() const -> ownvec; + + auto share() const -> own>; + static auto obtain(Store*, const Shared*) -> own; + + auto serialize() const -> vec; + static auto deserialize(Store*, const vec&) -> own; +}; + + +// Foreign Objects + +class WASM_API_EXTERN Foreign : public Ref { +public: + Foreign() = delete; + ~Foreign(); + + static auto make(Store*) -> own; + auto copy() const -> own; +}; + + +// Externals + +class Func; +class Global; +class Table; +class Memory; + +class WASM_API_EXTERN Extern : public Ref { +public: + Extern() = delete; + ~Extern(); + + auto copy() const -> own; + + auto kind() const -> ExternKind; + auto type() const -> own; + + auto func() -> Func*; + auto global() -> Global*; + auto table() -> Table*; + auto memory() -> Memory*; + + auto func() const -> const Func*; + auto global() const -> const Global*; + auto table() const -> const Table*; + auto memory() const -> const Memory*; +}; + + +// Function Instances + +class WASM_API_EXTERN Func : public Extern { +public: + Func() = delete; + ~Func(); + + using callback = auto (*)(const vec&, vec&) -> own; + using callback_with_env = auto (*)(void*, const vec&, vec&) -> own; + + static auto make(Store*, const FuncType*, callback) -> own; + static auto make(Store*, const FuncType*, callback_with_env, + void*, void (*finalizer)(void*) = nullptr) -> own; + auto copy() const -> own; + + auto type() const -> own; + auto param_arity() const -> size_t; + auto result_arity() const -> size_t; + + auto call(const vec&, vec&) const -> own; +}; + + +// Global Instances + +class WASM_API_EXTERN Global : public Extern { +public: + Global() = delete; + ~Global(); + + static auto make(Store*, const GlobalType*, const Value&) -> own; + auto copy() const -> own; + + auto type() const -> own; + auto get() const -> Value; + void set(const Value&); +}; + + +// Table Instances + +class WASM_API_EXTERN Table : public Extern { +public: + Table() = delete; + ~Table(); + + using size_t = uint32_t; + + static auto make( + Store*, const TableType*, const Ref* init = nullptr) -> own

; + auto copy() const -> own
; + + auto type() const -> own; + auto get(size_t index) const -> own; + auto set(size_t index, const Ref*) -> bool; + auto size() const -> size_t; + auto grow(size_t delta, const Ref* init = nullptr) -> bool; +}; + + +// Memory Instances + +class WASM_API_EXTERN Memory : public Extern { +public: + Memory() = delete; + ~Memory(); + + static auto make(Store*, const MemoryType*) -> own; + auto copy() const -> own; + + using pages_t = uint32_t; + + static const size_t page_size = 0x10000; + + auto type() const -> own; + auto data() const -> byte_t*; + auto data_size() const -> size_t; + auto size() const -> pages_t; + auto grow(pages_t delta) -> bool; +}; + + +// Module Instances + +class WASM_API_EXTERN Instance : public Ref { +public: + Instance() = delete; + ~Instance(); + + static auto make( + Store*, const Module*, const vec&, own* = nullptr + ) -> own; + auto copy() const -> own; + + auto exports() const -> ownvec; +}; + + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace wasm + +#endif // #ifdef WASM_HH diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/patch/0001-BUILD.gn-add-wasm-v8-lowlevel.patch b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/patch/0001-BUILD.gn-add-wasm-v8-lowlevel.patch new file mode 100644 index 0000000..fe7b35f --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/patch/0001-BUILD.gn-add-wasm-v8-lowlevel.patch @@ -0,0 +1,28 @@ +diff --git a/BUILD.gn b/BUILD.gn +index deedf82..6dced15 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -1679,6 +1679,7 @@ v8_header_set("v8_headers") { + + sources = [ + "include/v8.h", ++ "include/wasm-v8-lowlevel.hh", + "include/v8config.h", + ] + +@@ -2007,6 +2008,7 @@ v8_source_set("v8_base") { + "include/v8-testing.h", + "include/v8-util.h", + "include/v8.h", ++ "include/wasm-v8-lowlevel.hh", + "include/v8config.h", + "src/accessors.cc", + "src/accessors.h", +@@ -2020,6 +2022,7 @@ v8_source_set("v8_base") { + "src/api/api-natives.h", + "src/api/api.cc", + "src/api/api.h", ++ "src/wasm-v8-lowlevel.cc", + "src/asmjs/asm-js.cc", + "src/asmjs/asm-js.h", + "src/asmjs/asm-names.h", diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-bin.cc b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-bin.cc new file mode 100644 index 0000000..93f0b58 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-bin.cc @@ -0,0 +1,570 @@ +#include "wasm-bin.hh" + +#include + +namespace wasm { +namespace bin { + +//////////////////////////////////////////////////////////////////////////////// +// Encoding + +void encode_header(char*& ptr) { + std::memcpy(ptr, "\x00""asm\x01\x00\x00\x00", 8); + ptr += 8; +} + +auto u64_size(uint64_t n) -> size_t { + bool done = false; + size_t size = 0; + do { + ++size; + done = n <= 0x7f; + n = n >> 7; + } while (!done); + return size; +} + +auto u32_size(uint64_t n) -> size_t { + return u64_size(n); +} + +void encode_u64(char*& ptr, uint64_t n) { + bool done = false; + do { + done = n <= 0x7f; + *ptr++ = (n & 0x7f) | (done ? 0x00 : 0x80); + n = n >> 7; + } while (!done); +} + +void encode_u32(char*& ptr, uint32_t n) { + encode_u64(ptr, n); +} + +void encode_size32(char*& ptr, size_t n) { + assert(n <= 0xffffffff); + for (int i = 0; i < 5; ++i) { + *ptr++ = (n & 0x7f) | (i == 4 ? 0x00 : 0x80); + n = n >> 7; + } +} + + +void encode_valtype(char*& ptr, const ValType* type) { + switch (type->kind()) { + case ValKind::I32: *ptr++ = 0x7f; break; + case ValKind::I64: *ptr++ = 0x7e; break; + case ValKind::F32: *ptr++ = 0x7d; break; + case ValKind::F64: *ptr++ = 0x7c; break; + case ValKind::FUNCREF: *ptr++ = 0x70; break; + case ValKind::ANYREF: *ptr++ = 0x6f; break; + default: assert(false); + } +} + +auto zero_size(const ValType* type) -> size_t { + switch (type->kind()) { + case ValKind::I32: return 1; + case ValKind::I64: return 1; + case ValKind::F32: return 4; + case ValKind::F64: return 8; + case ValKind::FUNCREF: return 0; + case ValKind::ANYREF: return 0; + default: assert(false); + } +} + +void encode_const_zero(char*& ptr, const ValType* type) { + switch (type->kind()) { + case ValKind::I32: *ptr++ = 0x41; break; + case ValKind::I64: *ptr++ = 0x42; break; + case ValKind::F32: *ptr++ = 0x43; break; + case ValKind::F64: *ptr++ = 0x44; break; + case ValKind::FUNCREF: *ptr++ = 0xd0; break; + case ValKind::ANYREF: *ptr++ = 0xd0; break; + default: assert(false); + } + for (int i = 0; i < zero_size(type); ++i) *ptr++ = 0; +} + + +auto wrapper(const FuncType* type) -> vec { + auto in_arity = type->params().size(); + auto out_arity = type->results().size(); + auto size = 39 + in_arity + out_arity; + auto binary = vec::make_uninitialized(size); + auto ptr = binary.get(); + + encode_header(ptr); + + *ptr++ = 0x01; // type section + encode_size32(ptr, 12 + in_arity + out_arity); // size + *ptr++ = 1; // length + *ptr++ = 0x60; // function + encode_size32(ptr, in_arity); + for (size_t i = 0; i < in_arity; ++i) { + encode_valtype(ptr, type->params()[i].get()); + } + encode_size32(ptr, out_arity); + for (size_t i = 0; i < out_arity; ++i) { + encode_valtype(ptr, type->results()[i].get()); + } + + *ptr++ = 0x02; // import section + *ptr++ = 5; // size + *ptr++ = 1; // length + *ptr++ = 0; // module length + *ptr++ = 0; // name length + *ptr++ = 0x00; // func + *ptr++ = 0; // type index + + *ptr++ = 0x07; // export section + *ptr++ = 4; // size + *ptr++ = 1; // length + *ptr++ = 0; // name length + *ptr++ = 0x00; // func + *ptr++ = 0; // func index + + assert(ptr - binary.get() == size); + return binary; +} + +auto wrapper(const GlobalType* type) -> vec { + auto size = 25 + zero_size(type->content()); + auto binary = vec::make_uninitialized(size); + auto ptr = binary.get(); + + encode_header(ptr); + + *ptr++ = 0x06; // global section + encode_size32(ptr, 5 + zero_size(type->content())); // size + *ptr++ = 1; // length + encode_valtype(ptr, type->content()); + *ptr++ = (type->mutability() == Mutability::VAR); + encode_const_zero(ptr, type->content()); + *ptr++ = 0x0b; // end + + *ptr++ = 0x07; // export section + *ptr++ = 4; // size + *ptr++ = 1; // length + *ptr++ = 0; // name length + *ptr++ = 0x03; // global + *ptr++ = 0; // func index + + assert(ptr - binary.get() == size); + return binary; +} + + +//////////////////////////////////////////////////////////////////////////////// +// Decoding + +// Numbers + +auto u32(const byte_t*& pos) -> uint32_t { + uint32_t n = 0; + uint32_t shift = 0; + byte_t b; + do { + b = *pos++; + n += (b & 0x7f) << shift; + shift += 7; + } while ((b & 0x80) != 0); + return n; +} + +auto u64(const byte_t*& pos) -> uint64_t { + uint64_t n = 0; + uint64_t shift = 0; + byte_t b; + do { + b = *pos++; + n += (b & 0x7f) << shift; + shift += 7; + } while ((b & 0x80) != 0); + return n; +} + +void u32_skip(const byte_t*& pos) { + bin::u32(pos); +} + + +// Names + +auto name(const byte_t*& pos) -> Name { + auto size = bin::u32(pos); + auto start = pos; + auto name = Name::make_uninitialized(size); + std::memcpy(name.get(), start, size); + pos += size; + return name; +} + +void name_skip(const byte_t*& pos) { + auto size = bin::u32(pos); + pos += size; +} + + +// Types + +auto valtype(const byte_t*& pos) -> own { + switch (*pos++) { + case 0x7f: return ValType::make(ValKind::I32); + case 0x7e: return ValType::make(ValKind::I64); + case 0x7d: return ValType::make(ValKind::F32); + case 0x7c: return ValType::make(ValKind::F64); + case 0x70: return ValType::make(ValKind::FUNCREF); + case 0x6f: return ValType::make(ValKind::ANYREF); + default: + // TODO(wasm+): support new value types + assert(false); + } +} + +auto mutability(const byte_t*& pos) -> Mutability { + return *pos++ ? Mutability::VAR : Mutability::CONST; +} + +auto limits(const byte_t*& pos) -> Limits { + auto tag = *pos++; + auto min = bin::u32(pos); + if ((tag & 0x01) == 0) { + return Limits(min); + } else { + auto max = bin::u32(pos); + return Limits(min, max); + } +} + +auto stacktype(const byte_t*& pos) -> ownvec { + size_t size = bin::u32(pos); + auto v = ownvec::make_uninitialized(size); + for (uint32_t i = 0; i < size; ++i) v[i] = bin::valtype(pos); + return v; +} + +auto functype(const byte_t*& pos) -> own { + assert(*pos == 0x60); + ++pos; + auto params = bin::stacktype(pos); + auto results = bin::stacktype(pos); + return FuncType::make(std::move(params), std::move(results)); +} + +auto globaltype(const byte_t*& pos) -> own { + auto content = bin::valtype(pos); + auto mutability = bin::mutability(pos); + return GlobalType::make(std::move(content), mutability); +} + +auto tabletype(const byte_t*& pos) -> own { + auto elem = bin::valtype(pos); + auto limits = bin::limits(pos); + return TableType::make(std::move(elem), limits); +} + +auto memorytype(const byte_t*& pos) -> own { + auto limits = bin::limits(pos); + return MemoryType::make(limits); +} + + +void mutability_skip(const byte_t*& pos) { + ++pos; +} + +void limits_skip(const byte_t*& pos) { + auto tag = *pos++; + bin::u32_skip(pos); + if ((tag & 0x01) != 0) bin::u32_skip(pos); +} + +void valtype_skip(const byte_t*& pos) { + // TODO(wasm+): support new value types + ++pos; +} + +void globaltype_skip(const byte_t*& pos) { + bin::valtype_skip(pos); + bin::mutability_skip(pos); +} + +void tabletype_skip(const byte_t*& pos) { + bin::valtype_skip(pos); + bin::limits_skip(pos); +} + +void memorytype_skip(const byte_t*& pos) { + bin::limits_skip(pos); +} + + +// Expressions + +void expr_skip(const byte_t*& pos) { + switch (*pos++ & 0xff) { + case 0x41: // i32.const + case 0x42: // i64.const + case 0x23: // get_global + case 0xd2: { // ref.func + bin::u32_skip(pos); + } break; + case 0x43: { // f32.const + pos += 4; + } break; + case 0x44: { // f64.const + pos += 8; + } break; + case 0xd0: { // ref.null + } break; + default: { + assert(false); + } + } + ++pos; // end +} + + +// Sections + +enum sec_t : byte_t { + SEC_TYPE = 1, + SEC_IMPORT = 2, + SEC_FUNC = 3, + SEC_TABLE = 4, + SEC_MEMORY = 5, + SEC_GLOBAL = 6, + SEC_EXPORT = 7 +}; + +auto section(const vec& binary, bin::sec_t sec) -> const byte_t* { + const byte_t* end = binary.get() + binary.size(); + const byte_t* pos = binary.get() + 8; // skip header + while (pos < end && *pos++ != sec) { + auto size = bin::u32(pos); + pos += size; + } + if (pos == end) return nullptr; + bin::u32_skip(pos); + return pos; +} + +auto section_end(const vec& binary, bin::sec_t sec) -> const byte_t* { + const byte_t* end = binary.get() + binary.size(); + const byte_t* pos = binary.get() + 8; // skip header + while (pos < end && *pos != sec) { + ++pos; + auto size = bin::u32(pos); + pos += size; + } + if (pos == end) return nullptr; + ++pos; + auto size = bin::u32(pos); + return pos + size; +} + + +// Type section + +auto types(const vec& binary) -> ownvec { + auto pos = bin::section(binary, SEC_TYPE); + if (pos == nullptr) return ownvec::make(); + size_t size = bin::u32(pos); + // TODO(wasm+): support new deftypes + auto v = ownvec::make_uninitialized(size); + for (uint32_t i = 0; i < size; ++i) { + v[i] = bin::functype(pos); + } + assert(pos = bin::section_end(binary, SEC_TYPE)); + return v; +} + + +// Import section + +auto imports( + const vec& binary, const ownvec& types +) -> ownvec { + auto pos = bin::section(binary, SEC_IMPORT); + if (pos == nullptr) return ownvec::make(); + size_t size = bin::u32(pos); + auto v = ownvec::make_uninitialized(size); + for (uint32_t i = 0; i < size; ++i) { + auto module = bin::name(pos); + auto name = bin::name(pos); + own type; + switch (*pos++) { + case 0x00: type = types[bin::u32(pos)]->copy(); break; + case 0x01: type = bin::tabletype(pos); break; + case 0x02: type = bin::memorytype(pos); break; + case 0x03: type = bin::globaltype(pos); break; + default: assert(false); + } + v[i] = ImportType::make( + std::move(module), std::move(name), std::move(type)); + } + assert(pos = bin::section_end(binary, SEC_IMPORT)); + return v; +} + +auto count(const ownvec& imports, ExternKind kind) -> uint32_t { + uint32_t n = 0; + for (uint32_t i = 0; i < imports.size(); ++i) { + if (imports[i]->type()->kind() == kind) ++n; + } + return n; +} + + +// Function section + +auto funcs( + const vec& binary, + const ownvec& imports, const ownvec& types +) -> ownvec { + auto pos = bin::section(binary, SEC_FUNC); + size_t size = pos != nullptr ? bin::u32(pos) : 0; + auto v = ownvec::make_uninitialized( + size + count(imports, ExternKind::FUNC)); + size_t j = 0; + for (uint32_t i = 0; i < imports.size(); ++i) { + auto et = imports[i]->type(); + if (et->kind() == ExternKind::FUNC) { + v[j++] = et->func()->copy(); + } + } + if (pos != nullptr) { + for (; j < v.size(); ++j) { + v[j] = types[bin::u32(pos)]->copy(); + } + assert(pos = bin::section_end(binary, SEC_FUNC)); + } + return v; +} + + +// Global section + +auto globals( + const vec& binary, const ownvec& imports +) -> ownvec { + auto pos = bin::section(binary, SEC_GLOBAL); + size_t size = pos != nullptr ? bin::u32(pos) : 0; + auto v = ownvec::make_uninitialized( + size + count(imports, ExternKind::GLOBAL)); + size_t j = 0; + for (uint32_t i = 0; i < imports.size(); ++i) { + auto et = imports[i]->type(); + if (et->kind() == ExternKind::GLOBAL) { + v[j++] = et->global()->copy(); + } + } + if (pos != nullptr) { + for (; j < v.size(); ++j) { + v[j] = bin::globaltype(pos); + expr_skip(pos); + } + assert(pos = bin::section_end(binary, SEC_GLOBAL)); + } + return v; +} + + +// Table section + +auto tables( + const vec& binary, const ownvec& imports +) -> ownvec { + auto pos = bin::section(binary, SEC_TABLE); + size_t size = pos != nullptr ? bin::u32(pos) : 0; + auto v = ownvec::make_uninitialized( + size + count(imports, ExternKind::TABLE)); + size_t j = 0; + for (uint32_t i = 0; i < imports.size(); ++i) { + auto et = imports[i]->type(); + if (et->kind() == ExternKind::TABLE) { + v[j++] = et->table()->copy(); + } + } + if (pos != nullptr) { + for (; j < v.size(); ++j) { + v[j] = bin::tabletype(pos); + } + assert(pos = bin::section_end(binary, SEC_TABLE)); + } + return v; +} + + +// Memory section + +auto memories( + const vec& binary, const ownvec& imports +) -> ownvec { + auto pos = bin::section(binary, SEC_MEMORY); + size_t size = pos != nullptr ? bin::u32(pos) : 0; + auto v = ownvec::make_uninitialized( + size + count(imports, ExternKind::MEMORY)); + size_t j = 0; + for (uint32_t i = 0; i < imports.size(); ++i) { + auto et = imports[i]->type(); + if (et->kind() == ExternKind::MEMORY) { + v[j++] = et->memory()->copy(); + } + } + if (pos != nullptr) { + for (; j < v.size(); ++j) { + v[j] = bin::memorytype(pos); + } + assert(pos = bin::section_end(binary, SEC_MEMORY)); + } + return v; +} + + +// Export section + +auto exports(const vec& binary, + const ownvec& funcs, const ownvec& globals, + const ownvec& tables, const ownvec& memories +) -> ownvec { + auto pos = bin::section(binary, SEC_EXPORT); + if (pos == nullptr) return ownvec::make(); + size_t size = bin::u32(pos); + auto exports = ownvec::make_uninitialized(size); + for (uint32_t i = 0; i < size; ++i) { + auto name = bin::name(pos); + auto tag = *pos++; + auto index = bin::u32(pos); + own type; + switch (tag) { + case 0x00: type = funcs[index]->copy(); break; + case 0x01: type = tables[index]->copy(); break; + case 0x02: type = memories[index]->copy(); break; + case 0x03: type = globals[index]->copy(); break; + default: assert(false); + } + exports[i] = ExportType::make(std::move(name), std::move(type)); + } + assert(pos = bin::section_end(binary, SEC_EXPORT)); + return exports; +} + +auto imports(const vec& binary) -> ownvec { + return bin::imports(binary, bin::types(binary)); +} + +auto exports(const vec& binary) -> ownvec { + auto types = bin::types(binary); + auto imports = bin::imports(binary, types); + auto funcs = bin::funcs(binary, imports, types); + auto globals = bin::globals(binary, imports); + auto tables = bin::tables(binary, imports); + auto memories = bin::memories(binary, imports); + return bin::exports(binary, funcs, globals, tables, memories); +} + +} // namespace bin +} // namespace wasm diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-bin.hh b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-bin.hh new file mode 100644 index 0000000..15b2529 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-bin.hh @@ -0,0 +1,25 @@ +#ifndef __WASM_BIN_HH +#define __WASM_BIN_HH + +#include "wasm.hh" + +namespace wasm { +namespace bin { + +auto u32_size(uint32_t) -> size_t; +auto u64_size(uint64_t) -> size_t; +void encode_u32(char*& ptr, uint32_t n); +void encode_u64(char*& ptr, uint64_t n); +auto u32(const byte_t*& pos) -> uint32_t; +auto u64(const byte_t*& pos) -> uint64_t; + +auto wrapper(const FuncType*) -> vec; +auto wrapper(const GlobalType*) -> vec; + +auto imports(const vec& binary) -> ownvec; +auto exports(const vec& binary) -> ownvec; + +} // namespace bin +} // namespace wasm + +#endif // #ifdef __WASM_BIN_HH diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-c.cc b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-c.cc new file mode 100644 index 0000000..23f8151 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-c.cc @@ -0,0 +1,1024 @@ +#include "wasm.h" +#include "wasm.hh" + +#include "wasm-v8.cc" + +using namespace wasm; + +extern "C" { + +/////////////////////////////////////////////////////////////////////////////// +// Auxiliaries + +// Backing implementation + +extern "C++" { + +template +struct borrowed_vec { + vec it; + borrowed_vec(vec&& v) : it(std::move(v)) {} + borrowed_vec(borrowed_vec&& that) : it(std::move(that.it)) {} + ~borrowed_vec() { it.release(); } +}; + +} // extern "C++" + + +#define WASM_DEFINE_OWN(name, Name) \ + struct wasm_##name##_t : Name {}; \ + \ + void wasm_##name##_delete(wasm_##name##_t* x) { \ + delete x; \ + } \ + \ + extern "C++" inline auto hide_##name(Name* x) -> wasm_##name##_t* { \ + return static_cast(x); \ + } \ + extern "C++" inline auto hide_##name(const Name* x) -> const wasm_##name##_t* { \ + return static_cast(x); \ + } \ + extern "C++" inline auto reveal_##name(wasm_##name##_t* x) -> Name* { \ + return x; \ + } \ + extern "C++" inline auto reveal_##name(const wasm_##name##_t* x) -> const Name* { \ + return x; \ + } \ + extern "C++" inline auto get_##name(own& x) -> wasm_##name##_t* { \ + return hide_##name(x.get()); \ + } \ + extern "C++" inline auto get_##name(const own& x) -> const wasm_##name##_t* { \ + return hide_##name(x.get()); \ + } \ + extern "C++" inline auto release_##name(own&& x) -> wasm_##name##_t* { \ + return hide_##name(x.release()); \ + } \ + extern "C++" inline auto adopt_##name(wasm_##name##_t* x) -> own { \ + return make_own(x); \ + } + + +// Vectors + +#define WASM_DEFINE_VEC_BASE(name, Name, vec, plainvec, ptr_or_none) \ + static_assert( \ + sizeof(wasm_##name##_vec_t) == sizeof(vec), \ + "C/C++ incompatibility" \ + ); \ + static_assert( \ + sizeof(wasm_##name##_t ptr_or_none) == sizeof(vec::elem_type), \ + "C/C++ incompatibility" \ + ); \ + \ + extern "C++" inline auto hide_##name##_vec(vec& v) \ + -> wasm_##name##_vec_t* { \ + return reinterpret_cast(&v); \ + } \ + extern "C++" inline auto hide_##name##_vec(const vec& v) \ + -> const wasm_##name##_vec_t* { \ + return reinterpret_cast(&v); \ + } \ + extern "C++" inline auto hide_##name##_vec(vec::elem_type* v) \ + -> wasm_##name##_t ptr_or_none* { \ + return reinterpret_cast(v); \ + } \ + extern "C++" inline auto hide_##name##_vec(const vec::elem_type* v) \ + -> wasm_##name##_t ptr_or_none const* { \ + return reinterpret_cast(v); \ + } \ + extern "C++" inline auto reveal_##name##_vec(wasm_##name##_vec_t* v) \ + -> plainvec* { \ + return reinterpret_cast*>(v); \ + } \ + extern "C++" inline auto reveal_##name##_vec(const wasm_##name##_vec_t* v) \ + -> const plainvec* { \ + return reinterpret_cast*>(v); \ + } \ + extern "C++" inline auto reveal_##name##_vec(wasm_##name##_t ptr_or_none* v) \ + -> vec::elem_type* { \ + return reinterpret_cast::elem_type*>(v); \ + } \ + extern "C++" inline auto reveal_##name##_vec(wasm_##name##_t ptr_or_none const* v) \ + -> const vec::elem_type* { \ + return reinterpret_cast::elem_type*>(v); \ + } \ + extern "C++" inline auto get_##name##_vec(vec& v) \ + -> wasm_##name##_vec_t { \ + wasm_##name##_vec_t v2 = { v.size(), hide_##name##_vec(v.get()) }; \ + return v2; \ + } \ + extern "C++" inline auto get_##name##_vec(const vec& v) \ + -> const wasm_##name##_vec_t { \ + wasm_##name##_vec_t v2 = { \ + v.size(), const_cast(hide_##name##_vec(v.get())) }; \ + return v2; \ + } \ + extern "C++" inline auto release_##name##_vec(vec&& v) \ + -> wasm_##name##_vec_t { \ + wasm_##name##_vec_t v2 = { v.size(), hide_##name##_vec(v.release()) }; \ + return v2; \ + } \ + extern "C++" inline auto adopt_##name##_vec(wasm_##name##_vec_t* v) \ + -> vec { \ + return vec::adopt(v->size, reveal_##name##_vec(v->data)); \ + } \ + extern "C++" inline auto borrow_##name##_vec(const wasm_##name##_vec_t* v) \ + -> borrowed_vec::elem_type> { \ + return borrowed_vec::elem_type>(vec::adopt(v->size, reveal_##name##_vec(v->data))); \ + } \ + \ + void wasm_##name##_vec_new_uninitialized( \ + wasm_##name##_vec_t* out, size_t size \ + ) { \ + *out = release_##name##_vec(vec::make_uninitialized(size)); \ + } \ + void wasm_##name##_vec_new_empty(wasm_##name##_vec_t* out) { \ + wasm_##name##_vec_new_uninitialized(out, 0); \ + } \ + \ + void wasm_##name##_vec_delete(wasm_##name##_vec_t* v) { \ + adopt_##name##_vec(v); \ + } + +// Vectors with no ownership management of elements +#define WASM_DEFINE_VEC_PLAIN(name, Name) \ + WASM_DEFINE_VEC_BASE(name, Name, vec, vec, ) \ + \ + void wasm_##name##_vec_new( \ + wasm_##name##_vec_t* out, \ + size_t size, \ + const wasm_##name##_t data[] \ + ) { \ + auto v2 = vec::make_uninitialized(size); \ + if (v2.size() != 0) { \ + memcpy(v2.get(), data, size * sizeof(wasm_##name##_t)); \ + } \ + *out = release_##name##_vec(std::move(v2)); \ + } \ + \ + void wasm_##name##_vec_copy( \ + wasm_##name##_vec_t* out, const wasm_##name##_vec_t* v \ + ) { \ + wasm_##name##_vec_new(out, v->size, v->data); \ + } + +// Vectors that own their elements +#define WASM_DEFINE_VEC_OWN(name, Name) \ + WASM_DEFINE_VEC_BASE(name, Name, ownvec, vec, *) \ + \ + void wasm_##name##_vec_new( \ + wasm_##name##_vec_t* out, \ + size_t size, \ + wasm_##name##_t* const data[] \ + ) { \ + auto v2 = ownvec::make_uninitialized(size); \ + for (size_t i = 0; i < v2.size(); ++i) { \ + v2[i] = adopt_##name(data[i]); \ + } \ + *out = release_##name##_vec(std::move(v2)); \ + } \ + \ + void wasm_##name##_vec_copy( \ + wasm_##name##_vec_t* out, const wasm_##name##_vec_t* v \ + ) { \ + auto v2 = ownvec::make_uninitialized(v->size); \ + for (size_t i = 0; i < v2.size(); ++i) { \ + v2[i] = adopt_##name(wasm_##name##_copy(v->data[i])); \ + } \ + *out = release_##name##_vec(std::move(v2)); \ + } + +extern "C++" { +template +inline auto is_empty(T* p) -> bool { return !p; } +} + + +// Byte vectors + +using byte = byte_t; +WASM_DEFINE_VEC_PLAIN(byte, byte) + + +/////////////////////////////////////////////////////////////////////////////// +// Runtime Environment + +// Configuration + +WASM_DEFINE_OWN(config, Config) + +wasm_config_t* wasm_config_new() { + return release_config(Config::make()); +} + + +// Engine + +WASM_DEFINE_OWN(engine, Engine) + +wasm_engine_t* wasm_engine_new() { + return release_engine(Engine::make()); +} + +wasm_engine_t* wasm_engine_new_with_config(wasm_config_t* config) { + return release_engine(Engine::make(adopt_config(config))); +} + + +// Stores + +WASM_DEFINE_OWN(store, Store) + +wasm_store_t* wasm_store_new(wasm_engine_t* engine) { + return release_store(Store::make(engine)); +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Type Representations + +// Type attributes + +extern "C++" inline auto hide_mutability(Mutability mutability) -> wasm_mutability_t { + return static_cast(mutability); +} + +extern "C++" inline auto reveal_mutability(wasm_mutability_t mutability) -> Mutability { + return static_cast(mutability); +} + + +extern "C++" inline auto hide_limits(const Limits& limits) -> const wasm_limits_t* { + return reinterpret_cast(&limits); +} + +extern "C++" inline auto reveal_limits(wasm_limits_t limits) -> Limits { + return Limits(limits.min, limits.max); +} + + +extern "C++" inline auto hide_valkind(ValKind kind) -> wasm_valkind_t { + return static_cast(kind); +} + +extern "C++" inline auto reveal_valkind(wasm_valkind_t kind) -> ValKind { + return static_cast(kind); +} + + +extern "C++" inline auto hide_externkind(ExternKind kind) -> wasm_externkind_t { + return static_cast(kind); +} + +extern "C++" inline auto reveal_externkind(wasm_externkind_t kind) -> ExternKind { + return static_cast(kind); +} + + + +// Generic + +#define WASM_DEFINE_TYPE(name, Name) \ + WASM_DEFINE_OWN(name, Name) \ + WASM_DEFINE_VEC_OWN(name, Name) \ + \ + wasm_##name##_t* wasm_##name##_copy(wasm_##name##_t* t) { \ + return release_##name(t->copy()); \ + } + + +// Value Types + +WASM_DEFINE_TYPE(valtype, ValType) + +wasm_valtype_t* wasm_valtype_new(wasm_valkind_t k) { + return release_valtype(ValType::make(reveal_valkind(k))); +} + +wasm_valkind_t wasm_valtype_kind(const wasm_valtype_t* t) { + return hide_valkind(t->kind()); +} + + +// Function Types + +WASM_DEFINE_TYPE(functype, FuncType) + +wasm_functype_t* wasm_functype_new( + wasm_valtype_vec_t* params, wasm_valtype_vec_t* results +) { + return release_functype( + FuncType::make(adopt_valtype_vec(params), adopt_valtype_vec(results))); +} + +const wasm_valtype_vec_t* wasm_functype_params(const wasm_functype_t* ft) { + return hide_valtype_vec(ft->params()); +} + +const wasm_valtype_vec_t* wasm_functype_results(const wasm_functype_t* ft) { + return hide_valtype_vec(ft->results()); +} + + +// Global Types + +WASM_DEFINE_TYPE(globaltype, GlobalType) + +wasm_globaltype_t* wasm_globaltype_new( + wasm_valtype_t* content, wasm_mutability_t mutability +) { + return release_globaltype(GlobalType::make( + adopt_valtype(content), + reveal_mutability(mutability) + )); +} + +const wasm_valtype_t* wasm_globaltype_content(const wasm_globaltype_t* gt) { + return hide_valtype(gt->content()); +} + +wasm_mutability_t wasm_globaltype_mutability(const wasm_globaltype_t* gt) { + return hide_mutability(gt->mutability()); +} + + +// Table Types + +WASM_DEFINE_TYPE(tabletype, TableType) + +wasm_tabletype_t* wasm_tabletype_new( + wasm_valtype_t* element, const wasm_limits_t* limits +) { + return release_tabletype(TableType::make(adopt_valtype(element), reveal_limits(*limits))); +} + +const wasm_valtype_t* wasm_tabletype_element(const wasm_tabletype_t* tt) { + return hide_valtype(tt->element()); +} + +const wasm_limits_t* wasm_tabletype_limits(const wasm_tabletype_t* tt) { + return hide_limits(tt->limits()); +} + + +// Memory Types + +WASM_DEFINE_TYPE(memorytype, MemoryType) + +wasm_memorytype_t* wasm_memorytype_new(const wasm_limits_t* limits) { + return release_memorytype(MemoryType::make(reveal_limits(*limits))); +} + +const wasm_limits_t* wasm_memorytype_limits(const wasm_memorytype_t* mt) { + return hide_limits(mt->limits()); +} + + +// Extern Types + +WASM_DEFINE_TYPE(externtype, ExternType) + +wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t* et) { + return hide_externkind(et->kind()); +} + +wasm_externtype_t* wasm_functype_as_externtype(wasm_functype_t* ft) { + return hide_externtype(static_cast(ft)); +} +wasm_externtype_t* wasm_globaltype_as_externtype(wasm_globaltype_t* gt) { + return hide_externtype(static_cast(gt)); +} +wasm_externtype_t* wasm_tabletype_as_externtype(wasm_tabletype_t* tt) { + return hide_externtype(static_cast(tt)); +} +wasm_externtype_t* wasm_memorytype_as_externtype(wasm_memorytype_t* mt) { + return hide_externtype(static_cast(mt)); +} + +const wasm_externtype_t* wasm_functype_as_externtype_const( + const wasm_functype_t* ft +) { + return hide_externtype(static_cast(ft)); +} +const wasm_externtype_t* wasm_globaltype_as_externtype_const( + const wasm_globaltype_t* gt +) { + return hide_externtype(static_cast(gt)); +} +const wasm_externtype_t* wasm_tabletype_as_externtype_const( + const wasm_tabletype_t* tt +) { + return hide_externtype(static_cast(tt)); +} +const wasm_externtype_t* wasm_memorytype_as_externtype_const( + const wasm_memorytype_t* mt +) { + return hide_externtype(static_cast(mt)); +} + +wasm_functype_t* wasm_externtype_as_functype(wasm_externtype_t* et) { + return et->kind() == ExternKind::FUNC + ? hide_functype(static_cast(reveal_externtype(et))) : nullptr; +} +wasm_globaltype_t* wasm_externtype_as_globaltype(wasm_externtype_t* et) { + return et->kind() == ExternKind::GLOBAL + ? hide_globaltype(static_cast(reveal_externtype(et))) : nullptr; +} +wasm_tabletype_t* wasm_externtype_as_tabletype(wasm_externtype_t* et) { + return et->kind() == ExternKind::TABLE + ? hide_tabletype(static_cast(reveal_externtype(et))) : nullptr; +} +wasm_memorytype_t* wasm_externtype_as_memorytype(wasm_externtype_t* et) { + return et->kind() == ExternKind::MEMORY + ? hide_memorytype(static_cast(reveal_externtype(et))) : nullptr; +} + +const wasm_functype_t* wasm_externtype_as_functype_const( + const wasm_externtype_t* et +) { + return et->kind() == ExternKind::FUNC + ? hide_functype(static_cast(reveal_externtype(et))) : nullptr; +} +const wasm_globaltype_t* wasm_externtype_as_globaltype_const( + const wasm_externtype_t* et +) { + return et->kind() == ExternKind::GLOBAL + ? hide_globaltype(static_cast(reveal_externtype(et))) : nullptr; +} +const wasm_tabletype_t* wasm_externtype_as_tabletype_const( + const wasm_externtype_t* et +) { + return et->kind() == ExternKind::TABLE + ? hide_tabletype(static_cast(reveal_externtype(et))) : nullptr; +} +const wasm_memorytype_t* wasm_externtype_as_memorytype_const( + const wasm_externtype_t* et +) { + return et->kind() == ExternKind::MEMORY + ? hide_memorytype(static_cast(reveal_externtype(et))) : nullptr; +} + + +// Import Types + +WASM_DEFINE_TYPE(importtype, ImportType) + +wasm_importtype_t* wasm_importtype_new( + wasm_name_t* module, wasm_name_t* name, wasm_externtype_t* type +) { + return release_importtype( + ImportType::make(adopt_byte_vec(module), adopt_byte_vec(name), adopt_externtype(type))); +} + +const wasm_name_t* wasm_importtype_module(const wasm_importtype_t* it) { + return hide_byte_vec(it->module()); +} + +const wasm_name_t* wasm_importtype_name(const wasm_importtype_t* it) { + return hide_byte_vec(it->name()); +} + +const wasm_externtype_t* wasm_importtype_type(const wasm_importtype_t* it) { + return hide_externtype(it->type()); +} + + +// Export Types + +WASM_DEFINE_TYPE(exporttype, ExportType) + +wasm_exporttype_t* wasm_exporttype_new( + wasm_name_t* name, wasm_externtype_t* type +) { + return release_exporttype( + ExportType::make(adopt_byte_vec(name), adopt_externtype(type))); +} + +const wasm_name_t* wasm_exporttype_name(const wasm_exporttype_t* et) { + return hide_byte_vec(et->name()); +} + +const wasm_externtype_t* wasm_exporttype_type(const wasm_exporttype_t* et) { + return hide_externtype(et->type()); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Runtime Values + +// References + +#define WASM_DEFINE_REF_BASE(name, Name) \ + WASM_DEFINE_OWN(name, Name) \ + \ + wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t* t) { \ + return release_##name(t->copy()); \ + } \ + \ + bool wasm_##name##_same(const wasm_##name##_t* t1, const wasm_##name##_t* t2) { \ + return t1->same(t2); \ + } \ + \ + void* wasm_##name##_get_host_info(const wasm_##name##_t* r) { \ + return r->get_host_info(); \ + } \ + void wasm_##name##_set_host_info(wasm_##name##_t* r, void* info) { \ + r->set_host_info(info); \ + } \ + void wasm_##name##_set_host_info_with_finalizer( \ + wasm_##name##_t* r, void* info, void (*finalizer)(void*) \ + ) { \ + r->set_host_info(info, finalizer); \ + } + +#define WASM_DEFINE_REF(name, Name) \ + WASM_DEFINE_REF_BASE(name, Name) \ + \ + wasm_ref_t* wasm_##name##_as_ref(wasm_##name##_t* r) { \ + return hide_ref(static_cast(reveal_##name(r))); \ + } \ + wasm_##name##_t* wasm_ref_as_##name(wasm_ref_t* r) { \ + return hide_##name(static_cast(reveal_ref(r))); \ + } \ + \ + const wasm_ref_t* wasm_##name##_as_ref_const(const wasm_##name##_t* r) { \ + return hide_ref(static_cast(reveal_##name(r))); \ + } \ + const wasm_##name##_t* wasm_ref_as_##name##_const(const wasm_ref_t* r) { \ + return hide_##name(static_cast(reveal_ref(r))); \ + } + +#define WASM_DEFINE_SHARABLE_REF(name, Name) \ + WASM_DEFINE_REF(name, Name) \ + WASM_DEFINE_OWN(shared_##name, Shared) + + +WASM_DEFINE_REF_BASE(ref, Ref) + + +// Values + +extern "C++" { + +inline auto is_empty(wasm_val_t v) -> bool { + return !is_ref(reveal_valkind(v.kind)) || !v.of.ref; +} + +inline auto hide_val(Val v) -> wasm_val_t { + wasm_val_t v2 = { hide_valkind(v.kind()) }; + switch (v.kind()) { + case ValKind::I32: v2.of.i32 = v.i32(); break; + case ValKind::I64: v2.of.i64 = v.i64(); break; + case ValKind::F32: v2.of.f32 = v.f32(); break; + case ValKind::F64: v2.of.f64 = v.f64(); break; + case ValKind::ANYREF: + case ValKind::FUNCREF: v2.of.ref = hide_ref(v.ref()); break; + default: assert(false); + } + return v2; +} + +inline auto release_val(Val v) -> wasm_val_t { + wasm_val_t v2 = { hide_valkind(v.kind()) }; + switch (v.kind()) { + case ValKind::I32: v2.of.i32 = v.i32(); break; + case ValKind::I64: v2.of.i64 = v.i64(); break; + case ValKind::F32: v2.of.f32 = v.f32(); break; + case ValKind::F64: v2.of.f64 = v.f64(); break; + case ValKind::ANYREF: + case ValKind::FUNCREF: v2.of.ref = release_ref(v.release_ref()); break; + default: assert(false); + } + return v2; +} + +inline auto adopt_val(wasm_val_t v) -> Val { + switch (reveal_valkind(v.kind)) { + case ValKind::I32: return Val(v.of.i32); + case ValKind::I64: return Val(v.of.i64); + case ValKind::F32: return Val(v.of.f32); + case ValKind::F64: return Val(v.of.f64); + case ValKind::ANYREF: + case ValKind::FUNCREF: return Val(adopt_ref(v.of.ref)); + default: assert(false); + } +} + +struct borrowed_val { + Val it; + borrowed_val(Val&& v) : it(std::move(v)) {} + borrowed_val(borrowed_val&& that) : it(std::move(that.it)) {} + ~borrowed_val() { if (it.is_ref()) it.release_ref().release(); } +}; + +inline auto borrow_val(const wasm_val_t* v) -> borrowed_val { + Val v2; + switch (reveal_valkind(v->kind)) { + case ValKind::I32: v2 = Val(v->of.i32); break; + case ValKind::I64: v2 = Val(v->of.i64); break; + case ValKind::F32: v2 = Val(v->of.f32); break; + case ValKind::F64: v2 = Val(v->of.f64); break; + case ValKind::ANYREF: + case ValKind::FUNCREF: v2 = Val(adopt_ref(v->of.ref)); break; + default: assert(false); + } + return borrowed_val(std::move(v2)); +} + +} // extern "C++" + + +WASM_DEFINE_VEC_BASE(val, Val, vec, vec, ) + +void wasm_val_vec_new( + wasm_val_vec_t* out, size_t size, wasm_val_t const data[] +) { + auto v2 = vec::make_uninitialized(size); + for (size_t i = 0; i < v2.size(); ++i) { + v2[i] = adopt_val(data[i]); + } + *out = release_val_vec(std::move(v2)); +} + +void wasm_val_vec_copy(wasm_val_vec_t* out, const wasm_val_vec_t* v) { + auto v2 = vec::make_uninitialized(v->size); + for (size_t i = 0; i < v2.size(); ++i) { + wasm_val_t val; + wasm_val_copy(&v->data[i], &val); + v2[i] = adopt_val(val); + } + *out = release_val_vec(std::move(v2)); +} + + +void wasm_val_delete(wasm_val_t* v) { + if (is_ref(reveal_valkind(v->kind))) { + adopt_ref(v->of.ref); + } +} + +void wasm_val_copy(wasm_val_t* out, const wasm_val_t* v) { + *out = *v; + if (is_ref(reveal_valkind(v->kind))) { + out->of.ref = v->of.ref ? release_ref(v->of.ref->copy()) : nullptr; + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// Runtime Objects + +// Frames + +WASM_DEFINE_OWN(frame, Frame) +WASM_DEFINE_VEC_OWN(frame, Frame) + +wasm_frame_t* wasm_frame_copy(const wasm_frame_t* frame) { + return release_frame(frame->copy()); +} + +wasm_instance_t* wasm_frame_instance(const wasm_frame_t* frame); +// Defined below along with wasm_instance_t. + +uint32_t wasm_frame_func_index(const wasm_frame_t* frame) { + return reveal_frame(frame)->func_index(); +} + +size_t wasm_frame_func_offset(const wasm_frame_t* frame) { + return reveal_frame(frame)->func_offset(); +} + +size_t wasm_frame_module_offset(const wasm_frame_t* frame) { + return reveal_frame(frame)->module_offset(); +} + + +// Traps + +WASM_DEFINE_REF(trap, Trap) + +wasm_trap_t* wasm_trap_new(wasm_store_t* store, const wasm_message_t* message) { + auto message_ = borrow_byte_vec(message); + return release_trap(Trap::make(store, message_.it)); +} + +void wasm_trap_message(const wasm_trap_t* trap, wasm_message_t* out) { + *out = release_byte_vec(reveal_trap(trap)->message()); +} + +wasm_frame_t* wasm_trap_origin(const wasm_trap_t* trap) { + return release_frame(reveal_trap(trap)->origin()); +} + +void wasm_trap_trace(const wasm_trap_t* trap, wasm_frame_vec_t* out) { + *out = release_frame_vec(reveal_trap(trap)->trace()); +} + + +// Foreign Objects + +WASM_DEFINE_REF(foreign, Foreign) + +wasm_foreign_t* wasm_foreign_new(wasm_store_t* store) { + return release_foreign(Foreign::make(store)); +} + + +// Modules + +WASM_DEFINE_SHARABLE_REF(module, Module) + +bool wasm_module_validate(wasm_store_t* store, const wasm_byte_vec_t* binary) { + auto binary_ = borrow_byte_vec(binary); + return Module::validate(store, binary_.it); +} + +wasm_module_t* wasm_module_new( + wasm_store_t* store, const wasm_byte_vec_t* binary +) { + auto binary_ = borrow_byte_vec(binary); + return release_module(Module::make(store, binary_.it)); +} + + +void wasm_module_imports( + const wasm_module_t* module, wasm_importtype_vec_t* out +) { + *out = release_importtype_vec(reveal_module(module)->imports()); +} + +void wasm_module_exports( + const wasm_module_t* module, wasm_exporttype_vec_t* out +) { + *out = release_exporttype_vec(reveal_module(module)->exports()); +} + +void wasm_module_serialize(const wasm_module_t* module, wasm_byte_vec_t* out) { + *out = release_byte_vec(reveal_module(module)->serialize()); +} + +wasm_module_t* wasm_module_deserialize( + wasm_store_t* store, const wasm_byte_vec_t* binary +) { + auto binary_ = borrow_byte_vec(binary); + return release_module(Module::deserialize(store, binary_.it)); +} + +wasm_shared_module_t* wasm_module_share(const wasm_module_t* module) { + return release_shared_module(reveal_module(module)->share()); +} + +wasm_module_t* wasm_module_obtain(wasm_store_t* store, const wasm_shared_module_t* shared) { + return release_module(Module::obtain(store, shared)); +} + + +// Function Instances + +WASM_DEFINE_REF(func, Func) + +extern "C++" { + +auto wasm_callback( + void* env, const vec& args, vec& results +) -> own { + auto f = reinterpret_cast(env); + return adopt_trap(f(hide_val_vec(args), hide_val_vec(results))); +} + +struct wasm_callback_env_t { + wasm_func_callback_with_env_t callback; + void* env; + void (*finalizer)(void*); +}; + +auto wasm_callback_with_env( + void* env, const vec& args, vec& results +) -> own { + auto t = static_cast(env); + return adopt_trap(t->callback(t->env, hide_val_vec(args), hide_val_vec(results))); +} + +void wasm_callback_env_finalizer(void* env) { + auto t = static_cast(env); + if (t->finalizer) t->finalizer(t->env); + delete t; +} + +} // extern "C++" + +wasm_func_t* wasm_func_new( + wasm_store_t* store, const wasm_functype_t* type, + wasm_func_callback_t callback +) { + return release_func(Func::make( + store, type, wasm_callback, reinterpret_cast(callback))); +} + +wasm_functype_t* wasm_func_type(const wasm_func_t* func) { + return release_functype(func->type()); +} + +size_t wasm_func_param_arity(const wasm_func_t* func) { + return func->param_arity(); +} + +size_t wasm_func_result_arity(const wasm_func_t* func) { + return func->result_arity(); +} + +wasm_trap_t* wasm_func_call( + const wasm_func_t* func, const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + auto args_ = borrow_val_vec(args); + auto results_ = borrow_val_vec(results); + return release_trap(func->call(args_.it, results_.it)); +} + + +// Global Instances + +WASM_DEFINE_REF(global, Global) + +wasm_global_t* wasm_global_new( + wasm_store_t* store, const wasm_globaltype_t* type, const wasm_val_t* val +) { + auto val_ = borrow_val(val); + return release_global(Global::make(store, type, val_.it)); +} + +wasm_globaltype_t* wasm_global_type(const wasm_global_t* global) { + return release_globaltype(global->type()); +} + +void wasm_global_get(const wasm_global_t* global, wasm_val_t* out) { + *out = release_val(global->get()); +} + +void wasm_global_set(wasm_global_t* global, const wasm_val_t* val) { + auto val_ = borrow_val(val); + global->set(val_.it); +} + + +// Table Instances + +WASM_DEFINE_REF(table, Table) + +wasm_table_t* wasm_table_new( + wasm_store_t* store, const wasm_tabletype_t* type, wasm_ref_t* ref +) { + return release_table(Table::make(store, type, ref)); +} + +wasm_tabletype_t* wasm_table_type(const wasm_table_t* table) { + return release_tabletype(table->type()); +} + +wasm_ref_t* wasm_table_get(const wasm_table_t* table, wasm_table_size_t index) { + return release_ref(table->get(index)); +} + +bool wasm_table_set( + wasm_table_t* table, wasm_table_size_t index, wasm_ref_t* ref +) { + return table->set(index, ref); +} + +wasm_table_size_t wasm_table_size(const wasm_table_t* table) { + return table->size(); +} + +bool wasm_table_grow( + wasm_table_t* table, wasm_table_size_t delta, wasm_ref_t* ref +) { + return table->grow(delta, ref); +} + + +// Memory Instances + +WASM_DEFINE_REF(memory, Memory) + +wasm_memory_t* wasm_memory_new( + wasm_store_t* store, const wasm_memorytype_t* type +) { + return release_memory(Memory::make(store, type)); +} + +wasm_memorytype_t* wasm_memory_type(const wasm_memory_t* memory) { + return release_memorytype(memory->type()); +} + +wasm_byte_t* wasm_memory_data(wasm_memory_t* memory) { + return memory->data(); +} + +size_t wasm_memory_data_size(const wasm_memory_t* memory) { + return memory->data_size(); +} + +wasm_memory_pages_t wasm_memory_size(const wasm_memory_t* memory) { + return memory->size(); +} + +bool wasm_memory_grow(wasm_memory_t* memory, wasm_memory_pages_t delta) { + return memory->grow(delta); +} + + +// Externals + +WASM_DEFINE_REF(extern, Extern) +WASM_DEFINE_VEC_OWN(extern, Extern) + +wasm_externkind_t wasm_extern_kind(const wasm_extern_t* external) { + return hide_externkind(external->kind()); +} +wasm_externtype_t* wasm_extern_type(const wasm_extern_t* external) { + return release_externtype(external->type()); +} + +wasm_extern_t* wasm_func_as_extern(wasm_func_t* func) { + return hide_extern(static_cast(reveal_func(func))); +} +wasm_extern_t* wasm_global_as_extern(wasm_global_t* global) { + return hide_extern(static_cast(reveal_global(global))); +} +wasm_extern_t* wasm_table_as_extern(wasm_table_t* table) { + return hide_extern(static_cast(reveal_table(table))); +} +wasm_extern_t* wasm_memory_as_extern(wasm_memory_t* memory) { + return hide_extern(static_cast(reveal_memory(memory))); +} + +const wasm_extern_t* wasm_func_as_extern_const(const wasm_func_t* func) { + return hide_extern(static_cast(reveal_func(func))); +} +const wasm_extern_t* wasm_global_as_extern_const(const wasm_global_t* global) { + return hide_extern(static_cast(reveal_global(global))); +} +const wasm_extern_t* wasm_table_as_extern_const(const wasm_table_t* table) { + return hide_extern(static_cast(reveal_table(table))); +} +const wasm_extern_t* wasm_memory_as_extern_const(const wasm_memory_t* memory) { + return hide_extern(static_cast(reveal_memory(memory))); +} + +wasm_func_t* wasm_extern_as_func(wasm_extern_t* external) { + return hide_func(external->func()); +} +wasm_global_t* wasm_extern_as_global(wasm_extern_t* external) { + return hide_global(external->global()); +} +wasm_table_t* wasm_extern_as_table(wasm_extern_t* external) { + return hide_table(external->table()); +} +wasm_memory_t* wasm_extern_as_memory(wasm_extern_t* external) { + return hide_memory(external->memory()); +} + +const wasm_func_t* wasm_extern_as_func_const(const wasm_extern_t* external) { + return hide_func(external->func()); +} +const wasm_global_t* wasm_extern_as_global_const(const wasm_extern_t* external) { + return hide_global(external->global()); +} +const wasm_table_t* wasm_extern_as_table_const(const wasm_extern_t* external) { + return hide_table(external->table()); +} +const wasm_memory_t* wasm_extern_as_memory_const(const wasm_extern_t* external) { + return hide_memory(external->memory()); +} + + +// Module Instances + +WASM_DEFINE_REF(instance, Instance) + +wasm_instance_t* wasm_instance_new( + wasm_store_t* store, + const wasm_module_t* module, + const wasm_extern_vec_t* imports, + wasm_trap_t** trap +) { + own error; + auto imports_ = reveal_extern_vec(imports); + auto instance = + release_instance(Instance::make(store, module, *imports_, &error)); + if (trap) *trap = hide_trap(error.release()); + return instance; +} + +void wasm_instance_exports( + const wasm_instance_t* instance, wasm_extern_vec_t* out +) { + *out = release_extern_vec(instance->exports()); +} + + +wasm_instance_t* wasm_frame_instance(const wasm_frame_t* frame) { + return hide_instance(reveal_frame(frame)->instance()); +} + +} // extern "C" diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-v8-lowlevel.cc b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-v8-lowlevel.cc new file mode 100644 index 0000000..d00f48e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-v8-lowlevel.cc @@ -0,0 +1,443 @@ +#include "wasm-v8-lowlevel.hh" + +// TODO(v8): if we don't include these, api.h does not compile +#include "objects/objects.h" +#include "objects/bigint.h" +#include "objects/managed.h" +#include "objects/module.h" +#include "objects/shared-function-info.h" +#include "objects/templates.h" +#include "objects/fixed-array.h" +#include "objects/ordered-hash-table.h" +#include "objects/js-promise.h" +#include "objects/js-collection.h" + +#include "api/api.h" +#include "api/api-inl.h" +#include "wasm/wasm-objects.h" +#include "wasm/wasm-objects-inl.h" +#include "wasm/wasm-serialization.h" + + +namespace v8 { +namespace wasm { + + +// Objects + +auto object_isolate(v8::Local obj) -> v8::Isolate* { + auto v8_obj = v8::Utils::OpenHandle(*obj); + return reinterpret_cast(v8_obj->GetIsolate()); +} + +auto object_isolate(const v8::Persistent& obj) -> v8::Isolate* { + struct FakePersistent { v8::Object* val; }; + auto v8_obj = reinterpret_cast(&obj)->val; + return v8_obj->GetIsolate(); +} + +template +auto object_handle(T v8_obj) -> v8::internal::Handle { + return handle(v8_obj, v8_obj.GetIsolate()); +} + + +auto object_is_module(v8::Local obj) -> bool { + auto v8_obj = v8::Utils::OpenHandle(*obj); + return v8_obj->IsWasmModuleObject(); +} + +auto object_is_instance(v8::Local obj) -> bool { + auto v8_obj = v8::Utils::OpenHandle(*obj); + return v8_obj->IsWasmInstanceObject(); +} + +auto object_is_func(v8::Local obj) -> bool { + auto v8_obj = v8::Utils::OpenHandle(*obj); + return v8::internal::WasmExportedFunction::IsWasmExportedFunction(*v8_obj); +} + +auto object_is_global(v8::Local obj) -> bool { + auto v8_obj = v8::Utils::OpenHandle(*obj); + return v8_obj->IsWasmGlobalObject(); +} + +auto object_is_table(v8::Local obj) -> bool { + auto v8_obj = v8::Utils::OpenHandle(*obj); + return v8_obj->IsWasmTableObject(); +} + +auto object_is_memory(v8::Local obj) -> bool { + auto v8_obj = v8::Utils::OpenHandle(*obj); + return v8_obj->IsWasmMemoryObject(); +} + +auto object_is_error(v8::Local obj) -> bool { + auto v8_obj = v8::Utils::OpenHandle(*obj); + return v8_obj->IsJSError(); +} + + + +// Foreign pointers + +auto foreign_new(v8::Isolate* isolate, void* ptr) -> v8::Local { + auto foreign = v8::FromCData( + reinterpret_cast(isolate), + reinterpret_cast(ptr) + ); + return v8::Utils::ToLocal(foreign); +} + +auto foreign_get(v8::Local val) -> void* { + auto foreign = v8::Utils::OpenHandle(*val); + if (!foreign->IsForeign()) return nullptr; + auto addr = v8::ToCData(*foreign); + return reinterpret_cast(addr); +} + + +struct ManagedData { + ManagedData(void* info, void (*finalizer)(void*)) : + info(info), finalizer(finalizer) {} + + ~ManagedData() { + if (finalizer) (*finalizer)(info); + } + + void* info; + void (*finalizer)(void*); +}; + +auto managed_new(v8::Isolate* isolate, void* ptr, void (*finalizer)(void*)) -> v8::Local { + assert(ptr); + auto managed = v8::internal::Managed::FromUniquePtr( + reinterpret_cast(isolate), sizeof(ManagedData), + std::unique_ptr(new ManagedData(ptr, finalizer)) + ); + return v8::Utils::ToLocal(managed); +} + +auto managed_get(v8::Local val) -> void* { + auto v8_val = v8::Utils::OpenHandle(*val); + if (!v8_val->IsForeign()) return nullptr; + auto managed = + v8::internal::Handle>::cast(v8_val); + return managed->raw()->info; +} + + +// Types + +auto v8_valtype_to_wasm(v8::internal::wasm::ValueType v8_valtype) -> val_kind_t { + switch (v8_valtype) { + case v8::internal::wasm::kWasmI32: return I32; + case v8::internal::wasm::kWasmI64: return I64; + case v8::internal::wasm::kWasmF32: return F32; + case v8::internal::wasm::kWasmF64: return F64; + case v8::internal::wasm::kWasmAnyRef: return ANYREF; + case v8::internal::wasm::kWasmAnyFunc: return FUNCREF; + default: + UNREACHABLE(); + } +} + +auto func_type_param_arity(v8::Local function) -> uint32_t { + auto v8_object = v8::Utils::OpenHandle(function); + auto v8_function = v8::internal::Handle::cast(v8_object); + v8::internal::wasm::FunctionSig* sig = + v8_function->instance().module()->functions[v8_function->function_index()].sig; + return static_cast(sig->parameter_count()); +} + +auto func_type_result_arity(v8::Local function) -> uint32_t { + auto v8_object = v8::Utils::OpenHandle(function); + auto v8_function = v8::internal::Handle::cast(v8_object); + v8::internal::wasm::FunctionSig* sig = + v8_function->instance().module()->functions[v8_function->function_index()].sig; + return static_cast(sig->return_count()); +} + +auto func_type_param(v8::Local function, size_t i) -> val_kind_t { + auto v8_object = v8::Utils::OpenHandle(function); + auto v8_function = v8::internal::Handle::cast(v8_object); + v8::internal::wasm::FunctionSig* sig = + v8_function->instance().module()->functions[v8_function->function_index()].sig; + return v8_valtype_to_wasm(sig->GetParam(i)); +} + +auto func_type_result(v8::Local function, size_t i) -> val_kind_t { + auto v8_object = v8::Utils::OpenHandle(function); + auto v8_function = v8::internal::Handle::cast(v8_object); + v8::internal::wasm::FunctionSig* sig = + v8_function->instance().module()->functions[v8_function->function_index()].sig; + return v8_valtype_to_wasm(sig->GetReturn(i)); +} + +auto global_type_content(v8::Local global) -> val_kind_t { + auto v8_object = v8::Utils::OpenHandle(global); + auto v8_global = v8::internal::Handle::cast(v8_object); + return v8_valtype_to_wasm(v8_global->type()); +} + +auto global_type_mutable(v8::Local global) -> bool { + auto v8_object = v8::Utils::OpenHandle(global); + auto v8_global = v8::internal::Handle::cast(v8_object); + return v8_global->is_mutable(); +} + +auto table_type_min(v8::Local table) -> uint32_t { + auto v8_object = v8::Utils::OpenHandle(table); + auto v8_table = v8::internal::Handle::cast(v8_object); + return v8_table->current_length(); +} + +auto table_type_max(v8::Local table) -> uint32_t { + auto v8_object = v8::Utils::OpenHandle(table); + auto v8_table = v8::internal::Handle::cast(v8_object); + auto v8_max_obj = v8_table->maximum_length(); + uint32_t max; + return v8_max_obj.ToUint32(&max) ? max : 0xffffffffu; +} + +auto memory_type_min(v8::Local memory) -> uint32_t { + return memory_size(memory); +} + +auto memory_type_max(v8::Local memory) -> uint32_t { + auto v8_object = v8::Utils::OpenHandle(memory); + auto v8_memory = v8::internal::Handle::cast(v8_object); + return v8_memory->has_maximum_pages() ? v8_memory->maximum_pages() : 0xffffffffu; +} + + +// Modules + +auto module_binary_size(v8::Local module) -> size_t { + auto v8_object = v8::Utils::OpenHandle(module); + auto v8_module = v8::internal::Handle::cast(v8_object); + return v8_module->native_module()->wire_bytes().size(); +} + +auto module_binary(v8::Local module) -> const char* { + auto v8_object = v8::Utils::OpenHandle(module); + auto v8_module = v8::internal::Handle::cast(v8_object); + return reinterpret_cast(v8_module->native_module()->wire_bytes().begin()); +} + +auto module_serialize_size(v8::Local module) -> size_t { + auto v8_object = v8::Utils::OpenHandle(module); + auto v8_module = v8::internal::Handle::cast(v8_object); + v8::internal::wasm::WasmSerializer serializer(v8_module->native_module()); + return serializer.GetSerializedNativeModuleSize(); +} + +auto module_serialize(v8::Local module, char* buffer, size_t size) -> bool { + auto v8_object = v8::Utils::OpenHandle(module); + auto v8_module = v8::internal::Handle::cast(v8_object); + v8::internal::wasm::WasmSerializer serializer(v8_module->native_module()); + return serializer.SerializeNativeModule({reinterpret_cast(buffer), size}); +} + +auto module_deserialize( + v8::Isolate* isolate, + const char* binary, size_t binary_size, + const char* buffer, size_t buffer_size +) -> v8::MaybeLocal { + auto v8_isolate = reinterpret_cast(isolate); + auto maybe_v8_module = + v8::internal::wasm::DeserializeNativeModule(v8_isolate, + {reinterpret_cast(buffer), buffer_size}, + {reinterpret_cast(binary), binary_size}); + if (maybe_v8_module.is_null()) return v8::MaybeLocal(); + auto v8_module = v8::internal::Handle::cast(maybe_v8_module.ToHandleChecked()); + return v8::MaybeLocal(v8::Utils::ToLocal(v8_module)); +} + + +// Instances + +auto instance_module(v8::Local instance) -> v8::Local { + auto v8_object = v8::Utils::OpenHandle(instance); + auto v8_instance = v8::internal::Handle::cast(v8_object); + auto v8_module = object_handle(v8::internal::JSObject::cast(v8_instance->module_object())); + return v8::Utils::ToLocal(v8_module); +} + +auto instance_exports(v8::Local instance) -> v8::Local { + auto v8_object = v8::Utils::OpenHandle(instance); + auto v8_instance = v8::internal::Handle::cast(v8_object); + auto v8_exports = object_handle(v8_instance->exports_object()); + return v8::Utils::ToLocal(v8_exports); +} + + +// Externals + +auto extern_kind(v8::Local external) -> extern_kind_t { + auto v8_object = v8::Utils::OpenHandle(external); + + if (v8::internal::WasmExportedFunction::IsWasmExportedFunction(*v8_object)) return EXTERN_FUNC; + if (v8_object->IsWasmGlobalObject()) return EXTERN_GLOBAL; + if (v8_object->IsWasmTableObject()) return EXTERN_TABLE; + if (v8_object->IsWasmMemoryObject()) return EXTERN_MEMORY; + UNREACHABLE(); +} + + +// Functions + +auto func_instance(v8::Local function) -> v8::Local { + auto v8_function = v8::Utils::OpenHandle(*function); + auto v8_func = v8::internal::Handle::cast(v8_function); + auto v8_instance = object_handle(v8::internal::JSObject::cast(v8_func->instance())); + return v8::Utils::ToLocal(v8_instance); +} + + +// Globals + +auto global_get_i32(v8::Local global) -> int32_t { + auto v8_object = v8::Utils::OpenHandle(global); + auto v8_global = v8::internal::Handle::cast(v8_object); + return v8_global->GetI32(); +} +auto global_get_i64(v8::Local global) -> int64_t { + auto v8_object = v8::Utils::OpenHandle(global); + auto v8_global = v8::internal::Handle::cast(v8_object); + return v8_global->GetI64(); +} +auto global_get_f32(v8::Local global) -> float { + auto v8_object = v8::Utils::OpenHandle(global); + auto v8_global = v8::internal::Handle::cast(v8_object); + return v8_global->GetF32(); +} +auto global_get_f64(v8::Local global) -> double { + auto v8_object = v8::Utils::OpenHandle(global); + auto v8_global = v8::internal::Handle::cast(v8_object); + return v8_global->GetF64(); +} +auto global_get_ref(v8::Local global) -> v8::Local { + auto v8_object = v8::Utils::OpenHandle(global); + auto v8_global = v8::internal::Handle::cast(v8_object); + return v8::Utils::ToLocal(v8_global->GetRef()); +} + +void global_set_i32(v8::Local global, int32_t val) { + auto v8_object = v8::Utils::OpenHandle(global); + auto v8_global = v8::internal::Handle::cast(v8_object); + v8_global->SetI32(val); +} +void global_set_i64(v8::Local global, int64_t val) { + auto v8_object = v8::Utils::OpenHandle(global); + auto v8_global = v8::internal::Handle::cast(v8_object); + v8_global->SetI64(val); +} +void global_set_f32(v8::Local global, float val) { + auto v8_object = v8::Utils::OpenHandle(global); + auto v8_global = v8::internal::Handle::cast(v8_object); + v8_global->SetF32(val); +} +void global_set_f64(v8::Local global, double val) { + auto v8_object = v8::Utils::OpenHandle(global); + auto v8_global = v8::internal::Handle::cast(v8_object); + v8_global->SetF64(val); +} +void global_set_ref(v8::Local global, v8::Local val) { + auto v8_object = v8::Utils::OpenHandle(global); + auto v8_global = v8::internal::Handle::cast(v8_object); + v8_global->SetAnyRef(v8::Utils::OpenHandle(val)); +} + + +// Tables + +auto table_get(v8::Local table, size_t index) -> v8::MaybeLocal { + auto v8_object = v8::Utils::OpenHandle(table); + auto v8_table = v8::internal::Handle::cast(v8_object); + // TODO(v8): This should happen in WasmTableObject::Get. + if (index > v8_table->current_length()) return v8::MaybeLocal(); + + v8::internal::Handle v8_value = + v8::internal::WasmTableObject::Get( + v8_table->GetIsolate(), v8_table, static_cast(index)); + return v8::Utils::ToLocal(v8::internal::Handle::cast(v8_value)); +} + +auto table_set( + v8::Local table, size_t index, v8::Local value +) -> bool { + auto v8_object = v8::Utils::OpenHandle(table); + auto v8_table = v8::internal::Handle::cast(v8_object); + auto v8_value = v8::Utils::OpenHandle(value); + // TODO(v8): This should happen in WasmTableObject::Set. + if (index >= v8_table->current_length()) return false; + + { v8::TryCatch handler(table->GetIsolate()); + v8::internal::WasmTableObject::Set(v8_table->GetIsolate(), v8_table, + static_cast(index), v8_value); + if (handler.HasCaught()) return false; + } + + return true; +} + +auto table_size(v8::Local table) -> size_t { + auto v8_object = v8::Utils::OpenHandle(table); + auto v8_table = v8::internal::Handle::cast(v8_object); + return v8_table->current_length(); +} + +auto table_grow( + v8::Local table, size_t delta, v8::Local init +) -> bool { + auto v8_object = v8::Utils::OpenHandle(table); + auto v8_table = v8::internal::Handle::cast(v8_object); + if (delta > 0xfffffffflu) return false; + auto old_size = v8_table->current_length(); + auto new_size = old_size + static_cast(delta); + // TODO(v8): This should happen in WasmTableObject::Grow. + if (new_size > table_type_max(table)) return false; + + { v8::TryCatch handler(table->GetIsolate()); + v8::internal::WasmTableObject::Grow( + v8_table->GetIsolate(), v8_table, static_cast(delta), + v8::Utils::OpenHandle(init)); + if (handler.HasCaught()) return false; + } + + return true; +} + + +// Memory + +auto memory_data(v8::Local memory) -> char* { + auto v8_object = v8::Utils::OpenHandle(memory); + auto v8_memory = v8::internal::Handle::cast(v8_object); + return reinterpret_cast(v8_memory->array_buffer().backing_store()); +} + +auto memory_data_size(v8::Local memory)-> size_t { + auto v8_object = v8::Utils::OpenHandle(memory); + auto v8_memory = v8::internal::Handle::cast(v8_object); + return v8_memory->array_buffer().byte_length(); +} + +auto memory_size(v8::Local memory) -> uint32_t { + auto v8_object = v8::Utils::OpenHandle(memory); + auto v8_memory = v8::internal::Handle::cast(v8_object); + return static_cast( + v8_memory->array_buffer().byte_length() / v8::internal::wasm::kWasmPageSize); +} + +auto memory_grow(v8::Local memory, uint32_t delta) -> bool { + auto v8_object = v8::Utils::OpenHandle(memory); + auto v8_memory = v8::internal::Handle::cast(v8_object); + auto old = v8::internal::WasmMemoryObject::Grow( + v8_memory->GetIsolate(), v8_memory, delta); + return old != -1; +} + +} // namespace wasm +} // namespace v8 diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-v8-lowlevel.hh b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-v8-lowlevel.hh new file mode 100644 index 0000000..5b2969f --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-v8-lowlevel.hh @@ -0,0 +1,79 @@ +#ifndef __WASM_V8_LOWLEVEL_HH +#define __WASM_V8_LOWLEVEL_HH + +#include "v8.h" + +namespace v8 { +namespace wasm { + +auto object_isolate(v8::Local) -> v8::Isolate*; +auto object_isolate(const v8::Persistent&) -> v8::Isolate*; + +auto object_is_module(v8::Local) -> bool; +auto object_is_instance(v8::Local) -> bool; +auto object_is_func(v8::Local) -> bool; +auto object_is_global(v8::Local) -> bool; +auto object_is_table(v8::Local) -> bool; +auto object_is_memory(v8::Local) -> bool; +auto object_is_error(v8::Local) -> bool; + +auto foreign_new(v8::Isolate*, void*) -> v8::Local; +auto foreign_get(v8::Local) -> void*; + +auto managed_new(v8::Isolate*, void*, void (*)(void*)) -> v8::Local; +auto managed_get(v8::Local) -> void*; + +enum val_kind_t { I32, I64, F32, F64, ANYREF = 128, FUNCREF }; +auto func_type_param_arity(v8::Local global) -> uint32_t; +auto func_type_result_arity(v8::Local global) -> uint32_t; +auto func_type_param(v8::Local global, size_t) -> val_kind_t; +auto func_type_result(v8::Local global, size_t) -> val_kind_t; + +auto global_type_content(v8::Local global) -> val_kind_t; +auto global_type_mutable(v8::Local global) -> bool; + +auto table_type_min(v8::Local table) -> uint32_t; +auto table_type_max(v8::Local table) -> uint32_t; + +auto memory_type_min(v8::Local memory) -> uint32_t; +auto memory_type_max(v8::Local memory) -> uint32_t; + +auto module_binary_size(v8::Local module) -> size_t; +auto module_binary(v8::Local module) -> const char*; +auto module_serialize_size(v8::Local module) -> size_t; +auto module_serialize(v8::Local module, char*, size_t) -> bool; +auto module_deserialize(v8::Isolate*, const char*, size_t, const char*, size_t) -> v8::MaybeLocal; + +auto instance_module(v8::Local instance) -> v8::Local; +auto instance_exports(v8::Local instance) -> v8::Local; + +enum extern_kind_t { EXTERN_FUNC, EXTERN_GLOBAL, EXTERN_TABLE, EXTERN_MEMORY }; +auto extern_kind(v8::Local external) -> extern_kind_t; + +auto func_instance(v8::Local) -> v8::Local; + +auto global_get_i32(v8::Local global) -> int32_t; +auto global_get_i64(v8::Local global) -> int64_t; +auto global_get_f32(v8::Local global) -> float; +auto global_get_f64(v8::Local global) -> double; +auto global_get_ref(v8::Local global) -> v8::Local; +void global_set_i32(v8::Local global, int32_t); +void global_set_i64(v8::Local global, int64_t); +void global_set_f32(v8::Local global, float); +void global_set_f64(v8::Local global, double); +void global_set_ref(v8::Local global, v8::Local); + +auto table_get(v8::Local table, size_t index) -> v8::MaybeLocal; +auto table_set(v8::Local table, size_t index, v8::Local) -> bool; +auto table_size(v8::Local table) -> size_t; +auto table_grow(v8::Local table, size_t delta, v8::Local) -> bool; + +auto memory_data(v8::Local memory) -> char*; +auto memory_data_size(v8::Local memory)-> size_t; +auto memory_size(v8::Local memory) -> uint32_t; +auto memory_grow(v8::Local memory, uint32_t delta) -> bool; + +} // namespace wasm +} // namespace v8 + +#endif // #define __WASM_V8_LOWLEVEL_HH diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-v8.cc b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-v8.cc new file mode 100644 index 0000000..b58aaf4 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm-c-api/src/wasm-v8.cc @@ -0,0 +1,2137 @@ +#include "wasm.hh" +#include "wasm-bin.hh" +#include "wasm-v8-lowlevel.hh" + +#include "v8.h" +#include "libplatform/libplatform.h" + +#include + +#ifdef WASM_API_DEBUG +#include +#endif + + +namespace wasm_v8 { + using namespace v8::wasm; +} + +namespace v8 { + namespace internal { + extern bool FLAG_expose_gc; + extern bool FLAG_experimental_wasm_bigint; + extern bool FLAG_experimental_wasm_mv; + extern bool FLAG_experimental_wasm_anyref; + extern bool FLAG_experimental_wasm_bulk_memory; + extern bool FLAG_experimental_wasm_return_call; + } +} + +namespace wasm { + +/////////////////////////////////////////////////////////////////////////////// +// Auxiliaries + +[[noreturn]] void UNIMPLEMENTED(const char* s) { + std::cerr << "Wasm API: " << s << " not supported yet!\n"; + exit(1); +} + +template +void ignore(T) {} + + +template struct implement; + +template +auto impl(C* x) -> typename implement ::type* { + return reinterpret_cast::type*>(x); +} + +template +auto impl(const C* x) -> const typename implement::type* { + return reinterpret_cast::type*>(x); +} + +template +auto seal(typename implement ::type* x) -> C* { + return reinterpret_cast(x); +} + +template +auto seal(const typename implement ::type* x) -> const C* { + return reinterpret_cast(x); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Debug aids + +struct Stats { + enum category_t { + BYTE, CONFIG, ENGINE, STORE, FRAME, + VALTYPE, FUNCTYPE, GLOBALTYPE, TABLETYPE, MEMORYTYPE, + EXTERNTYPE, IMPORTTYPE, EXPORTTYPE, + VAL, REF, TRAP, + MODULE, INSTANCE, FUNC, GLOBAL, TABLE, MEMORY, EXTERN, + STRONG_COUNT, + FUNCDATA_FUNCTYPE, FUNCDATA_VALTYPE, + CATEGORY_COUNT + }; + enum cardinality_t { + OWN, VEC, SHARED, CARDINALITY_COUNT + }; + +#ifdef WASM_API_DEBUG + static const char* name[STRONG_COUNT]; + static const char* left[CARDINALITY_COUNT]; + static const char* right[CARDINALITY_COUNT]; + + std::atomic made[CATEGORY_COUNT][CARDINALITY_COUNT]; + std::atomic freed[CATEGORY_COUNT][CARDINALITY_COUNT]; + + Stats() { + for (int i = 0; i < CATEGORY_COUNT; ++i) { + for (int j = 0; j < CARDINALITY_COUNT; ++j) { + made[i][j] = freed[i][j] = 0; + } + } + } + + ~Stats() { + // Hack for func data weakly owned by V8 heap. + freed[FUNCTYPE][OWN] += + made[FUNCDATA_FUNCTYPE][OWN] - freed[FUNCDATA_FUNCTYPE][OWN]; + freed[VALTYPE][OWN] += + made[FUNCDATA_VALTYPE][OWN] - freed[FUNCDATA_VALTYPE][OWN]; + freed[VALTYPE][VEC] += + made[FUNCDATA_VALTYPE][VEC] - freed[FUNCDATA_VALTYPE][VEC]; + // Hack for shared modules. + freed[BYTE][VEC] += made[MODULE][SHARED] - freed[MODULE][SHARED]; + + bool leak = false; + for (int i = 0; i < STRONG_COUNT; ++i) { + for (int j = 0; j < CARDINALITY_COUNT; ++j) { + assert(made[i][j] >= freed[i][j]); + auto live = made[i][j] - freed[i][j]; + if (live) { + std::cerr << "Leaked " << live << " instances of wasm::" + << left[j] << name[i] << right[j] + << ", made " << made[i][j] << ", freed " << freed[i][j] << "!" + << std::endl; + leak = true; + } + } + } + if (leak) exit(1); + } +#endif + + void make(category_t i, void* ptr, cardinality_t j = OWN, size_t n = 1) { +#ifdef WASM_API_DEBUG +#ifdef WASM_API_DEBUG_LOG + if (ptr) { + std::clog << "[make] " << ptr + << " wasm::" << left[j] << name[i] << right[j] << std::endl; + } +#endif + made[i][j] += n; +#endif + } + + void free(category_t i, void* ptr, cardinality_t j = OWN, size_t n = 1) { +#ifdef WASM_API_DEBUG +#ifdef WASM_API_DEBUG_LOG + if (ptr) { + std::clog << "[free] " << ptr + << " wasm::" << left[j] << name[i] << right[j] << std::endl; + } +#endif + freed[i][j] += n; + if (freed[i][j] > made[i][j]) { + std::cerr << "Deleting instance of wasm::" + << left[j] << name[i] << right[j] << " when none is alive" + << ", made " << made[i][j] << ", freed " << freed[i][j] << "!" + << std::endl; + exit(1); + } +#endif + } + + static category_t categorize(const v8::Persistent& pobj) { +#ifdef WASM_API_DEBUG + auto isolate = wasm_v8::object_isolate(pobj); + v8::HandleScope handle_scope(isolate); + auto obj = pobj.Get(isolate); + if (wasm_v8::object_is_func(obj)) return FUNC; + if (wasm_v8::object_is_global(obj)) return GLOBAL; + if (wasm_v8::object_is_table(obj)) return TABLE; + if (wasm_v8::object_is_memory(obj)) return MEMORY; + if (wasm_v8::object_is_module(obj)) return MODULE; + if (wasm_v8::object_is_instance(obj)) return INSTANCE; + if (wasm_v8::object_is_error(obj)) return TRAP; +#endif + return REF; + } +}; + +#ifdef WASM_API_DEBUG +const char* Stats::name[STRONG_COUNT] = { + "byte_t", "Config", "Engine", "Store", "Frame", + "ValType", "FuncType", "GlobalType", "TableType", "MemoryType", + "ExternType", "ImportType", "ExportType", + "Val", "Ref", "Trap", + "Module", "Instance", "Func", "Global", "Table", "Memory", "Extern" +}; + +const char* Stats::left[CARDINALITY_COUNT] = { + "", "vec<", "Shared<" +}; + +const char* Stats::right[CARDINALITY_COUNT] = { + "", ">", ">" +}; +#endif + + +Stats stats; + + +// Vectors + +#ifdef WASM_API_DEBUG + +#define DEFINE_VEC(type, vec, STAT) \ + template<> void vec::make_data() { \ + if (data_) stats.make(Stats::STAT, data_.get(), Stats::VEC); \ + } \ + \ + template<> void vec::free_data() { \ + if (data_) stats.free(Stats::STAT, data_.get(), Stats::VEC); \ + } + +DEFINE_VEC(byte_t, vec, BYTE) +DEFINE_VEC(Frame, ownvec, FRAME) +DEFINE_VEC(ValType, ownvec, VALTYPE) +DEFINE_VEC(FuncType, ownvec, FUNCTYPE) +DEFINE_VEC(GlobalType, ownvec, GLOBALTYPE) +DEFINE_VEC(TableType, ownvec, TABLETYPE) +DEFINE_VEC(MemoryType, ownvec, MEMORYTYPE) +DEFINE_VEC(ExternType, ownvec, EXTERNTYPE) +DEFINE_VEC(ImportType, ownvec, IMPORTTYPE) +DEFINE_VEC(ExportType, ownvec, EXPORTTYPE) +DEFINE_VEC(Ref, ownvec, REF) +DEFINE_VEC(Trap, ownvec, TRAP) +DEFINE_VEC(Module, ownvec, MODULE) +DEFINE_VEC(Instance, ownvec, INSTANCE) +DEFINE_VEC(Func, ownvec, FUNC) +DEFINE_VEC(Global, ownvec, GLOBAL) +DEFINE_VEC(Table, ownvec, TABLE) +DEFINE_VEC(Memory, ownvec, MEMORY) +DEFINE_VEC(Extern, ownvec, EXTERN) +DEFINE_VEC(Extern*, vec, EXTERN) +DEFINE_VEC(Val, vec, VAL) + +#endif // #ifdef WASM_API_DEBUG + + +/////////////////////////////////////////////////////////////////////////////// +// Runtime Environment + +// Configuration + +struct ConfigImpl { + ConfigImpl() { stats.make(Stats::CONFIG, this); } + ~ConfigImpl() { stats.free(Stats::CONFIG, this); } +}; + +template<> struct implement { using type = ConfigImpl; }; + + +Config::~Config() { + impl(this)->~ConfigImpl(); +} + +void Config::operator delete(void *p) { + ::operator delete(p); +} + +auto Config::make() -> own { + return own(seal(new(std::nothrow) ConfigImpl())); +} + + +// Engine + +struct EngineImpl { + static bool created; + + std::unique_ptr platform; + + EngineImpl() { + assert(!created); + created = true; + stats.make(Stats::ENGINE, this); + } + + ~EngineImpl() { + v8::V8::Dispose(); + v8::V8::ShutdownPlatform(); + stats.free(Stats::ENGINE, this); + } +}; + +bool EngineImpl::created = false; + +template<> struct implement { using type = EngineImpl; }; + + +Engine::~Engine() { + impl(this)->~EngineImpl(); +} + +void Engine::operator delete(void *p) { + ::operator delete(p); +} + +auto Engine::make(own&& config) -> own { + v8::internal::FLAG_expose_gc = true; + v8::internal::FLAG_experimental_wasm_bigint = true; + v8::internal::FLAG_experimental_wasm_mv = true; + v8::internal::FLAG_experimental_wasm_anyref = true; + v8::internal::FLAG_experimental_wasm_bulk_memory = true; + v8::internal::FLAG_experimental_wasm_return_call = true; + // v8::V8::SetFlagsFromCommandLine(&argc, const_cast(argv), false); + auto engine = new(std::nothrow) EngineImpl; + if (!engine) return own(); + // v8::V8::InitializeICUDefaultLocation(argv[0]); + // v8::V8::InitializeExternalStartupData(argv[0]); + engine->platform = v8::platform::NewDefaultPlatform(); + v8::V8::InitializePlatform(engine->platform.get()); + v8::V8::Initialize(); + return make_own(seal(engine)); +} + + +// Stores + +enum v8_string_t { + V8_S_EMPTY, + V8_S_I32, V8_S_I64, V8_S_F32, V8_S_F64, V8_S_ANYREF, V8_S_ANYFUNC, + V8_S_VALUE, V8_S_MUTABLE, V8_S_ELEMENT, V8_S_MINIMUM, V8_S_MAXIMUM, + V8_S_COUNT +}; + +enum v8_symbol_t { + V8_Y_CALLBACK, V8_Y_ENV, + V8_Y_COUNT +}; + +enum v8_function_t { + V8_F_WEAKMAP, V8_F_WEAKMAP_PROTO, V8_F_WEAKMAP_GET, V8_F_WEAKMAP_SET, + V8_F_MODULE, V8_F_GLOBAL, V8_F_TABLE, V8_F_MEMORY, + V8_F_INSTANCE, V8_F_VALIDATE, + V8_F_COUNT, +}; + +class StoreImpl { + friend own Store::make(Engine*); + + v8::Isolate::CreateParams create_params_; + v8::Isolate *isolate_; + v8::Eternal context_; + v8::Eternal strings_[V8_S_COUNT]; + v8::Eternal symbols_[V8_Y_COUNT]; + v8::Eternal functions_[V8_F_COUNT]; + v8::Eternal host_data_map_; + v8::Eternal callback_symbol_; + v8::Persistent* handle_pool_ = nullptr; // TODO: use v8::Value + +public: + StoreImpl() { + stats.make(Stats::STORE, this); + } + + ~StoreImpl() { +#ifdef WASM_API_DEBUG + isolate_->RequestGarbageCollectionForTesting( + v8::Isolate::kFullGarbageCollection); +#endif + { + v8::HandleScope scope(isolate_); + while (handle_pool_ != nullptr) { + auto handle = handle_pool_; + handle_pool_ = reinterpret_cast*>( + wasm_v8::foreign_get(handle->Get(isolate_))); + delete handle; + } + } + context()->Exit(); + isolate_->Exit(); + isolate_->Dispose(); + delete create_params_.array_buffer_allocator; + stats.free(Stats::STORE, this); + } + + auto isolate() const -> v8::Isolate* { + return isolate_; + } + + auto context() const -> v8::Local { + return context_.Get(isolate_); + } + + auto v8_string(v8_string_t i) const -> v8::Local { + return strings_[i].Get(isolate_); + } + auto v8_string(v8_symbol_t i) const -> v8::Local { + return symbols_[i].Get(isolate_); + } + auto v8_function(v8_function_t i) const -> v8::Local { + return functions_[i].Get(isolate_); + } + + auto host_data_map() const -> v8::Local { + return host_data_map_.Get(isolate_); + } + + static auto get(v8::Isolate* isolate) -> StoreImpl* { + return static_cast(isolate->GetData(0)); + } + + auto make_handle() -> v8::Persistent* { + if (handle_pool_ == nullptr) { + static const size_t n = 100; + for (size_t i = 0; i < n; ++i) { + auto v8_next = wasm_v8::foreign_new(isolate_, handle_pool_); + handle_pool_ = new(std::nothrow) v8::Persistent(); + if (!handle_pool_) return nullptr; + handle_pool_->Reset(isolate_, v8::Local::Cast(v8_next)); + } + } + auto handle = handle_pool_; + handle_pool_ = reinterpret_cast*>( + wasm_v8::foreign_get(handle->Get(isolate_))); + return handle; + } + + void free_handle(v8::Persistent* handle) { + // TODO: shrink pool? + auto next = wasm_v8::foreign_new(isolate_, handle_pool_); + handle->Reset(isolate_, v8::Local::Cast(next)); + handle_pool_ = handle; + } +}; + +template<> struct implement { using type = StoreImpl; }; + + +Store::~Store() { + impl(this)->~StoreImpl(); +} + +void Store::operator delete(void *p) { + ::operator delete(p); +} + +auto Store::make(Engine*) -> own { + auto store = make_own(new(std::nothrow) StoreImpl()); + if (!store) return own(); + + // Create isolate. + store->create_params_.array_buffer_allocator = + v8::ArrayBuffer::Allocator::NewDefaultAllocator(); + auto isolate = v8::Isolate::New(store->create_params_); + if (!isolate) return own(); + + { + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + + // Create context. + auto context = v8::Context::New(isolate); + if (context.IsEmpty()) return own(); + v8::Context::Scope context_scope(context); + + store->isolate_ = isolate; + store->context_ = v8::Eternal(isolate, context); + + // Create strings. + static const char* const raw_strings[V8_S_COUNT] = { + "", + "i32", "i64", "f32", "f64", "anyref", "anyfunc", + "value", "mutable", "element", "initial", "maximum", + }; + for (int i = 0; i < V8_S_COUNT; ++i) { + auto maybe = v8::String::NewFromUtf8(isolate, raw_strings[i], + v8::NewStringType::kNormal); + if (maybe.IsEmpty()) return own(); + auto string = maybe.ToLocalChecked(); + store->strings_[i] = v8::Eternal(isolate, string); + } + + for (int i = 0; i < V8_Y_COUNT; ++i) { + auto symbol = v8::Symbol::New(isolate); + store->symbols_[i] = v8::Eternal(isolate, symbol); + } + + // Extract functions. + auto global = context->Global(); + auto maybe_wasm_name = v8::String::NewFromUtf8(isolate, "WebAssembly", + v8::NewStringType::kNormal); + if (maybe_wasm_name.IsEmpty()) return own(); + auto wasm_name = maybe_wasm_name.ToLocalChecked(); + auto maybe_wasm = global->Get(context, wasm_name); + if (maybe_wasm.IsEmpty()) return own(); + auto wasm = v8::Local::Cast(maybe_wasm.ToLocalChecked()); + v8::Local weakmap; + v8::Local weakmap_proto; + + struct { + const char* name; + v8::Local* carrier; + } raw_functions[V8_F_COUNT] = { + {"WeakMap", &global}, {"prototype", &weakmap}, + {"get", &weakmap_proto}, {"set", &weakmap_proto}, + {"Module", &wasm}, {"Global", &wasm}, {"Table", &wasm}, {"Memory", &wasm}, + {"Instance", &wasm}, {"validate", &wasm}, + }; + for (int i = 0; i < V8_F_COUNT; ++i) { + auto maybe_name = v8::String::NewFromUtf8(isolate, raw_functions[i].name, + v8::NewStringType::kNormal); + if (maybe_name.IsEmpty()) return own(); + auto name = maybe_name.ToLocalChecked(); + assert(!raw_functions[i].carrier->IsEmpty()); + // TODO(wasm+): remove + if ((*raw_functions[i].carrier)->IsUndefined()) continue; + auto maybe_obj = (*raw_functions[i].carrier)->Get(context, name); + if (maybe_obj.IsEmpty()) return own(); + auto obj = v8::Local::Cast(maybe_obj.ToLocalChecked()); + if (i == V8_F_WEAKMAP_PROTO) { + assert(obj->IsObject()); + weakmap_proto = obj; + } else { + assert(obj->IsFunction()); + auto function = v8::Local::Cast(obj); + store->functions_[i] = v8::Eternal(isolate, function); + if (i == V8_F_WEAKMAP) weakmap = function; + } + } + + // Create host data weak map. + v8::Local empty_args[] = {}; + auto maybe_weakmap = + store->v8_function(V8_F_WEAKMAP)->NewInstance(context, 0, empty_args); + if (maybe_weakmap.IsEmpty()) return own(); + auto map = v8::Local::Cast(maybe_weakmap.ToLocalChecked()); + assert(map->IsWeakMap()); + store->host_data_map_ = v8::Eternal(isolate, map); + } + + store->isolate()->Enter(); + store->context()->Enter(); + isolate->SetData(0, store.get()); + + return make_own(seal(store.release())); +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Type Representations + +// Value Types + +struct ValTypeImpl { + ValKind kind; + + ValTypeImpl(ValKind kind) : kind(kind) {} +}; + +template<> struct implement { using type = ValTypeImpl; }; + +ValTypeImpl* valtype_i32 = new ValTypeImpl(ValKind::I32); +ValTypeImpl* valtype_i64 = new ValTypeImpl(ValKind::I64); +ValTypeImpl* valtype_f32 = new ValTypeImpl(ValKind::F32); +ValTypeImpl* valtype_f64 = new ValTypeImpl(ValKind::F64); +ValTypeImpl* valtype_anyref = new ValTypeImpl(ValKind::ANYREF); +ValTypeImpl* valtype_funcref = new ValTypeImpl(ValKind::FUNCREF); + + +ValType::~ValType() { + stats.free(Stats::VALTYPE, this); +} + +void ValType::operator delete(void*) {} + +auto ValType::make(ValKind k) -> own { + ValTypeImpl* valtype; + switch (k) { + case ValKind::I32: valtype = valtype_i32; break; + case ValKind::I64: valtype = valtype_i64; break; + case ValKind::F32: valtype = valtype_f32; break; + case ValKind::F64: valtype = valtype_f64; break; + case ValKind::ANYREF: valtype = valtype_anyref; break; + case ValKind::FUNCREF: valtype = valtype_funcref; break; + default: + // TODO(wasm+): support new value types + assert(false); + }; + auto result = seal(valtype); + stats.make(Stats::VALTYPE, result); + return own(result); +} + +auto ValType::copy() const -> own { + return make(kind()); +} + +auto ValType::kind() const -> ValKind { + return impl(this)->kind; +} + + +// Extern Types + +struct ExternTypeImpl { + ExternKind kind; + + explicit ExternTypeImpl(ExternKind kind) : kind(kind) {} + virtual ~ExternTypeImpl() {} +}; + +template<> struct implement { using type = ExternTypeImpl; }; + + +ExternType::~ExternType() { + impl(this)->~ExternTypeImpl(); +} + +void ExternType::operator delete(void *p) { + ::operator delete(p); +} + +auto ExternType::copy() const -> own { + switch (kind()) { + case ExternKind::FUNC: return func()->copy(); + case ExternKind::GLOBAL: return global()->copy(); + case ExternKind::TABLE: return table()->copy(); + case ExternKind::MEMORY: return memory()->copy(); + } +} + +auto ExternType::kind() const -> ExternKind { + return impl(this)->kind; +} + + +// Function Types + +struct FuncTypeImpl : ExternTypeImpl { + ownvec params; + ownvec results; + + FuncTypeImpl(ownvec& params, ownvec& results) : + ExternTypeImpl(ExternKind::FUNC), + params(std::move(params)), results(std::move(results)) + { + stats.make(Stats::FUNCTYPE, this); + } + + ~FuncTypeImpl() { + stats.free(Stats::FUNCTYPE, this); + } +}; + +template<> struct implement { using type = FuncTypeImpl; }; + + +FuncType::~FuncType() {} + +auto FuncType::make(ownvec&& params, ownvec&& results) + -> own { + return params && results + ? own( + seal(new(std::nothrow) FuncTypeImpl(params, results))) + : own(); +} + +auto FuncType::copy() const -> own { + return make(params().deep_copy(), results().deep_copy()); +} + +auto FuncType::params() const -> const ownvec& { + return impl(this)->params; +} + +auto FuncType::results() const -> const ownvec& { + return impl(this)->results; +} + + +auto ExternType::func() -> FuncType* { + return kind() == ExternKind::FUNC + ? seal(static_cast(impl(this))) + : nullptr; +} + +auto ExternType::func() const -> const FuncType* { + return kind() == ExternKind::FUNC + ? seal(static_cast(impl(this))) + : nullptr; +} + + +// Global Types + +struct GlobalTypeImpl : ExternTypeImpl { + own content; + Mutability mutability; + + GlobalTypeImpl(own& content, Mutability mutability) : + ExternTypeImpl(ExternKind::GLOBAL), + content(std::move(content)), mutability(mutability) + { + stats.make(Stats::GLOBALTYPE, this); + } + + ~GlobalTypeImpl() { + stats.free(Stats::GLOBALTYPE, this); + } +}; + +template<> struct implement { using type = GlobalTypeImpl; }; + + +GlobalType::~GlobalType() {} + +auto GlobalType::make( + own&& content, Mutability mutability +) -> own { + return content + ? own( + seal(new(std::nothrow) GlobalTypeImpl(content, mutability))) + : own(); +} + +auto GlobalType::copy() const -> own { + return make(content()->copy(), mutability()); +} + +auto GlobalType::content() const -> const ValType* { + return impl(this)->content.get(); +} + +auto GlobalType::mutability() const -> Mutability { + return impl(this)->mutability; +} + + +auto ExternType::global() -> GlobalType* { + return kind() == ExternKind::GLOBAL + ? seal(static_cast(impl(this))) + : nullptr; +} + +auto ExternType::global() const -> const GlobalType* { + return kind() == ExternKind::GLOBAL + ? seal(static_cast(impl(this))) + : nullptr; +} + + +// Table Types + +struct TableTypeImpl : ExternTypeImpl { + own element; + Limits limits; + + TableTypeImpl(own& element, Limits limits) : + ExternTypeImpl(ExternKind::TABLE), element(std::move(element)), limits(limits) + { + stats.make(Stats::TABLETYPE, this); + } + + ~TableTypeImpl() { + stats.free(Stats::TABLETYPE, this); + } +}; + +template<> struct implement { using type = TableTypeImpl; }; + + +TableType::~TableType() {} + +auto TableType::make(own&& element, Limits limits) -> own { + return element + ? own( + seal(new(std::nothrow) TableTypeImpl(element, limits))) + : own(); +} + +auto TableType::copy() const -> own { + return make(element()->copy(), limits()); +} + +auto TableType::element() const -> const ValType* { + return impl(this)->element.get(); +} + +auto TableType::limits() const -> const Limits& { + return impl(this)->limits; +} + + +auto ExternType::table() -> TableType* { + return kind() == ExternKind::TABLE + ? seal(static_cast(impl(this))) + : nullptr; +} + +auto ExternType::table() const -> const TableType* { + return kind() == ExternKind::TABLE + ? seal(static_cast(impl(this))) + : nullptr; +} + + +// Memory Types + +struct MemoryTypeImpl : ExternTypeImpl { + Limits limits; + + MemoryTypeImpl(Limits limits) : + ExternTypeImpl(ExternKind::MEMORY), limits(limits) + { + stats.make(Stats::MEMORYTYPE, this); + } + + ~MemoryTypeImpl() { + stats.free(Stats::MEMORYTYPE, this); + } +}; + +template<> struct implement { using type = MemoryTypeImpl; }; + + +MemoryType::~MemoryType() {} + +auto MemoryType::make(Limits limits) -> own { + return own( + seal(new(std::nothrow) MemoryTypeImpl(limits))); +} + +auto MemoryType::copy() const -> own { + return MemoryType::make(limits()); +} + +auto MemoryType::limits() const -> const Limits& { + return impl(this)->limits; +} + + +auto ExternType::memory() -> MemoryType* { + return kind() == ExternKind::MEMORY + ? seal(static_cast(impl(this))) + : nullptr; +} + +auto ExternType::memory() const -> const MemoryType* { + return kind() == ExternKind::MEMORY + ? seal(static_cast(impl(this))) + : nullptr; +} + + +// Import Types + +struct ImportTypeImpl { + Name module; + Name name; + own type; + + ImportTypeImpl(Name& module, Name& name, own& type) : + module(std::move(module)), name(std::move(name)), type(std::move(type)) + { + stats.make(Stats::IMPORTTYPE, this); + } + + ~ImportTypeImpl() { + stats.free(Stats::IMPORTTYPE, this); + } +}; + +template<> struct implement { using type = ImportTypeImpl; }; + + +ImportType::~ImportType() { + impl(this)->~ImportTypeImpl(); +} + +void ImportType::operator delete(void *p) { + ::operator delete(p); +} + +auto ImportType::make( + Name&& module, Name&& name, own&& type +) -> own { + return module && name && type + ? own( + seal(new(std::nothrow) ImportTypeImpl(module, name, type))) + : own(); +} + +auto ImportType::copy() const -> own { + return make(module().copy(), name().copy(), type()->copy()); +} + +auto ImportType::module() const -> const Name& { + return impl(this)->module; +} + +auto ImportType::name() const -> const Name& { + return impl(this)->name; +} + +auto ImportType::type() const -> const ExternType* { + return impl(this)->type.get(); +} + + +// Export Types + +struct ExportTypeImpl { + Name name; + own type; + + ExportTypeImpl(Name& name, own& type) : + name(std::move(name)), type(std::move(type)) + { + stats.make(Stats::EXPORTTYPE, this); + } + + ~ExportTypeImpl() { + stats.free(Stats::EXPORTTYPE, this); + } +}; + +template<> struct implement { using type = ExportTypeImpl; }; + + +ExportType::~ExportType() { + impl(this)->~ExportTypeImpl(); +} + +void ExportType::operator delete(void *p) { + ::operator delete(p); +} + +auto ExportType::make( + Name&& name, own&& type +) -> own { + return name && type + ? own( + seal(new(std::nothrow) ExportTypeImpl(name, type))) + : own(); +} + +auto ExportType::copy() const -> own { + return make(name().copy(), type()->copy()); +} + +auto ExportType::name() const -> const Name& { + return impl(this)->name; +} + +auto ExportType::type() const -> const ExternType* { + return impl(this)->type.get(); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Conversions of types from and to V8 objects + +// Types + +auto valtype_to_v8( + StoreImpl* store, const ValType* type +) -> v8::Local { + v8_string_t string; + switch (type->kind()) { + case ValKind::I32: string = V8_S_I32; break; + case ValKind::I64: string = V8_S_I64; break; + case ValKind::F32: string = V8_S_F32; break; + case ValKind::F64: string = V8_S_F64; break; + case ValKind::ANYREF: string = V8_S_ANYREF; break; + case ValKind::FUNCREF: string = V8_S_ANYFUNC; break; + default: + // TODO(wasm+): support new value types + assert(false); + } + return store->v8_string(string); +} + +auto mutability_to_v8( + StoreImpl* store, Mutability mutability +) -> v8::Local { + return v8::Boolean::New(store->isolate(), mutability == Mutability::VAR); +} + +void limits_to_v8(StoreImpl* store, Limits limits, v8::Local desc) { + auto isolate = store->isolate(); + auto context = store->context(); + ignore(desc->DefineOwnProperty(context, store->v8_string(V8_S_MINIMUM), + v8::Integer::NewFromUnsigned(isolate, limits.min))); + if (limits.max != Limits(0).max) { + ignore(desc->DefineOwnProperty(context, store->v8_string(V8_S_MAXIMUM), + v8::Integer::NewFromUnsigned(isolate, limits.max))); + } +} + +auto globaltype_to_v8( + StoreImpl* store, const GlobalType* type +) -> v8::Local { + auto isolate = store->isolate(); + auto context = store->context(); + auto desc = v8::Object::New(isolate); + ignore(desc->DefineOwnProperty(context, store->v8_string(V8_S_VALUE), + valtype_to_v8(store, type->content()))); + ignore(desc->DefineOwnProperty(context, store->v8_string(V8_S_MUTABLE), + mutability_to_v8(store, type->mutability()))); + return desc; +} + +auto tabletype_to_v8( + StoreImpl* store, const TableType* type +) -> v8::Local { + auto isolate = store->isolate(); + auto context = store->context(); + auto desc = v8::Object::New(isolate); + ignore(desc->DefineOwnProperty(context, store->v8_string(V8_S_ELEMENT), + valtype_to_v8(store, type->element()))); + limits_to_v8(store, type->limits(), desc); + return desc; +} + +auto memorytype_to_v8( + StoreImpl* store, const MemoryType* type +) -> v8::Local { + auto isolate = store->isolate(); + auto desc = v8::Object::New(isolate); + limits_to_v8(store, type->limits(), desc); + return desc; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Runtime Values + +// References + +template +class RefImpl : public v8::Persistent { +public: + RefImpl() = delete; + ~RefImpl() = delete; + + static auto make(StoreImpl* store, v8::Local obj) -> own { + static_assert(sizeof(RefImpl) == sizeof(v8::Persistent), + "incompatible object layout"); + auto self = static_cast(store->make_handle()); + if (!self) return nullptr; + self->Reset(store->isolate(), obj); + stats.make(Stats::categorize(*self), self); + return make_own(seal(self)); + } + + auto copy() const -> own { + v8::HandleScope handle_scope(isolate()); + return make(store(), v8_object()); + } + + auto store() const -> StoreImpl* { + return StoreImpl::get(isolate()); + } + + auto isolate() const -> v8::Isolate* { + return wasm_v8::object_isolate(*this); + } + + auto v8_object() const -> v8::Local { + return Get(isolate()); + } + + auto get_host_info() const -> void* { + v8::HandleScope handle_scope(isolate()); + auto store = this->store(); + + v8::Local args[] = { v8_object() }; + auto maybe_result = store->v8_function(V8_F_WEAKMAP_GET)->Call( + store->context(), store->host_data_map(), 1, args); + if (maybe_result.IsEmpty()) return nullptr; + return wasm_v8::managed_get(maybe_result.ToLocalChecked()); + } + + void set_host_info(void* info, void (*finalizer)(void*)) { + v8::HandleScope handle_scope(isolate()); + auto store = this->store(); + auto managed = wasm_v8::managed_new(store->isolate(), info, finalizer); + v8::Local args[] = { v8_object(), managed }; + auto maybe_result = store->v8_function(V8_F_WEAKMAP_SET)->Call( + store->context(), store->host_data_map(), 2, args); + if (maybe_result.IsEmpty()) return; + } +}; + +template<> struct implement { using type = RefImpl; }; + + +Ref::~Ref() { + stats.free(Stats::categorize(*impl(this)), this); + v8::HandleScope handle_scope(impl(this)->isolate()); + impl(this)->store()->free_handle(impl(this)); +} + +void Ref::operator delete(void *p) {} + +auto Ref::copy() const -> own { + return impl(this)->copy(); +} + +auto Ref::same(const Ref* that) const -> bool { + v8::HandleScope handle_scope(impl(this)->isolate()); + return impl(this)->v8_object()->SameValue(impl(that)->v8_object()); +} + +auto Ref::get_host_info() const -> void* { + return impl(this)->get_host_info(); +} + +void Ref::set_host_info(void* info, void (*finalizer)(void*)) { + impl(this)->set_host_info(info, finalizer); +} + + +// Value Conversion + +auto ref_to_v8(StoreImpl* store, const Ref* r) -> v8::Local { + if (r == nullptr) { + return v8::Null(store->isolate()); + } else { + return impl(r)->v8_object(); + } +} + +auto val_to_v8(StoreImpl* store, const Val& v) -> v8::Local { + auto isolate = store->isolate(); + switch (v.kind()) { + case ValKind::I32: return v8::Integer::NewFromUnsigned(isolate, v.i32()); + case ValKind::I64: return v8::BigInt::New(isolate, v.i64()); + case ValKind::F32: return v8::Number::New(isolate, v.f32()); + case ValKind::F64: return v8::Number::New(isolate, v.f64()); + case ValKind::ANYREF: + case ValKind::FUNCREF: + return ref_to_v8(store, v.ref()); + default: assert(false); + } +} + +auto v8_to_ref(StoreImpl* store, v8::Local value) -> own { + if (value->IsNull()) { + return nullptr; + } else if (value->IsObject()) { + return RefImpl::make(store, v8::Local::Cast(value)); + } else { + UNIMPLEMENTED("JS primitive ref value"); + } +} + +auto v8_to_val( + StoreImpl* store, v8::Local value, const ValType* t +) -> Val { + auto context = store->context(); + switch (t->kind()) { + case ValKind::I32: return Val(value->Int32Value(context).ToChecked()); + case ValKind::I64: { + auto bigint = value->ToBigInt(context).ToLocalChecked(); + return Val(bigint->Int64Value()); + } + case ValKind::F32: { + auto number = value->NumberValue(context).ToChecked(); + return Val(static_cast(number)); + } + case ValKind::F64: return Val(value->NumberValue(context).ToChecked()); + case ValKind::ANYREF: + case ValKind::FUNCREF: { + return Val(v8_to_ref(store, value)); + } + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// Runtime Objects + +// Frames + +struct FrameImpl { + FrameImpl( + own&& instance, uint32_t func_index, + size_t func_offset, size_t module_offset + ) : + instance(std::move(instance)), + func_index(func_index), + func_offset(func_offset), + module_offset(module_offset) + { + stats.make(Stats::FRAME, this); + } + + ~FrameImpl() { stats.free(Stats::FRAME, this); } + + own instance; + uint32_t func_index; + size_t func_offset; + size_t module_offset; +}; + +template<> struct implement { using type = FrameImpl; }; + + +Frame::~Frame() { + impl(this)->~FrameImpl(); +} + +void Frame::operator delete(void *p) { + ::operator delete(p); +} + +auto Frame::copy() const -> own { + auto self = impl(this); + return own(seal(new(std::nothrow) FrameImpl( + self->instance->copy(), self->func_index, self->func_offset, + self->module_offset))); +} + +auto Frame::instance() const -> Instance* { + return impl(this)->instance.get(); +} + +auto Frame::func_index() const -> uint32_t { + return impl(this)->func_index; +} + +auto Frame::func_offset() const -> size_t { + return impl(this)->func_offset; +} + +auto Frame::module_offset() const -> size_t { + return impl(this)->module_offset; +} + + +// Traps + +template<> struct implement { using type = RefImpl; }; + + +Trap::~Trap() {} + +auto Trap::copy() const -> own { + return impl(this)->copy(); +} + +auto Trap::make(Store* store_abs, const Message& message) -> own { + auto store = impl(store_abs); + v8::Isolate* isolate = store->isolate(); + v8::HandleScope handle_scope(isolate); + + auto maybe_string = v8::String::NewFromUtf8(isolate, message.get(), + v8::NewStringType::kNormal, message.size()); + if (maybe_string.IsEmpty()) return own(); + auto exception = v8::Exception::Error(maybe_string.ToLocalChecked()); + return RefImpl::make(store, v8::Local::Cast(exception)); +} + +auto Trap::message() const -> Message { + auto isolate = impl(this)->isolate(); + v8::HandleScope handle_scope(isolate); + + auto message = v8::Exception::CreateMessage(isolate, impl(this)->v8_object()); + v8::String::Utf8Value string(isolate, message->Get()); + return vec::make_nt(std::string(*string)); +} + +auto Trap::origin() const -> own { + // TODO(v8): implement + return own(nullptr); +} + +auto Trap::trace() const -> ownvec { + // TODO(v8): implement + return ownvec::make(); +} + + +// Foreign Objects + +template<> struct implement { using type = RefImpl; }; + + +Foreign::~Foreign() {} + +auto Foreign::copy() const -> own { + return impl(this)->copy(); +} + +auto Foreign::make(Store* store_abs) -> own { + auto store = impl(store_abs); + auto isolate = store->isolate(); + v8::HandleScope handle_scope(isolate); + + auto obj = v8::Object::New(isolate); + return RefImpl::make(store, obj); +} + + +// Modules + +template<> struct implement { using type = RefImpl; }; + + +Module::~Module() {} + +auto Module::copy() const -> own { + return impl(this)->copy(); +} + +auto Module::validate(Store* store_abs, const vec& binary) -> bool { + auto store = impl(store_abs); + v8::Isolate* isolate = store->isolate(); + v8::HandleScope handle_scope(isolate); + + auto array_buffer = v8::ArrayBuffer::New( + isolate, const_cast(binary.get()), binary.size()); + + v8::Local args[] = {array_buffer}; + auto result = store->v8_function(V8_F_VALIDATE)->Call( + store->context(), v8::Undefined(isolate), 1, args); + if (result.IsEmpty()) return false; + + return result.ToLocalChecked()->IsTrue(); +} + +auto Module::make(Store* store_abs, const vec& binary) -> own { + auto store = impl(store_abs); + auto isolate = store->isolate(); + auto context = store->context(); + v8::HandleScope handle_scope(isolate); + + auto array_buffer = v8::ArrayBuffer::New( + isolate, const_cast(binary.get()), binary.size()); + + v8::Local args[] = {array_buffer}; + auto maybe_obj = + store->v8_function(V8_F_MODULE)->NewInstance(context, 1, args); + if (maybe_obj.IsEmpty()) return nullptr; + return RefImpl::make(store, maybe_obj.ToLocalChecked()); +} + +auto Module::imports() const -> ownvec { + v8::HandleScope handle_scope(impl(this)->isolate()); + auto module = impl(this)->v8_object(); + auto binary = vec::adopt( + wasm_v8::module_binary_size(module), + const_cast(wasm_v8::module_binary(module)) + ); + auto imports = wasm::bin::imports(binary); + binary.release(); + return imports; + // return impl(this)->data->imports.copy(); +/* OBSOLETE? + auto store = module->store(); + auto isolate = store->isolate(); + auto context = store->context(); + v8::HandleScope handle_scope(isolate); + + v8::Local args[] = { module->v8_object() }; + auto result = store->v8_function(V8_F_IMPORTS)->Call( + context, v8::Undefined(isolate), 1, args); + if (result.IsEmpty()) return wasm_importtype_vec_empty(); + auto array = v8::Local::Cast(result.ToLocalChecked()); + size_t size = array->Length(); + + wasm_importtype_vec_t imports = wasm_importtype_vec_new_uninitialized(size); + for (size_t i = 0; i < size; ++i) { + auto desc = v8::Local::Cast(array->Get(i)); + auto module_str = v8::Local::Cast( + desc->Get(context, store->v8_string(V8_S_MODULE)).ToLocalChecked()); + auto name_str = v8::Local::Cast( + desc->Get(context, store->v8_string(V8_S_NAME)).ToLocalChecked()); + auto kind_str = v8::Local::Cast( + desc->Get(context, store->v8_string(V8_S_KIND)).ToLocalChecked()); + + auto type = wasm_externtype_new_from_v8_kind(store, kind_str); + auto module = wasm_byte_vec_new_from_v8_string(module_str); + auto name = wasm_byte_vec_new_from_v8_string(name_str); + imports.data[i] = wasm_importtype_new(module, name, type); + } + + return imports; +*/ +} + +auto Module::exports() const -> ownvec { + v8::HandleScope handle_scope(impl(this)->isolate()); + auto module = impl(this)->v8_object(); + auto binary = vec::adopt( + wasm_v8::module_binary_size(module), + const_cast(wasm_v8::module_binary(module)) + ); + auto exports = wasm::bin::exports(binary); + binary.release(); + return exports; + // return impl(this)->data->exports.copy(); +/* OBSOLETE? + auto store = module->store(); + auto isolate = store->isolate(); + auto context = store->context(); + v8::HandleScope handle_scope(isolate); + + v8::Local args[] = { module->v8_object() }; + auto result = store->v8_function(V8_F_EXPORTS)->Call( + context, v8::Undefined(isolate), 1, args); + if (result.IsEmpty()) return wasm_exporttype_vec_empty(); + auto array = v8::Local::Cast(result.ToLocalChecked()); + size_t size = array->Length(); + + wasm_exporttype_vec_t exports = wasm_exporttype_vec_new_uninitialized(size); + for (size_t i = 0; i < size; ++i) { + auto desc = v8::Local::Cast(array->Get(i)); + auto name_str = v8::Local::Cast( + desc->Get(context, store->v8_string(V8_S_NAME)).ToLocalChecked()); + auto kind_str = v8::Local::Cast( + desc->Get(context, store->v8_string(V8_S_KIND)).ToLocalChecked()); + + auto type = wasm_externtype_new_from_v8_kind(store, kind_str); + auto name = wasm_byte_vec_new_from_v8_string(name_str); + exports.data[i] = wasm_exporttype_new(name, type); + } + + return exports; +*/ +} + +auto Module::serialize() const -> vec { + v8::HandleScope handle_scope(impl(this)->isolate()); + auto module = impl(this)->v8_object(); + auto binary_size = wasm_v8::module_binary_size(module); + auto serial_size = wasm_v8::module_serialize_size(module); + auto size_size = wasm::bin::u64_size(binary_size); + auto buffer = vec::make_uninitialized( + size_size + binary_size + serial_size); + auto ptr = buffer.get(); + wasm::bin::encode_u64(ptr, binary_size); + std::memcpy(ptr, wasm_v8::module_binary(module), binary_size); + ptr += binary_size; + if (!wasm_v8::module_serialize(module, ptr, serial_size)) buffer.reset(); + return buffer; +} + +auto Module::deserialize(Store* store_abs, const vec& serialized) -> own { + auto store = impl(store_abs); + auto isolate = store->isolate(); + v8::HandleScope handle_scope(isolate); + auto ptr = serialized.get(); + auto binary_size = wasm::bin::u64(ptr); + auto size_size = ptr - serialized.get(); + auto serial_size = serialized.size() - size_size - binary_size; + auto maybe_obj = wasm_v8::module_deserialize( + isolate, ptr, binary_size, ptr + binary_size, serial_size); + if (maybe_obj.IsEmpty()) return nullptr; + return RefImpl::make(store, maybe_obj.ToLocalChecked()); +} + + +// TODO(v8): do better when V8 can do better. +template<> struct implement> { using type = vec; }; + +template<> +Shared::~Shared() { + stats.free(Stats::MODULE, this, Stats::SHARED); + impl(this)->~vec(); +} + +template<> +void Shared::operator delete(void* p) { + ::operator delete(p); +} + +auto Module::share() const -> own> { + auto shared = seal>(new vec(serialize())); + stats.make(Stats::MODULE, shared, Stats::SHARED); + return make_own(shared); +} + +auto Module::obtain(Store* store, const Shared* shared) -> own { + return Module::deserialize(store, *impl(shared)); +} + + + + +// Externals + +template<> struct implement { using type = RefImpl; }; + + +Extern::~Extern() {} + +auto Extern::copy() const -> own { + return impl(this)->copy(); +} + +auto Extern::kind() const -> ExternKind { + v8::HandleScope handle_scope(impl(this)->isolate()); + return static_cast(wasm_v8::extern_kind(impl(this)->v8_object())); +} + +auto Extern::type() const -> own { + switch (kind()) { + case ExternKind::FUNC: return func()->type(); + case ExternKind::GLOBAL: return global()->type(); + case ExternKind::TABLE: return table()->type(); + case ExternKind::MEMORY: return memory()->type(); + } +} + +auto Extern::func() -> Func* { + return kind() == ExternKind::FUNC ? static_cast(this) : nullptr; +} + +auto Extern::global() -> Global* { + return kind() == ExternKind::GLOBAL ? static_cast(this) : nullptr; +} + +auto Extern::table() -> Table* { + return kind() == ExternKind::TABLE ? static_cast(this) : nullptr; +} + +auto Extern::memory() -> Memory* { + return kind() == ExternKind::MEMORY ? static_cast(this) : nullptr; +} + +auto Extern::func() const -> const Func* { + return kind() == ExternKind::FUNC ? static_cast(this) : nullptr; +} + +auto Extern::global() const -> const Global* { + return kind() == ExternKind::GLOBAL ? static_cast(this) : nullptr; +} + +auto Extern::table() const -> const Table* { + return kind() == ExternKind::TABLE ? static_cast(this) : nullptr; +} + +auto Extern::memory() const -> const Memory* { + return kind() == ExternKind::MEMORY ? static_cast(this) : nullptr; +} + +auto extern_to_v8(const Extern* ex) -> v8::Local { + return impl(ex)->v8_object(); +} + + +// Function Instances + +template<> struct implement { using type = RefImpl; }; + + +Func::~Func() {} + +auto Func::copy() const -> own { + return impl(this)->copy(); +} + +struct FuncData { + Store* store; + own type; + enum Kind { CALLBACK, CALLBACK_WITH_ENV } kind; + union { + Func::callback callback; + Func::callback_with_env callback_with_env; + }; + void (*finalizer)(void*); + void* env; + + FuncData(Store* store, const FuncType* type, Kind kind) : + store(store), type(type->copy()), kind(kind), finalizer(nullptr) + { + stats.make(Stats::FUNCDATA_FUNCTYPE, nullptr); + stats.make(Stats::FUNCDATA_VALTYPE, nullptr, Stats::OWN, type->params().size()); + stats.make(Stats::FUNCDATA_VALTYPE, nullptr, Stats::OWN, type->results().size()); + if (type->params().get()) stats.make(Stats::FUNCDATA_VALTYPE, nullptr, Stats::VEC); + if (type->results().get()) stats.make(Stats::FUNCDATA_VALTYPE, nullptr, Stats::VEC); + } + + ~FuncData() { + stats.free(Stats::FUNCDATA_FUNCTYPE, nullptr); + stats.free(Stats::FUNCDATA_VALTYPE, nullptr, Stats::OWN, type->params().size()); + stats.free(Stats::FUNCDATA_VALTYPE, nullptr, Stats::OWN, type->results().size()); + if (type->params().get()) stats.free(Stats::FUNCDATA_VALTYPE, nullptr, Stats::VEC); + if (type->results().get()) stats.free(Stats::FUNCDATA_VALTYPE, nullptr, Stats::VEC); + if (finalizer) (*finalizer)(env); + } + + static void v8_callback(const v8::FunctionCallbackInfo&); + static void finalize_func_data(void* data); +}; + +namespace { + +auto make_func(Store* store_abs, FuncData* data) -> own { + auto store = impl(store_abs); + auto isolate = store->isolate(); + v8::HandleScope handle_scope(isolate); + auto context = store->context(); + + // Create V8 function + auto v8_data = wasm_v8::foreign_new(isolate, data); + auto function_template = v8::FunctionTemplate::New( + isolate, &FuncData::v8_callback, v8_data); + auto maybe_func_obj = function_template->GetFunction(context); + if (maybe_func_obj.IsEmpty()) return own(); + auto func_obj = maybe_func_obj.ToLocalChecked(); + + // Create wrapper instance + auto binary = wasm::bin::wrapper(data->type.get()); + auto module = Module::make(store_abs, binary); + + auto imports_obj = v8::Object::New(isolate); + auto module_obj = v8::Object::New(isolate); + auto str = store->v8_string(V8_S_EMPTY); + ignore(imports_obj->DefineOwnProperty(context, str, module_obj)); + ignore(module_obj->DefineOwnProperty(context, str, func_obj)); + + v8::Local instantiate_args[] = { + impl(module.get())->v8_object(), imports_obj + }; + auto instance_obj = store->v8_function(V8_F_INSTANCE)->NewInstance( + context, 2, instantiate_args).ToLocalChecked(); + assert(!instance_obj.IsEmpty()); + assert(instance_obj->IsObject()); + auto exports_obj = wasm_v8::instance_exports(instance_obj); + assert(!exports_obj.IsEmpty()); + assert(exports_obj->IsObject()); + auto wrapped_func_obj = v8::Local::Cast( + exports_obj->Get(context, str).ToLocalChecked()); + assert(!wrapped_func_obj.IsEmpty()); + assert(wrapped_func_obj->IsFunction()); + + auto func = RefImpl::make(store, wrapped_func_obj); + func->set_host_info(data, &FuncData::finalize_func_data); + return func; +} + +auto func_type(v8::Local v8_func) -> own { + // return impl(this)->data->type->copy(); + auto param_arity = wasm_v8::func_type_param_arity(v8_func); + auto result_arity = wasm_v8::func_type_result_arity(v8_func); + auto params = ownvec::make_uninitialized(param_arity); + auto results = ownvec::make_uninitialized(result_arity); + + for (size_t i = 0; i < params.size(); ++i) { + auto kind = static_cast(wasm_v8::func_type_param(v8_func, i)); + params[i] = ValType::make(kind); + } + for (size_t i = 0; i < results.size(); ++i) { + auto kind = static_cast(wasm_v8::func_type_result(v8_func, i)); + results[i] = ValType::make(kind); + } + + return FuncType::make(std::move(params), std::move(results)); +} + +} // namespace + +auto Func::make( + Store* store, const FuncType* type, Func::callback callback +) -> own { + auto data = new FuncData(store, type, FuncData::CALLBACK); + data->callback = callback; + return make_func(store, data); +} + +auto Func::make( + Store* store, const FuncType* type, + callback_with_env callback, void* env, void (*finalizer)(void*) +) -> own { + auto data = new FuncData(store, type, FuncData::CALLBACK_WITH_ENV); + data->callback_with_env = callback; + data->env = env; + data->finalizer = finalizer; + return make_func(store, data); +} + +auto Func::type() const -> own { + // return impl(this)->data->type->copy(); + v8::HandleScope handle_scope(impl(this)->isolate()); + return func_type(impl(this)->v8_object()); +} + +auto Func::param_arity() const -> size_t { + v8::HandleScope handle_scope(impl(this)->isolate()); + return wasm_v8::func_type_param_arity(impl(this)->v8_object()); +} + +auto Func::result_arity() const -> size_t { + v8::HandleScope handle_scope(impl(this)->isolate()); + return wasm_v8::func_type_result_arity(impl(this)->v8_object()); +} + +auto Func::call(const vec& args, vec& results) const -> own { + auto func = impl(this); + auto store = func->store(); + auto isolate = store->isolate(); + v8::HandleScope handle_scope(isolate); + + auto context = store->context(); + auto type = this->type(); + auto& param_types = type->params(); + auto& result_types = type->results(); + + // TODO: cache v8_args array per thread. + auto v8_args = std::unique_ptr[]>( + new(std::nothrow) v8::Local[param_types.size()]); + for (size_t i = 0; i < param_types.size(); ++i) { + assert(args[i].kind() == param_types[i]->kind()); + v8_args[i] = val_to_v8(store, args[i]); + } + + v8::TryCatch handler(isolate); + auto v8_function = v8::Local::Cast(func->v8_object()); + auto maybe_val = v8_function->Call( + context, v8::Undefined(isolate), param_types.size(), v8_args.get()); + + if (handler.HasCaught()) { + auto exception = handler.Exception(); + if (!exception->IsObject()) { + auto maybe_string = exception->ToString(store->context()); + auto string = maybe_string.IsEmpty() + ? store->v8_string(V8_S_EMPTY) : maybe_string.ToLocalChecked(); + exception = v8::Exception::Error(string); + } + return RefImpl::make(store, v8::Local::Cast(exception)); + } + + auto val = maybe_val.ToLocalChecked(); + if (result_types.size() == 0) { + assert(val->IsUndefined()); + } else if (result_types.size() == 1) { + assert(!val->IsUndefined()); + new (&results[0]) Val(v8_to_val(store, val, result_types[0].get())); + } else { + assert(val->IsArray()); + auto array = v8::Handle::Cast(val); + for (size_t i = 0; i < result_types.size(); ++i) { + auto maybe = array->Get(context, i); + assert(!maybe.IsEmpty()); + new (&results[i]) Val(v8_to_val( + store, maybe.ToLocalChecked(), result_types[i].get())); + } + } + return nullptr; +} + +void FuncData::v8_callback(const v8::FunctionCallbackInfo& info) { + auto v8_data = v8::Local::Cast(info.Data()); + auto self = reinterpret_cast(wasm_v8::foreign_get(v8_data)); + auto store = impl(self->store); + auto isolate = store->isolate(); + v8::HandleScope handle_scope(isolate); + + auto& param_types = self->type->params(); + auto& result_types = self->type->results(); + + assert(param_types.size() == info.Length()); + + // TODO: cache params and result arrays per thread. + auto args = vec::make_uninitialized(param_types.size()); + auto results = vec::make_uninitialized(result_types.size()); + for (size_t i = 0; i < param_types.size(); ++i) { + args[i] = v8_to_val(store, info[i], param_types[i].get()); + } + + own trap; + if (self->kind == CALLBACK_WITH_ENV) { + trap = self->callback_with_env(self->env, args, results); + } else { + trap = self->callback(args, results); + } + + if (trap) { + isolate->ThrowException(impl(trap.get())->v8_object()); + return; + } + + auto ret = info.GetReturnValue(); + if (result_types.size() == 0) { + ret.SetUndefined(); + } else if (result_types.size() == 1) { + assert(results[0].kind() == result_types[0]->kind()); + ret.Set(val_to_v8(store, results[0])); + } else { + auto context = store->context(); + auto array = v8::Array::New(isolate, result_types.size()); + for (size_t i = 0; i < result_types.size(); ++i) { + auto success = array->Set(context, i, val_to_v8(store, results[i])); + assert(success.IsJust() && success.ToChecked()); + } + ret.Set(array); + } +} + +void FuncData::finalize_func_data(void* data) { + delete reinterpret_cast(data); +} + + +// Global Instances + +template<> struct implement { using type = RefImpl; }; + + +Global::~Global() {} + +auto Global::copy() const -> own { + return impl(this)->copy(); +} + +auto Global::make( + Store* store_abs, const GlobalType* type, const Val& val +) -> own { + auto store = impl(store_abs); + auto isolate = store->isolate(); + v8::HandleScope handle_scope(isolate); + auto context = store->context(); + + assert(type->content()->kind() == val.kind()); + + // Create wrapper instance + auto binary = wasm::bin::wrapper(type); + auto module = Module::make(store_abs, binary); + + v8::Local instantiate_args[] = { impl(module.get())->v8_object() }; + auto instance_obj = store->v8_function(V8_F_INSTANCE)->NewInstance( + context, 1, instantiate_args).ToLocalChecked(); + auto exports_obj = wasm_v8::instance_exports(instance_obj); + auto obj = v8::Local::Cast( + exports_obj->Get(context, store->v8_string(V8_S_EMPTY)).ToLocalChecked()); + assert(!obj.IsEmpty() && obj->IsObject()); + + auto global = RefImpl::make(store, obj); + assert(global); + global->set(val); + return global; +} + +auto Global::type() const -> own { + // return impl(this)->data->type->copy(); + v8::HandleScope handle_scope(impl(this)->isolate()); + auto v8_global = impl(this)->v8_object(); + auto kind = static_cast(wasm_v8::global_type_content(v8_global)); + auto mutability = wasm_v8::global_type_mutable(v8_global) + ? Mutability::VAR : Mutability::CONST; + return GlobalType::make(ValType::make(kind), mutability); +} + +auto Global::get() const -> Val { + v8::HandleScope handle_scope(impl(this)->isolate()); + auto v8_global = impl(this)->v8_object(); + switch (type()->content()->kind()) { + case ValKind::I32: return Val(wasm_v8::global_get_i32(v8_global)); + case ValKind::I64: return Val(wasm_v8::global_get_i64(v8_global)); + case ValKind::F32: return Val(wasm_v8::global_get_f32(v8_global)); + case ValKind::F64: return Val(wasm_v8::global_get_f64(v8_global)); + case ValKind::ANYREF: + case ValKind::FUNCREF: { + auto store = impl(this)->store(); + return Val(v8_to_ref(store, wasm_v8::global_get_ref(v8_global))); + } + default: + assert(false); + } +} + +void Global::set(const Val& val) { + v8::HandleScope handle_scope(impl(this)->isolate()); + auto v8_global = impl(this)->v8_object(); + switch (val.kind()) { + case ValKind::I32: return wasm_v8::global_set_i32(v8_global, val.i32()); + case ValKind::I64: return wasm_v8::global_set_i64(v8_global, val.i64()); + case ValKind::F32: return wasm_v8::global_set_f32(v8_global, val.f32()); + case ValKind::F64: return wasm_v8::global_set_f64(v8_global, val.f64()); + case ValKind::ANYREF: + case ValKind::FUNCREF: { + auto store = impl(this)->store(); + return wasm_v8::global_set_ref(v8_global, ref_to_v8(store, val.ref())); + } + default: + assert(false); + } +} + + +// Table Instances + +template<> struct implement
{ using type = RefImpl
; }; + + +Table::~Table() {} + +auto Table::copy() const -> own
{ + return impl(this)->copy(); +} + +auto Table::make( + Store* store_abs, const TableType* type, const Ref* ref +) -> own
{ + auto store = impl(store_abs); + auto isolate = store->isolate(); + v8::HandleScope handle_scope(isolate); + auto context = store->context(); + + v8::Local init = v8::Null(isolate); + if (ref) init = impl(ref)->v8_object(); + v8::Local args[] = {tabletype_to_v8(store, type), init}; + auto maybe_obj = + store->v8_function(V8_F_TABLE)->NewInstance(context, 2, args); + if (maybe_obj.IsEmpty()) return own
(); + auto table = RefImpl
::make(store, maybe_obj.ToLocalChecked()); + // TODO(wasm+): pass reference initialiser as parameter + if (table && ref) { + auto size = type->limits().min; + auto obj = maybe_obj.ToLocalChecked(); + for (size_t i = 0; i < size; ++i) { + wasm_v8::table_set(obj, i, v8::Local::Cast(init)); + } + } + return table; +} + +auto Table::type() const -> own { + // return impl(this)->data->type->copy(); + v8::HandleScope handle_scope(impl(this)->isolate()); + auto v8_table = impl(this)->v8_object(); + uint32_t min = wasm_v8::table_type_min(v8_table); + uint32_t max = wasm_v8::table_type_max(v8_table); + // TODO(wasm+): support new element types. + return TableType::make(ValType::make(ValKind::FUNCREF), Limits(min, max)); +} + +auto Table::get(size_t index) const -> own { + v8::HandleScope handle_scope(impl(this)->isolate()); + auto maybe = wasm_v8::table_get(impl(this)->v8_object(), index); + if (maybe.IsEmpty()) return own(); + auto obj = v8::Local::Cast(maybe.ToLocalChecked()); + return v8_to_ref(impl(this)->store(), obj); +} + +auto Table::set(size_t index, const Ref* ref) -> bool { + v8::HandleScope handle_scope(impl(this)->isolate()); + auto val = ref_to_v8(impl(this)->store(), ref); + return wasm_v8::table_set(impl(this)->v8_object(), index, val); +} + +auto Table::size() const -> size_t { + v8::HandleScope handle_scope(impl(this)->isolate()); + return wasm_v8::table_size(impl(this)->v8_object()); +} + +auto Table::grow(size_t delta, const Ref* ref) -> bool { + v8::HandleScope handle_scope(impl(this)->isolate()); + auto val = ref_to_v8(impl(this)->store(), ref); + return wasm_v8::table_grow(impl(this)->v8_object(), delta, val); +} + + +// Memory Instances + +template<> struct implement { using type = RefImpl; }; + + +Memory::~Memory() {} + +auto Memory::copy() const -> own { + return impl(this)->copy(); +} + +auto Memory::make(Store* store_abs, const MemoryType* type) -> own { + auto store = impl(store_abs); + auto isolate = store->isolate(); + v8::HandleScope handle_scope(isolate); + auto context = store->context(); + + v8::Local args[] = { memorytype_to_v8(store, type) }; + auto maybe_obj = + store->v8_function(V8_F_MEMORY)->NewInstance(context, 1, args); + if (maybe_obj.IsEmpty()) return own(); + return RefImpl::make(store, maybe_obj.ToLocalChecked()); +} + +auto Memory::type() const -> own { + // return impl(this)->data->type->copy(); + v8::HandleScope handle_scope(impl(this)->isolate()); + auto v8_memory = impl(this)->v8_object(); + uint32_t min = wasm_v8::memory_type_min(v8_memory); + uint32_t max = wasm_v8::memory_type_max(v8_memory); + return MemoryType::make(Limits(min, max)); +} + +auto Memory::data() const -> byte_t* { + v8::HandleScope handle_scope(impl(this)->isolate()); + return wasm_v8::memory_data(impl(this)->v8_object()); +} + +auto Memory::data_size() const -> size_t { + v8::HandleScope handle_scope(impl(this)->isolate()); + return wasm_v8::memory_data_size(impl(this)->v8_object()); +} + +auto Memory::size() const -> pages_t { + v8::HandleScope handle_scope(impl(this)->isolate()); + return wasm_v8::memory_size(impl(this)->v8_object()); +} + +auto Memory::grow(pages_t delta) -> bool { + v8::HandleScope handle_scope(impl(this)->isolate()); + return wasm_v8::memory_grow(impl(this)->v8_object(), delta); +} + + +// Module Instances + +template<> struct implement { using type = RefImpl; }; + + +Instance::~Instance() {} + +auto Instance::copy() const -> own { + return impl(this)->copy(); +} + +auto Instance::make( + Store* store_abs, const Module* module_abs, const vec& imports, + own* trap +) -> own { + auto store = impl(store_abs); + auto module = impl(module_abs); + auto isolate = store->isolate(); + auto context = store->context(); + v8::HandleScope handle_scope(isolate); + + assert(wasm_v8::object_isolate(module->v8_object()) == isolate); + + if (trap) *trap = nullptr; + auto import_types = module_abs->imports(); + auto imports_obj = v8::Object::New(isolate); + for (size_t i = 0; i < import_types.size(); ++i) { + auto type = import_types[i].get(); + auto maybe_module = v8::String::NewFromOneByte( + isolate, reinterpret_cast(type->module().get()), + v8::NewStringType::kNormal, type->module().size() + ); + if (maybe_module.IsEmpty()) return own(); + auto module_str = maybe_module.ToLocalChecked(); + auto maybe_name = v8::String::NewFromOneByte( + isolate, reinterpret_cast(type->name().get()), + v8::NewStringType::kNormal, type->name().size() + ); + if (maybe_name.IsEmpty()) return own(); + auto name_str = maybe_name.ToLocalChecked(); + + v8::Local module_obj; + if (imports_obj->HasOwnProperty(context, module_str).ToChecked()) { + module_obj = v8::Local::Cast( + imports_obj->Get(context, module_str).ToLocalChecked()); + } else { + module_obj = v8::Object::New(isolate); + ignore(imports_obj->DefineOwnProperty(context, module_str, module_obj)); + } + + ignore(module_obj->DefineOwnProperty( + context, name_str, extern_to_v8(imports[i]))); + } + + v8::TryCatch handler(isolate); + v8::Local instantiate_args[] = {module->v8_object(), imports_obj}; + auto obj = store->v8_function(V8_F_INSTANCE)->NewInstance( + context, 2, instantiate_args).ToLocalChecked(); + + if (handler.HasCaught() && trap) { + auto exception = handler.Exception(); + if (!exception->IsObject()) { + auto maybe_string = exception->ToString(store->context()); + auto string = maybe_string.IsEmpty() + ? store->v8_string(V8_S_EMPTY) : maybe_string.ToLocalChecked(); + exception = v8::Exception::Error(string); + } + *trap = RefImpl::make(store, v8::Local::Cast(exception)); + return nullptr; + } + + return RefImpl::make(store, obj); +} + +auto Instance::exports() const -> ownvec { + auto instance = impl(this); + auto store = instance->store(); + auto isolate = store->isolate(); + auto context = store->context(); + v8::HandleScope handle_scope(isolate); + + auto module_obj = wasm_v8::instance_module(instance->v8_object()); + auto exports_obj = wasm_v8::instance_exports(instance->v8_object()); + assert(!module_obj.IsEmpty() && module_obj->IsObject()); + assert(!exports_obj.IsEmpty() && exports_obj->IsObject()); + + auto module = RefImpl::make(store, module_obj); + auto export_types = module->exports(); + auto exports = ownvec::make_uninitialized(export_types.size()); + if (!exports) return ownvec::invalid(); + + for (size_t i = 0; i < export_types.size(); ++i) { + auto& name = export_types[i]->name(); + auto maybe_name_obj = v8::String::NewFromUtf8(isolate, name.get(), + v8::NewStringType::kNormal, name.size()); + if (maybe_name_obj.IsEmpty()) return ownvec::invalid(); + auto name_obj = maybe_name_obj.ToLocalChecked(); + auto obj = v8::Local::Cast( + exports_obj->Get(context, name_obj).ToLocalChecked()); + + auto type = export_types[i]->type(); + switch (type->kind()) { + case ExternKind::FUNC: { + assert(wasm_v8::extern_kind(obj) == wasm_v8::EXTERN_FUNC); + exports[i] = RefImpl::make(store, obj); + } break; + case ExternKind::GLOBAL: { + assert(wasm_v8::extern_kind(obj) == wasm_v8::EXTERN_GLOBAL); + exports[i] = RefImpl::make(store, obj); + } break; + case ExternKind::TABLE: { + assert(wasm_v8::extern_kind(obj) == wasm_v8::EXTERN_TABLE); + exports[i] = RefImpl
::make(store, obj); + } break; + case ExternKind::MEMORY: { + assert(wasm_v8::extern_kind(obj) == wasm_v8::EXTERN_MEMORY); + exports[i] = RefImpl::make(store, obj); + } break; + } + } + + return exports; +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace wasm diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm.h b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm.h new file mode 100644 index 0000000..1582dd0 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasm.h @@ -0,0 +1,50 @@ +// This header file is used only for test purposes! It is used by unit +// test inside the `src/` directory for the moment. + +#ifndef TEST_WASM +#define TEST_WASM + +#include "../wasm.h" +#include "../wasmer.h" +#include +#include + +#if defined(_WIN32) || defined(_WIN64) +#define strtok_r strtok_s +#endif + +wasm_engine_t *wasm_engine_new() { + wasm_config_t *config = wasm_config_new(); + + char *wasmer_test_compiler = getenv("WASMER_CAPI_CONFIG"); + char *wasmer_test_engine; + + strtok_r(wasmer_test_compiler, "-", &wasmer_test_engine); + printf("Using compiler: %s, engine: %s\n", wasmer_test_compiler, + wasmer_test_engine); + if (strcmp(wasmer_test_compiler, "cranelift") == 0) { + assert(wasmer_is_compiler_available(CRANELIFT)); + wasm_config_set_compiler(config, CRANELIFT); + } else if (strcmp(wasmer_test_compiler, "llvm") == 0) { + assert(wasmer_is_compiler_available(LLVM)); + wasm_config_set_compiler(config, LLVM); + } else if (strcmp(wasmer_test_compiler, "singlepass") == 0) { + assert(wasmer_is_compiler_available(SINGLEPASS)); + wasm_config_set_compiler(config, SINGLEPASS); + } else if (wasmer_test_compiler) { + printf("Compiler %s not recognized\n", wasmer_test_compiler); + abort(); + } + if (strcmp(wasmer_test_engine, "universal") == 0) { + assert(wasmer_is_engine_available(UNIVERSAL)); + wasm_config_set_engine(config, UNIVERSAL); + } else if (wasmer_test_engine) { + printf("Engine %s not recognized\n", wasmer_test_engine); + abort(); + } + + wasm_engine_t *engine = wasm_engine_new_with_config(config); + return engine; +} + +#endif diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasmer-c-api-test-runner/Cargo.toml b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasmer-c-api-test-runner/Cargo.toml new file mode 100644 index 0000000..e359694 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasmer-c-api-test-runner/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "wasmer-c-api-test-runner" +version = "4.2.8" +edition = "2021" +license = "MIT" +description = "wasmer-c-api-test-runner" + +[dependencies] +cc = "1.0" +target-lexicon = "0.11" +regex = "1.6" +walkdir = "2.3.2" \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasmer-c-api-test-runner/src/lib.rs b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasmer-c-api-test-runner/src/lib.rs new file mode 100644 index 0000000..ffa6482 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasmer-c-api-test-runner/src/lib.rs @@ -0,0 +1,451 @@ +#[cfg(test)] +use std::error::Error; +#[cfg(test)] +use std::process::Stdio; + +#[cfg(test)] +static INCLUDE_REGEX: &str = "#include \"(.*)\""; + +#[derive(Debug)] +pub struct Config { + pub wasmer_dir: String, + pub root_dir: String, +} + +impl Config { + pub fn get() -> Config { + let mut config = Config { + wasmer_dir: std::env::var("WASMER_DIR").unwrap_or_default(), + root_dir: std::env::var("ROOT_DIR").unwrap_or_default(), + }; + + let wasmer_base_dir = find_wasmer_base_dir(); + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + + if config.wasmer_dir.is_empty() { + println!("manifest dir = {manifest_dir}, wasmer root dir = {wasmer_base_dir}"); + config.wasmer_dir = wasmer_base_dir.clone() + "/package"; + assert!(std::path::Path::new(&config.wasmer_dir).exists()); + } + if config.root_dir.is_empty() { + config.root_dir = wasmer_base_dir + "/lib/c-api/tests"; + } + + config + } +} + +fn find_wasmer_base_dir() -> String { + let wasmer_base_dir = env!("CARGO_MANIFEST_DIR"); + let mut path2 = wasmer_base_dir.split("wasmer").collect::>(); + path2.pop(); + let mut wasmer_base_dir = path2.join("wasmer"); + + if wasmer_base_dir.contains("wasmer/lib/c-api") { + wasmer_base_dir = wasmer_base_dir + .split("wasmer/lib/c-api") + .next() + .unwrap() + .to_string() + + "wasmer"; + } else if wasmer_base_dir.contains("wasmer\\lib\\c-api") { + wasmer_base_dir = wasmer_base_dir + .split("wasmer\\lib\\c-api") + .next() + .unwrap() + .to_string() + + "wasmer"; + } + + wasmer_base_dir +} + +#[derive(Default)] +pub struct RemoveTestsOnDrop {} + +impl Drop for RemoveTestsOnDrop { + fn drop(&mut self) { + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + for entry in std::fs::read_dir(manifest_dir).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + let extension = path.extension().and_then(|s| s.to_str()); + if extension == Some("obj") || extension == Some("exe") || extension == Some("o") { + println!("removing {}", path.display()); + let _ = std::fs::remove_file(&path); + } + } + if let Some(parent) = std::path::Path::new(&manifest_dir).parent() { + for entry in std::fs::read_dir(parent).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + let extension = path.extension().and_then(|s| s.to_str()); + if extension == Some("obj") || extension == Some("exe") || extension == Some("o") { + println!("removing {}", path.display()); + let _ = std::fs::remove_file(&path); + } + } + } + } +} + +#[cfg(test)] +pub const CAPI_BASE_TESTS: &[&str] = &[ + "wasm-c-api/example/callback", + "wasm-c-api/example/memory", + "wasm-c-api/example/start", + "wasm-c-api/example/global", + "wasm-c-api/example/reflect", + "wasm-c-api/example/trap", + "wasm-c-api/example/hello", + "wasm-c-api/example/serialize", + "wasm-c-api/example/multi", +]; + +#[allow(unused_variables, dead_code)] +pub const CAPI_BASE_TESTS_NOT_WORKING: &[&str] = &[ + "wasm-c-api/example/finalize", + "wasm-c-api/example/hostref", + "wasm-c-api/example/threads", + "wasm-c-api/example/table", +]; + +// Runs all the tests that are working in the /c directory +#[test] +fn test_ok() { + let _drop = RemoveTestsOnDrop::default(); + let config = Config::get(); + println!("config: {:#?}", config); + + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let host = target_lexicon::HOST.to_string(); + let target = &host; + + let wasmer_dll_dir = format!("{}/lib", config.wasmer_dir); + let libwasmer_so_path = format!("{}/lib/libwasmer.so", config.wasmer_dir); + let exe_dir = format!("{manifest_dir}/../wasm-c-api/example"); + let path = std::env::var("PATH").unwrap_or_default(); + let newpath = format!("{};{path}", wasmer_dll_dir.replace('/', "\\")); + + if target.contains("msvc") { + for test in CAPI_BASE_TESTS.iter() { + let mut build = cc::Build::new(); + let build = build + .cargo_metadata(false) + .warnings(true) + .static_crt(true) + .extra_warnings(true) + .warnings_into_errors(false) + .debug(true) + .host(&host) + .target(target) + .opt_level(1); + + let compiler = build.try_get_compiler().unwrap(); + + println!("compiler {:#?}", compiler); + + // run vcvars + let vcvars_bat_path = find_vcvars64(&compiler).expect("no vcvars64.bat"); + let mut vcvars = std::process::Command::new("cmd"); + vcvars.arg("/C"); + vcvars.arg(vcvars_bat_path); + println!("running {vcvars:?}"); + + // cmd /C vcvars64.bat + let output = vcvars + .output() + .expect("could not invoke vcvars64.bat at {vcvars_bat_path}"); + + if !output.status.success() { + println!(); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + // print_wasmer_root_to_stdout(&config); + panic!("failed to invoke vcvars64.bat {test}"); + } + + let mut command = compiler.to_command(); + + command.arg(&format!("{manifest_dir}/../{test}.c")); + if !config.wasmer_dir.is_empty() { + command.arg("/I"); + command.arg(&format!("{}/wasm-c-api/include/", config.root_dir)); + command.arg("/I"); + command.arg(&format!("{}/include/", config.wasmer_dir)); + let mut log = String::new(); + fixup_symlinks( + &[ + format!("{}/include/", config.wasmer_dir), + format!("{}/wasm-c-api/include/", config.root_dir), + config.root_dir.to_string(), + ], + &mut log, + &config.root_dir, + ) + .unwrap_or_else(|_| panic!("failed to fix symlinks: {log}")); + println!("{log}"); + } + command.arg("/link"); + if !config.wasmer_dir.is_empty() { + command.arg(&format!("/LIBPATH:{}/lib", config.wasmer_dir)); + command.arg(&format!("{}/lib/wasmer.dll.lib", config.wasmer_dir)); + } + command.arg(&format!("/OUT:{manifest_dir}/../{test}.exe")); + + println!("compiling {test}: {command:?}"); + + // compile + let output = command + .output() + .unwrap_or_else(|_| panic!("failed to compile {command:#?}")); + if !output.status.success() { + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + println!("output: {:#?}", output); + // print_wasmer_root_to_stdout(&config); + panic!("failed to compile {test}"); + } + + if std::path::Path::new(&format!("{manifest_dir}/../{test}.exe")).exists() { + println!("exe does not exist"); + } + + // execute + let mut command = std::process::Command::new(&format!("{manifest_dir}/../{test}.exe")); + println!("newpath: {}", newpath.clone()); + command.env("PATH", newpath.clone()); + command.current_dir(exe_dir.clone()); + println!("executing {test}: {command:?}"); + println!("setting current dir = {exe_dir}"); + let output = command + .output() + .unwrap_or_else(|_| panic!("failed to run {command:#?}")); + if !output.status.success() { + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + println!("output: {:#?}", output); + // print_wasmer_root_to_stdout(&config); + panic!("failed to execute {test}"); + } + + // cc -g -IC:/Users/felix/Development/wasmer/lib/c-api/tests/ + // -IC:/Users/felix/Development/wasmer/package/include + // + // -Wl,-rpath,C:/Users/felix/Development/wasmer/package/lib + // + // wasm-c-api/example/callback.c + // + // -LC:/Users/felix/Development/wasmer/package/lib -lwasmer + // + // -o wasm-c-api/example/callback + } + } else { + for test in CAPI_BASE_TESTS.iter() { + let compiler_cmd = match std::process::Command::new("cc").output() { + Ok(_) => "cc", + Err(_) => "gcc", + }; + let mut command = std::process::Command::new(compiler_cmd); + + if !config.wasmer_dir.is_empty() { + command.arg("-I"); + command.arg(&format!("{}/wasm-c-api/include/", config.root_dir)); + command.arg("-I"); + command.arg(&format!("{}/include/", config.wasmer_dir)); + let mut log = String::new(); + fixup_symlinks( + &[ + format!("{}/include/", config.wasmer_dir), + format!("{}/wasm-c-api/include/", config.root_dir), + config.root_dir.to_string(), + ], + &mut log, + &config.root_dir, + ) + .unwrap_or_else(|_| panic!("failed to fix symlinks: {log}")); + } + command.arg(&format!("{manifest_dir}/../{test}.c")); + if !config.wasmer_dir.is_empty() { + command.arg("-L"); + command.arg(&format!("{}/lib/", config.wasmer_dir)); + command.arg("-lwasmer"); + command.arg(&format!("-Wl,-rpath,{}/lib/", config.wasmer_dir)); + } + command.arg("-o"); + command.arg(&format!("{manifest_dir}/../{test}")); + + // print_wasmer_root_to_stdout(&config); + + println!("compile: {command:#?}"); + // compile + let output = command + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .current_dir(find_wasmer_base_dir()) + .output() + .unwrap_or_else(|_| panic!("failed to compile {command:#?}")); + if !output.status.success() { + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + // print_wasmer_root_to_stdout(&config); + panic!("failed to compile {test}: {command:#?}"); + } + + // execute + let mut command = std::process::Command::new(&format!("{manifest_dir}/../{test}")); + command.env("LD_PRELOAD", libwasmer_so_path.clone()); + command.current_dir(exe_dir.clone()); + println!("execute: {command:#?}"); + let output = command + .output() + .unwrap_or_else(|_| panic!("failed to run {command:#?}")); + if !output.status.success() { + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + // print_wasmer_root_to_stdout(&config); + panic!("failed to execute {test}: {command:#?}"); + } + } + } + + for test in CAPI_BASE_TESTS.iter() { + let _ = std::fs::remove_file(&format!("{manifest_dir}/{test}.obj")); + let _ = std::fs::remove_file(&format!("{manifest_dir}/../{test}.exe")); + let _ = std::fs::remove_file(&format!("{manifest_dir}/../{test}")); + } +} + +#[cfg(test)] +fn print_wasmer_root_to_stdout(config: &Config) { + println!("print_wasmer_root_to_stdout"); + + use walkdir::WalkDir; + + println!( + "wasmer dir: {}", + std::path::Path::new(&config.wasmer_dir) + .canonicalize() + .unwrap() + .display() + ); + + for entry in WalkDir::new(&config.wasmer_dir) + .into_iter() + .filter_map(Result::ok) + { + let f_name = String::from(entry.path().canonicalize().unwrap().to_string_lossy()); + println!("{f_name}"); + } + + println!( + "root dir: {}", + std::path::Path::new(&config.root_dir) + .canonicalize() + .unwrap() + .display() + ); + + for entry in WalkDir::new(&config.root_dir) + .into_iter() + .filter_map(Result::ok) + { + let f_name = String::from(entry.path().canonicalize().unwrap().to_string_lossy()); + println!("{f_name}"); + } + + println!("printed"); +} + +#[cfg(test)] +fn fixup_symlinks( + include_paths: &[String], + log: &mut String, + root_dir: &str, +) -> Result<(), Box> { + let source = std::path::Path::new(root_dir) + .join("lib") + .join("c-api") + .join("tests") + .join("wasm-c-api") + .join("include") + .join("wasm.h"); + let target = std::path::Path::new(root_dir) + .join("lib") + .join("c-api") + .join("tests") + .join("wasm.h"); + println!("copying {} -> {}", source.display(), target.display()); + let _ = std::fs::copy(source, target); + + log.push_str(&format!("include paths: {include_paths:?}")); + for i in include_paths { + let i = i.replacen("-I", "", 1); + let i = i.replacen("/I", "", 1); + let mut paths_headers = Vec::new(); + let readdir = match std::fs::read_dir(&i) { + Ok(o) => o, + Err(_) => continue, + }; + for entry in readdir { + let entry = entry?; + let path = entry.path(); + let path_display = format!("{}", path.display()); + if path_display.ends_with('h') { + paths_headers.push(path_display); + } + } + fixup_symlinks_inner(&paths_headers, log)?; + } + + Ok(()) +} + +#[cfg(test)] +fn fixup_symlinks_inner(include_paths: &[String], log: &mut String) -> Result<(), Box> { + log.push_str(&format!("fixup symlinks: {include_paths:#?}")); + let regex = regex::Regex::new(INCLUDE_REGEX).unwrap(); + for path in include_paths.iter() { + let file = match std::fs::read_to_string(path) { + Ok(o) => o, + _ => continue, + }; + let lines_3 = file.lines().take(3).collect::>(); + log.push_str(&format!("first 3 lines of {path:?}: {:#?}\n", lines_3)); + + let parent = std::path::Path::new(&path).parent().unwrap(); + if let Ok(symlink) = std::fs::read_to_string(parent.join(&file)) { + log.push_str(&format!("symlinking {path:?}\n")); + std::fs::write(path, symlink)?; + } + + // follow #include directives and recurse + let filepaths = regex + .captures_iter(&file) + .map(|c| c[1].to_string()) + .collect::>(); + log.push_str(&format!("regex captures: ({path:?}): {:#?}\n", filepaths)); + let joined_filepaths = filepaths + .iter() + .map(|s| { + let path = parent.join(s); + format!("{}", path.display()) + }) + .collect::>(); + fixup_symlinks_inner(&joined_filepaths, log)?; + } + Ok(()) +} + +#[cfg(test)] +fn find_vcvars64(compiler: &cc::Tool) -> Option { + if !compiler.is_like_msvc() { + return None; + } + + let path = compiler.path(); + let path = format!("{}", path.display()); + let split = path.split("VC").next()?; + + Some(format!("{split}VC\\Auxiliary\\Build\\vcvars64.bat")) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasmer.h b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasmer.h new file mode 100644 index 0000000..3247ece --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/tests/wasmer.h @@ -0,0 +1,25 @@ +// This header file is used only for test purposes! It is used by unit +// test inside the `src/` directory for the moment. + +#ifndef TEST_WASMER +#define TEST_WASMER + +#include "../wasmer.h" +#include "wasm.h" +#include +#include + +// Assert that a `wasm_name_t` equals something. +void wasmer_assert_name(const wasm_name_t *name, const char *expected) { + assert(name->size == strlen(expected) && + strncmp(name->data, expected, name->size) == 0); +} + +// Helper to quickly create a `wasm_byte_vec_t` from a string, à la +// `wasm_name_new_from_string`. +static inline void wasmer_byte_vec_new_from_string(wasm_byte_vec_t *out, + const char *s) { + wasm_byte_vec_new(out, strlen(s), s); +} + +#endif /* TEST_WASMER */ diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/wasm.h b/arbitrator/tools/module_roots/wasmer/lib/c-api/wasm.h new file mode 120000 index 0000000..14a5311 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/wasm.h @@ -0,0 +1 @@ +tests/wasm-c-api/include/wasm.h \ No newline at end of file diff --git a/arbitrator/tools/module_roots/wasmer/lib/c-api/wasmer_wasm.h b/arbitrator/tools/module_roots/wasmer/lib/c-api/wasmer_wasm.h new file mode 100644 index 0000000..d41ff3c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/c-api/wasmer_wasm.h @@ -0,0 +1,3 @@ +#include "wasmer.h" + +#pragma message "The wasmer_wasm.h header file is being deprecated, please use wasmer.h instead." diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/Cargo.toml b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/Cargo.toml new file mode 100644 index 0000000..3539f06 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/Cargo.toml @@ -0,0 +1,83 @@ +[package] +name = "wasmer-compiler-cli" +description = "Wasmer Compiler CLI" +categories = ["wasm", "command-line-interface"] +keywords = ["wasm", "webassembly", "cli"] +readme = "README.md" +default-run = "wasmer-compiler" +build = "build.rs" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[[bin]] +name = "wasmer-compiler" +path = "src/bin/wasmer_compiler.rs" +doc = false + +[dependencies] +wasmer-compiler = { version = "=4.2.8", path = "../compiler", features = ["compiler"] } +wasmer-types = { version = "=4.2.8", path = "../types" } +is-terminal = "0.4.7" +colored = "2.0" +anyhow = "1.0" +# For the function names autosuggestion +distance = "0.4" +# For the inspect subcommand +bytesize = "1.0" +cfg-if = "1.0" +# For debug feature +fern = { version = "0.6", features = ["colored"], optional = true } +log = { version = "0.4", optional = true } +target-lexicon = { version = "0.12", features = ["std"] } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +wasmer-compiler-singlepass = { version = "=4.2.8", path = "../compiler-singlepass", optional = true } +wasmer-compiler-cranelift = { version = "=4.2.8", path = "../compiler-cranelift", optional = true } +clap = { version = "4.2.8", features = ["derive", "env"] } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasmer-compiler-singlepass = { version = "=4.2.8", path = "../compiler-singlepass", optional = true, default-features = false, features = ["wasm"] } +wasmer-compiler-cranelift = { version = "=4.2.8", path = "../compiler-cranelift", optional = true, default-features = false, features = ["wasm"] } +# NOTE: Must use different features for clap because the "color" feature does not +# work on wasi, due to the anstream dependency not compiling. +clap = { version = "4.2.8", default-features = false, features = [ + "std", + "help", + "usage", + "error-context", + "suggestions", + "derive", + "env", +]} + +[target.'cfg(target_os = "linux")'.dependencies] +unix_mode = "0.1.3" + +[features] +# Don't add the compiler features in default, please add them on the Makefile +# since we might want to autoconfigure them depending on the availability on the host. +default = [] +engine = [] +compiler = [ + "wasmer-compiler/translator", + "wasmer-compiler/compiler", +] +singlepass = [ + "wasmer-compiler-singlepass", + "compiler", +] +cranelift = [ + "wasmer-compiler-cranelift", + "compiler", +] +debug = ["fern", "log"] +disable-all-logging = [] +jit = [] + +[package.metadata.docs.rs] +rustc-args = ["--cfg", "docsrs"] diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/README.md b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/README.md new file mode 100644 index 0000000..0b53d49 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/README.md @@ -0,0 +1,31 @@ +# `wasmer-cli-compiler` [![Build Status](https://github.com/wasmerio/wasmer/workflows/build/badge.svg?style=flat-square)](https://github.com/wasmerio/wasmer/actions?query=workflow%3Abuild) [![Join Wasmer Slack](https://img.shields.io/static/v1?label=Slack&message=join%20chat&color=brighgreen&style=flat-square)](https://slack.wasmer.io) [![MIT License](https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square)](https://github.com/wasmerio/wasmer/blob/master/LICENSE) + +This crate is the Wasmer Compiler only CLI. + + +## Features + +The Compiler only Wasmer supports the following features: +* `wasi` (default): support for [WASI]. +* `emscripten` (default): support for [Emscripten]. +* `singlepass`: support for the [Singlepass compiler]. + +[WASI]: https://github.com/wasmerio/wasmer/tree/master/lib/wasi/ +[Emscripten]: https://github.com/wasmerio/wasmer/tree/master/lib/emscripten/ +[Singlepass compiler]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-singlepass/ + +## CLI commands + +Once you have Wasmer installed, you can start executing WebAssembly files easily: + +Get the current Wasmer version: + +```bash +wasmer-compiler -V +``` + +Compile a WebAssembly file: + +```bash +wasmer-compiler compile myfile.wasm -o myfile.wasmu --singlepass +``` diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/build.rs b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/build.rs new file mode 100644 index 0000000..36fe647 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/build.rs @@ -0,0 +1,4 @@ +pub fn main() { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-env-changed=WASMER_INSTALL_PREFIX"); +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/bin/wasmer_compiler.rs b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/bin/wasmer_compiler.rs new file mode 100644 index 0000000..ba0f2e1 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/bin/wasmer_compiler.rs @@ -0,0 +1,8 @@ +use wasmer_compiler_cli::cli::wasmer_main; + +#[cfg(feature = "run")] +compile_error!("Cannot enable run with the compile-only build"); + +fn main() { + wasmer_main(); +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/cli.rs b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/cli.rs new file mode 100644 index 0000000..c67e2df --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/cli.rs @@ -0,0 +1,75 @@ +//! The logic for the Wasmer CLI tool. + +use crate::commands::Compile; +use crate::commands::{Config, Validate}; +use crate::error::PrettyError; +use anyhow::Result; + +use clap::{error::ErrorKind, Parser}; + +#[derive(Parser)] +#[clap( + name = "wasmer-compiler", + about = "WebAssembly standalone Compiler.", + author +)] +/// The options for the wasmer Command Line Interface +enum WasmerCLIOptions { + /// Validate a WebAssembly binary + #[clap(name = "validate")] + Validate(Validate), + + /// Compile a WebAssembly binary + #[clap(name = "compile")] + Compile(Compile), + + /// Get various configuration information needed + /// to compile programs which use Wasmer + #[clap(name = "config")] + Config(Config), +} + +impl WasmerCLIOptions { + fn execute(&self) -> Result<()> { + match self { + Self::Validate(validate) => validate.execute(), + Self::Compile(compile) => compile.execute(), + Self::Config(config) => config.execute(), + } + } +} + +/// The main function for the Wasmer CLI tool. +pub fn wasmer_main() { + // We allow windows to print properly colors + #[cfg(windows)] + colored::control::set_virtual_terminal(true).unwrap(); + + // We try to run wasmer with the normal arguments. + // Eg. `wasmer ` + // In case that fails, we fallback trying the Run subcommand directly. + // Eg. `wasmer myfile.wasm --dir=.` + // + // In case we've been run as wasmer-binfmt-interpreter myfile.wasm args, + // we assume that we're registered via binfmt_misc + let args = std::env::args().collect::>(); + let command = args.get(1); + let options = { + match command.unwrap_or(&"".to_string()).as_ref() { + "compile" | "config" | "help" | "inspect" | "validate" => WasmerCLIOptions::parse(), + _ => { + WasmerCLIOptions::try_parse_from(args.iter()).unwrap_or_else(|e| { + match e.kind() { + // This fixes a issue that: + // 1. Shows the version twice when doing `wasmer -V` + // 2. Shows the run help (instead of normal help) when doing `wasmer --help` + ErrorKind::DisplayVersion | ErrorKind::DisplayHelp => e.exit(), + _ => WasmerCLIOptions::Compile(Compile::parse()), + } + }) + } + } + }; + + PrettyError::report(options.execute()); +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/commands.rs b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/commands.rs new file mode 100644 index 0000000..7441883 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/commands.rs @@ -0,0 +1,7 @@ +//! The commands available in the Wasmer binary. +mod compile; +mod config; +mod validate; + +pub use compile::*; +pub use {config::*, validate::*}; diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/commands/compile.rs b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/commands/compile.rs new file mode 100644 index 0000000..9ebccc9 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/commands/compile.rs @@ -0,0 +1,118 @@ +use crate::store::StoreOptions; +use crate::warning; +use anyhow::{Context, Result}; +use clap::Parser; +use std::fs; +use std::path::{Path, PathBuf}; +use wasmer_compiler::{ArtifactBuild, ArtifactCreate, ModuleEnvironment}; +use wasmer_types::entity::PrimaryMap; +use wasmer_types::{ + Architecture, CompileError, CpuFeature, MemoryIndex, MemoryStyle, TableIndex, TableStyle, + Target, Triple, +}; + +#[derive(Debug, Parser)] +/// The options for the `wasmer compile` subcommand +pub struct Compile { + /// Input file + #[clap(name = "FILE")] + path: PathBuf, + + /// Output file + #[clap(name = "OUTPUT PATH", short = 'o')] + output: PathBuf, + + /// Compilation Target triple + #[clap(long = "target")] + target_triple: Option, + + #[clap(flatten)] + store: StoreOptions, + + #[clap(short = 'm')] + cpu_features: Vec, +} + +impl Compile { + /// Runs logic for the `compile` subcommand + pub fn execute(&self) -> Result<()> { + self.inner_execute() + .context(format!("failed to compile `{}`", self.path.display())) + } + + fn inner_execute(&self) -> Result<()> { + let target = self + .target_triple + .as_ref() + .map(|target_triple| { + let mut features = self + .cpu_features + .clone() + .into_iter() + .fold(CpuFeature::set(), |a, b| a | b); + // Cranelift requires SSE2, so we have this "hack" for now to facilitate + // usage + if target_triple.architecture == Architecture::X86_64 { + features |= CpuFeature::SSE2; + } + Target::new(target_triple.clone(), features) + }) + .unwrap_or_default(); + let (engine_builder, compiler_type) = self.store.get_engine_for_target(target.clone())?; + let engine = engine_builder.engine(); + let output_filename = self + .output + .file_stem() + .map(|osstr| osstr.to_string_lossy().to_string()) + .unwrap_or_default(); + // `.wasmu` is the default extension for all the triples. It + // stands for “Wasm Universal”. + let recommended_extension = "wasmu"; + match self.output.extension() { + Some(ext) => { + if ext != recommended_extension { + warning!("the output file has a wrong extension. We recommend using `{}.{}` for the chosen target", &output_filename, &recommended_extension) + } + } + None => { + warning!("the output file has no extension. We recommend using `{}.{}` for the chosen target", &output_filename, &recommended_extension) + } + } + let tunables = self.store.get_tunables_for_target(&target)?; + + println!("Compiler: {}", compiler_type.to_string()); + println!("Target: {}", target.triple()); + + // compile and save the artifact (without using module from api) + let path: &Path = self.path.as_ref(); + let wasm_bytes = std::fs::read(path)?; + let environ = ModuleEnvironment::new(); + let translation = environ.translate(&wasm_bytes).map_err(CompileError::Wasm)?; + let module = translation.module; + let memory_styles: PrimaryMap = module + .memories + .values() + .map(|memory_type| tunables.memory_style(memory_type)) + .collect(); + let table_styles: PrimaryMap = module + .tables + .values() + .map(|table_type| tunables.table_style(table_type)) + .collect(); + let artifact = ArtifactBuild::new( + &mut engine.inner_mut(), + &wasm_bytes, + &target, + memory_styles, + table_styles, + )?; + let serialized = artifact.serialize()?; + fs::write(output_filename, serialized)?; + eprintln!( + "✔ File compiled successfully to `{}`.", + self.output.display(), + ); + + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/commands/config.rs b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/commands/config.rs new file mode 100644 index 0000000..155fb0c --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/commands/config.rs @@ -0,0 +1,107 @@ +use crate::VERSION; +use anyhow::{Context, Result}; +use clap::Parser; +use std::env; +use std::path::PathBuf; + +#[derive(Debug, Parser)] +/// The options for the `wasmer config` subcommand +pub struct Config { + /// Print the installation prefix. + #[clap(long, conflicts_with = "pkg_config")] + prefix: bool, + + /// Directory containing Wasmer executables. + #[clap(long, conflicts_with = "pkg_config")] + bindir: bool, + + /// Directory containing Wasmer headers. + #[clap(long, conflicts_with = "pkg_config")] + includedir: bool, + + /// Directory containing Wasmer libraries. + #[clap(long, conflicts_with = "pkg_config")] + libdir: bool, + + /// Libraries needed to link against Wasmer components. + #[clap(long, conflicts_with = "pkg_config")] + libs: bool, + + /// C compiler flags for files that include Wasmer headers. + #[clap(long, conflicts_with = "pkg_config")] + cflags: bool, + + /// It outputs the necessary details for compiling + /// and linking a program to Wasmer, using the `pkg-config` format. + #[clap(long)] + pkg_config: bool, +} + +impl Config { + /// Runs logic for the `config` subcommand + pub fn execute(&self) -> Result<()> { + self.inner_execute() + .context("failed to retrieve the wasmer config".to_string()) + } + fn inner_execute(&self) -> Result<()> { + let key = "WASMER_DIR"; + let wasmer_dir = env::var(key) + .ok() + .or_else(|| option_env!("WASMER_INSTALL_PREFIX").map(str::to_string)) + .or_else(|| { + // Allowing deprecated function home_dir since it works fine, + // and will never be removed from std. + #[allow(deprecated)] + let dir = std::env::home_dir()?.join(".wasmer").to_str()?.to_string(); + + Some(dir) + }) + .context(format!( + "failed to retrieve the {} environment variables", + key + ))?; + + let prefix = PathBuf::from(wasmer_dir); + + let prefixdir = prefix.display().to_string(); + let bindir = prefix.join("bin").display().to_string(); + let includedir = prefix.join("include").display().to_string(); + let libdir = prefix.join("lib").display().to_string(); + let cflags = format!("-I{}", includedir); + let libs = format!("-L{} -lwasmer", libdir); + + if self.pkg_config { + println!("prefix={}", prefixdir); + println!("exec_prefix={}", bindir); + println!("includedir={}", includedir); + println!("libdir={}", libdir); + println!(); + println!("Name: wasmer"); + println!("Description: The Wasmer library for running WebAssembly"); + println!("Version: {}", VERSION); + println!("Cflags: {}", cflags); + println!("Libs: {}", libs); + return Ok(()); + } + + if self.prefix { + println!("{}", prefixdir); + } + if self.bindir { + println!("{}", bindir); + } + if self.includedir { + println!("{}", includedir); + } + if self.libdir { + println!("{}", libdir); + } + if self.libs { + println!("{}", libs); + } + if self.cflags { + println!("{}", cflags); + } + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/commands/validate.rs b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/commands/validate.rs new file mode 100644 index 0000000..c3c421f --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/commands/validate.rs @@ -0,0 +1,40 @@ +use crate::store::StoreOptions; +use anyhow::{bail, Context, Result}; +use clap::Parser; +use std::path::PathBuf; +use std::str::FromStr; +use wasmer_types::{is_wasm, CpuFeature, Target, Triple}; + +#[derive(Debug, Parser)] +/// The options for the `wasmer validate` subcommand +pub struct Validate { + /// File to validate as WebAssembly + #[clap(name = "FILE")] + path: PathBuf, + + #[clap(flatten)] + store: StoreOptions, +} + +impl Validate { + /// Runs logic for the `validate` subcommand + pub fn execute(&self) -> Result<()> { + self.inner_execute() + .context(format!("failed to validate `{}`", self.path.display())) + } + fn inner_execute(&self) -> Result<()> { + let target = Target::new( + Triple::from_str("x86_64-linux-gnu").unwrap(), + CpuFeature::SSE2 | CpuFeature::AVX, + ); + let (engine_builder, _compiler_type) = self.store.get_engine_for_target(target)?; + let engine = engine_builder.engine(); + let module_contents = std::fs::read(&self.path)?; + if !is_wasm(&module_contents) { + bail!("`wasmer validate` only validates WebAssembly files"); + } + engine.validate(&module_contents)?; + eprintln!("Validation passed for `{}`.", self.path.display()); + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/common.rs b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/common.rs new file mode 100644 index 0000000..81af031 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/common.rs @@ -0,0 +1,53 @@ +//! Common module with common used structures across different +//! commands. +use crate::VERSION; +use clap::Parser; +use std::env; +use std::path::PathBuf; + +#[derive(Debug, Parser, Clone, Default)] +/// The WebAssembly features that can be passed through the +/// Command Line args. +pub struct WasmFeatures { + /// Enable support for the SIMD proposal. + #[clap(long = "enable-simd")] + pub simd: bool, + + /// Enable support for the threads proposal. + #[clap(long = "enable-threads")] + pub threads: bool, + + /// Enable support for the reference types proposal. + #[clap(long = "enable-reference-types")] + pub reference_types: bool, + + /// Enable support for the multi value proposal. + #[clap(long = "enable-multi-value")] + pub multi_value: bool, + + /// Enable support for the bulk memory proposal. + #[clap(long = "enable-bulk-memory")] + pub bulk_memory: bool, + + /// Enable support for all pre-standard proposals. + #[clap(long = "enable-all")] + pub all: bool, +} + +/// Get the cache dir +pub fn get_cache_dir() -> PathBuf { + match env::var("WASMER_CACHE_DIR") { + Ok(dir) => { + let mut path = PathBuf::from(dir); + path.push(VERSION); + path + } + Err(_) => { + // We use a temporal directory for saving cache files + let mut temp_dir = env::temp_dir(); + temp_dir.push("wasmer"); + temp_dir.push(VERSION); + temp_dir + } + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/error.rs b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/error.rs new file mode 100644 index 0000000..f23b503 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/error.rs @@ -0,0 +1,114 @@ +//! Implements `PretyError` to print pretty errors in the CLI (when they happen) + +use anyhow::{Chain, Error}; +use colored::*; +use std::fmt::{self, Debug, Write}; + +/// A `PrettyError` for printing `anyhow::Error` nicely. +pub struct PrettyError { + error: Error, +} + +/// A macro that prints a warning with nice colors +#[macro_export] +macro_rules! warning { + ($($arg:tt)*) => ({ + use colored::*; + eprintln!("{}: {}", "warning".yellow().bold(), format!($($arg)*)); + }) +} + +impl PrettyError { + /// Process a `Result` printing any errors and exiting + /// the process after + pub fn report(result: Result) -> ! { + std::process::exit(match result { + Ok(_t) => 0, + Err(error) => { + eprintln!("{:?}", PrettyError { error }); + 1 + } + }); + } +} + +impl Debug for PrettyError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let error = &self.error; + + if f.alternate() { + return Debug::fmt(&error, f); + } + + write!(f, "{}", format!("{}: {}", "error".red(), error).bold())?; + // write!(f, "{}", error)?; + + if let Some(cause) = error.source() { + // write!(f, "\n{}:", "caused by".bold().blue())?; + let chain = Chain::new(cause); + let (total_errors, _) = chain.size_hint(); + for (n, error) in chain.enumerate() { + writeln!(f)?; + let mut indented = Indented { + inner: f, + number: Some(n + 1), + is_last: n == total_errors - 1, + started: false, + }; + write!(indented, "{}", error)?; + } + } + Ok(()) + } +} + +struct Indented<'a, D> { + inner: &'a mut D, + number: Option, + started: bool, + is_last: bool, +} + +impl Write for Indented<'_, T> +where + T: Write, +{ + fn write_str(&mut self, s: &str) -> fmt::Result { + for (i, line) in s.split('\n').enumerate() { + if !self.started { + self.started = true; + match self.number { + Some(number) => { + if !self.is_last { + write!( + self.inner, + "{} {: >4} ", + "│".bold().blue(), + format!("{}:", number).dimmed() + )? + } else { + write!( + self.inner, + "{}{: >2}: ", + "╰─â–ļ".bold().blue(), + format!("{}", number).bold().blue() + )? + } + } + None => self.inner.write_str(" ")?, + } + } else if i > 0 { + self.inner.write_char('\n')?; + if self.number.is_some() { + self.inner.write_str(" ")?; + } else { + self.inner.write_str(" ")?; + } + } + + self.inner.write_str(line)?; + } + + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/lib.rs b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/lib.rs new file mode 100644 index 0000000..7ee91da --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/lib.rs @@ -0,0 +1,30 @@ +//! The Wasmer binary lib + +#![deny( + missing_docs, + dead_code, + nonstandard_style, + unused_mut, + unused_variables, + unused_unsafe, + unreachable_patterns +)] +#![doc(html_favicon_url = "https://wasmer.io/images/icons/favicon-32x32.png")] +#![doc(html_logo_url = "https://github.com/wasmerio.png?size=200")] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +#[macro_use] +extern crate anyhow; + +pub mod commands; +pub mod common; +#[macro_use] +pub mod error; +pub mod cli; +#[cfg(feature = "debug")] +pub mod logging; +pub mod store; +pub mod utils; + +/// Version number of this crate. +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/logging.rs b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/logging.rs new file mode 100644 index 0000000..88ae282 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/logging.rs @@ -0,0 +1,68 @@ +//! Logging functions for the debug feature. + +use anyhow::Result; +use fern::colors::{Color, ColoredLevelConfig}; +use std::time; + +/// The debug level +pub type DebugLevel = log::LevelFilter; + +/// Subroutine to instantiate the loggers +pub fn set_up_logging(verbose: u8) -> Result<(), String> { + let colors_line = ColoredLevelConfig::new() + .error(Color::Red) + .warn(Color::Yellow) + .trace(Color::BrightBlack); + let should_color = crate::utils::wasmer_should_print_color(); + + let colors_level = colors_line.info(Color::Green); + let level = match verbose { + 1 => DebugLevel::Debug, + _ => DebugLevel::Trace, + }; + let dispatch = fern::Dispatch::new() + .level(level) + .chain({ + let base = if should_color { + fern::Dispatch::new().format(move |out, message, record| { + let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH).expect("Can't get time"); + out.finish(format_args!( + "{color_line}[{seconds}.{millis} {level} {target}{color_line}]{ansi_close} {message}", + color_line = format_args!( + "\x1B[{}m", + colors_line.get_color(&record.level()).to_fg_str() + ), + seconds = time.as_secs(), + millis = time.subsec_millis(), + level = colors_level.color(record.level()), + target = record.target(), + ansi_close = "\x1B[0m", + message = message, + )); + }) + } else { + // default formatter without color + fern::Dispatch::new().format(move |out, message, record| { + let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH).expect("Can't get time"); + out.finish(format_args!( + "[{seconds}.{millis} {level} {target}] {message}", + seconds = time.as_secs(), + millis = time.subsec_millis(), + level = record.level(), + target = record.target(), + message = message, + )); + }) + }; + + base + .filter(|metadata| { + metadata.target().starts_with("wasmer") + }) + .chain(std::io::stdout()) + }); + + dispatch.apply().map_err(|e| format!("{}", e))?; + + Ok(()) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/store.rs b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/store.rs new file mode 100644 index 0000000..370d59b --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/store.rs @@ -0,0 +1,383 @@ +//! Common module with common used structures across different +//! commands. + +use crate::common::WasmFeatures; +use anyhow::Result; +use clap::Parser; +use std::string::ToString; +#[allow(unused_imports)] +use std::sync::Arc; +use wasmer_compiler::EngineBuilder; +use wasmer_compiler::{CompilerConfig, Features}; +use wasmer_types::{MemoryStyle, MemoryType, Pages, PointerWidth, TableStyle, TableType, Target}; + +/// Minimul Subset of Tunable parameters for WebAssembly compilation. +#[derive(Clone)] +pub struct SubsetTunables { + /// For static heaps, the size in wasm pages of the heap protected by bounds checking. + pub static_memory_bound: Pages, + + /// The size in bytes of the offset guard for static heaps. + pub static_memory_offset_guard_size: u64, + + /// The size in bytes of the offset guard for dynamic heaps. + pub dynamic_memory_offset_guard_size: u64, +} + +impl SubsetTunables { + /// Get the `BaseTunables` for a specific Target + pub fn for_target(target: &Target) -> Self { + let triple = target.triple(); + let pointer_width: PointerWidth = triple.pointer_width().unwrap(); + let (static_memory_bound, static_memory_offset_guard_size): (Pages, u64) = + match pointer_width { + PointerWidth::U16 => (0x400.into(), 0x1000), + PointerWidth::U32 => (0x4000.into(), 0x1_0000), + // Static Memory Bound: + // Allocating 4 GiB of address space let us avoid the + // need for explicit bounds checks. + // Static Memory Guard size: + // Allocating 2 GiB of address space lets us translate wasm + // offsets into x86 offsets as aggressively as we can. + PointerWidth::U64 => (0x1_0000.into(), 0x8000_0000), + }; + + // Allocate a small guard to optimize common cases but without + // wasting too much memory. + // The Windows memory manager seems more laxed than the other ones + // And a guard of just 1 page may not be enough is some borderline cases + // So using 2 pages for guard on this platform + #[cfg(target_os = "windows")] + let dynamic_memory_offset_guard_size: u64 = 0x2_0000; + #[cfg(not(target_os = "windows"))] + let dynamic_memory_offset_guard_size: u64 = 0x1_0000; + + Self { + static_memory_bound, + static_memory_offset_guard_size, + dynamic_memory_offset_guard_size, + } + } + /// Get a `MemoryStyle` for the provided `MemoryType` + pub fn memory_style(&self, memory: &MemoryType) -> MemoryStyle { + // A heap with a maximum that doesn't exceed the static memory bound specified by the + // tunables make it static. + // + // If the module doesn't declare an explicit maximum treat it as 4GiB. + let maximum = memory.maximum.unwrap_or_else(Pages::max_value); + if maximum <= self.static_memory_bound { + MemoryStyle::Static { + // Bound can be larger than the maximum for performance reasons + bound: self.static_memory_bound, + offset_guard_size: self.static_memory_offset_guard_size, + } + } else { + MemoryStyle::Dynamic { + offset_guard_size: self.dynamic_memory_offset_guard_size, + } + } + } + + /// Get a [`TableStyle`] for the provided [`TableType`]. + pub fn table_style(&self, _table: &TableType) -> TableStyle { + TableStyle::CallerChecksSignature + } +} + +#[derive(Debug, Clone, Parser, Default)] +/// The compiler and engine options +pub struct StoreOptions { + #[clap(flatten)] + compiler: CompilerOptions, +} + +#[derive(Debug, Clone, Parser, Default)] +/// The compiler options +pub struct CompilerOptions { + /// Use Singlepass compiler. + #[clap(long, conflicts_with_all = &["cranelift", "llvm"])] + singlepass: bool, + + /// Use Cranelift compiler. + #[clap(long, conflicts_with_all = &["singlepass", "llvm"])] + cranelift: bool, + + /// Use LLVM compiler. + #[clap(long, conflicts_with_all = &["singlepass", "cranelift"])] + llvm: bool, + + /// Enable compiler internal verification. + #[allow(unused)] + #[clap(long)] + #[allow(dead_code)] + enable_verifier: bool, + + /// LLVM debug directory, where IR and object files will be written to. + #[allow(unused)] + #[cfg(feature = "llvm")] + #[cfg_attr(feature = "llvm", clap(long, parse(from_os_str)))] + llvm_debug_dir: Option, + + #[clap(flatten)] + features: WasmFeatures, +} + +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"); + } + } + } + } + + /// Get the enaled Wasm features. + pub fn get_features(&self, mut features: Features) -> Result { + if self.features.threads || self.features.all { + features.threads(true); + } + if self.features.multi_value || self.features.all { + features.multi_value(true); + } + if self.features.simd || self.features.all { + features.simd(true); + } + if self.features.bulk_memory || self.features.all { + features.bulk_memory(true); + } + if self.features.reference_types || self.features.all { + features.reference_types(true); + } + Ok(features) + } + + fn get_engine_by_type( + &self, + target: Target, + compiler_config: Box, + ) -> Result { + let features = self.get_features(compiler_config.default_features_for_target(&target))?; + let engine: EngineBuilder = EngineBuilder::new(compiler_config) + .set_target(Some(target)) + .set_features(Some(features)); + + Ok(engine) + } + + /// 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 = "singlepass")] + CompilerType::Singlepass => { + let mut config = wasmer_compiler_singlepass::Singlepass::new(); + if self.enable_verifier { + config.enable_verifier(); + } + Box::new(config) + } + #[cfg(feature = "cranelift")] + CompilerType::Cranelift => { + let mut config = wasmer_compiler_cranelift::Cranelift::new(); + if self.enable_verifier { + config.enable_verifier(); + } + Box::new(config) + } + #[cfg(feature = "llvm")] + CompilerType::LLVM => { + use std::fmt; + use std::fs::File; + use std::io::Write; + use wasmer_compiler_llvm::{ + CompiledKind, InkwellMemoryBuffer, InkwellModule, LLVMCallbacks, LLVM, + }; + use wasmer_types::entity::EntityRef; + let mut config = LLVM::new(); + struct Callbacks { + debug_dir: PathBuf, + } + impl Callbacks { + fn new(debug_dir: PathBuf) -> Result { + // Create the debug dir in case it doesn't exist + std::fs::create_dir_all(&debug_dir)?; + Ok(Self { debug_dir }) + } + } + // Converts a kind into a filename, that we will use to dump + // the contents of the IR object file to. + fn types_to_signature(types: &[Type]) -> String { + types + .iter() + .map(|ty| match ty { + Type::I32 => "i".to_string(), + Type::I64 => "I".to_string(), + Type::F32 => "f".to_string(), + Type::F64 => "F".to_string(), + Type::V128 => "v".to_string(), + Type::ExternRef => "e".to_string(), + Type::FuncRef => "r".to_string(), + }) + .collect::>() + .join("") + } + // Converts a kind into a filename, that we will use to dump + // the contents of the IR object file to. + fn function_kind_to_filename(kind: &CompiledKind) -> String { + match kind { + CompiledKind::Local(local_index) => { + format!("function_{}", local_index.index()) + } + CompiledKind::FunctionCallTrampoline(func_type) => format!( + "trampoline_call_{}_{}", + types_to_signature(&func_type.params()), + types_to_signature(&func_type.results()) + ), + CompiledKind::DynamicFunctionTrampoline(func_type) => format!( + "trampoline_dynamic_{}_{}", + types_to_signature(&func_type.params()), + types_to_signature(&func_type.results()) + ), + CompiledKind::Module => "module".into(), + } + } + impl LLVMCallbacks for Callbacks { + fn preopt_ir(&self, kind: &CompiledKind, module: &InkwellModule) { + let mut path = self.debug_dir.clone(); + path.push(format!("{}.preopt.ll", function_kind_to_filename(kind))); + module + .print_to_file(&path) + .expect("Error while dumping pre optimized LLVM IR"); + } + fn postopt_ir(&self, kind: &CompiledKind, module: &InkwellModule) { + let mut path = self.debug_dir.clone(); + path.push(format!("{}.postopt.ll", function_kind_to_filename(kind))); + module + .print_to_file(&path) + .expect("Error while dumping post optimized LLVM IR"); + } + fn obj_memory_buffer( + &self, + kind: &CompiledKind, + memory_buffer: &InkwellMemoryBuffer, + ) { + let mut path = self.debug_dir.clone(); + path.push(format!("{}.o", function_kind_to_filename(kind))); + let mem_buf_slice = memory_buffer.as_slice(); + let mut file = File::create(path) + .expect("Error while creating debug object file from LLVM IR"); + let mut pos = 0; + while pos < mem_buf_slice.len() { + pos += file.write(&mem_buf_slice[pos..]).unwrap(); + } + } + } + + impl fmt::Debug for Callbacks { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "LLVMCallbacks") + } + } + + if let Some(ref llvm_debug_dir) = self.llvm_debug_dir { + config.callbacks(Some(Arc::new(Callbacks::new(llvm_debug_dir.clone())?))); + } + if self.enable_verifier { + config.enable_verifier(); + } + Box::new(config) + } + #[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm",)))] + compiler => { + bail!( + "The `{}` compiler is not included in this binary.", + compiler.to_string() + ) + } + }; + + #[allow(unreachable_code)] + Ok((compiler_config, compiler)) + } +} + +/// The compiler used for the store +#[derive(Debug, PartialEq, Eq)] +pub enum CompilerType { + /// Singlepass compiler + Singlepass, + /// Cranelift compiler + Cranelift, + /// LLVM compiler + LLVM, + /// Headless compiler + #[allow(dead_code)] + Headless, +} + +impl CompilerType { + /// Return all enabled compilers + pub fn enabled() -> Vec { + vec![ + #[cfg(feature = "singlepass")] + Self::Singlepass, + #[cfg(feature = "cranelift")] + Self::Cranelift, + #[cfg(feature = "llvm")] + Self::LLVM, + ] + } +} + +impl ToString for CompilerType { + fn to_string(&self) -> String { + match self { + Self::Singlepass => "singlepass".to_string(), + Self::Cranelift => "cranelift".to_string(), + Self::LLVM => "llvm".to_string(), + Self::Headless => "headless".to_string(), + } + } +} + +impl StoreOptions { + /// Get a EngineBulder for the Target + pub fn get_engine_for_target(&self, target: Target) -> Result<(EngineBuilder, CompilerType)> { + let (compiler_config, compiler_type) = self.compiler.get_compiler_config()?; + let engine = self.get_engine_with_compiler(target, compiler_config)?; + Ok((engine, compiler_type)) + } + + fn get_engine_with_compiler( + &self, + target: Target, + compiler_config: Box, + ) -> Result { + self.compiler.get_engine_by_type(target, compiler_config) + } + + /// Get (Subset)Tunables for the Target + pub fn get_tunables_for_target(&self, target: &Target) -> Result { + let tunables = SubsetTunables::for_target(target); + Ok(tunables) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/utils.rs b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/utils.rs new file mode 100644 index 0000000..7a4a925 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli-compiler/src/utils.rs @@ -0,0 +1,97 @@ +//! Utility functions for the WebAssembly module +use anyhow::{bail, Context, Result}; +use is_terminal::IsTerminal; +use std::path::PathBuf; +use std::{env, path::Path}; + +/// Whether or not Wasmer should print with color +pub fn wasmer_should_print_color() -> bool { + env::var("WASMER_COLOR") + .ok() + .and_then(|inner| inner.parse::().ok()) + .unwrap_or_else(|| std::io::stdout().is_terminal()) +} + +fn retrieve_alias_pathbuf(alias: &str, real_dir: &str) -> Result<(String, PathBuf)> { + let pb = Path::new(real_dir) + .canonicalize() + .with_context(|| format!("Unable to get the absolute path for \"{real_dir}\""))?; + + if let Ok(pb_metadata) = pb.metadata() { + if !pb_metadata.is_dir() { + bail!("\"{real_dir}\" exists, but it is not a directory"); + } + } else { + bail!("Directory \"{real_dir}\" does not exist"); + } + + Ok((alias.to_string(), pb)) +} + +/// Parses a mapdir from a string +pub fn parse_mapdir(entry: &str) -> Result<(String, PathBuf)> { + // We try first splitting by `::` + if let [alias, real_dir] = entry.split("::").collect::>()[..] { + retrieve_alias_pathbuf(alias, real_dir) + } + // And then we try splitting by `:` (for compatibility with previous API) + else if let [alias, real_dir] = entry.split(':').collect::>()[..] { + retrieve_alias_pathbuf(alias, real_dir) + } else { + bail!( + "Directory mappings must consist of two paths separate by a `::` or `:`. Found {}", + &entry + ) + } +} + +/// Parses an environment variable. +pub fn parse_envvar(entry: &str) -> Result<(String, String)> { + let entry = entry.trim(); + + match entry.find('=') { + None => bail!( + "Environment variable must be of the form `=`; found `{}`", + &entry + ), + + Some(0) => bail!( + "Environment variable is not well formed, the `name` is missing in `=`; got `{}`", + &entry + ), + + Some(position) if position == entry.len() - 1 => bail!( + "Environment variable is not well formed, the `value` is missing in `=`; got `{}`", + &entry + ), + + Some(position) => Ok((entry[..position].into(), entry[position + 1..].into())), + } +} + +#[cfg(test)] +mod tests { + use super::parse_envvar; + + #[test] + fn test_parse_envvar() { + assert_eq!( + parse_envvar("A").unwrap_err().to_string(), + "Environment variable must be of the form `=`; found `A`" + ); + assert_eq!( + parse_envvar("=A").unwrap_err().to_string(), + "Environment variable is not well formed, the `name` is missing in `=`; got `=A`" + ); + assert_eq!( + parse_envvar("A=").unwrap_err().to_string(), + "Environment variable is not well formed, the `value` is missing in `=`; got `A=`" + ); + assert_eq!(parse_envvar("A=B").unwrap(), ("A".into(), "B".into())); + assert_eq!(parse_envvar(" A=B\t").unwrap(), ("A".into(), "B".into())); + assert_eq!( + parse_envvar("A=B=C=D").unwrap(), + ("A".into(), "B=C=D".into()) + ); + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/Cargo.toml b/arbitrator/tools/module_roots/wasmer/lib/cli/Cargo.toml new file mode 100644 index 0000000..4857c58 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/Cargo.toml @@ -0,0 +1,262 @@ +[package] +name = "wasmer-cli" +description = "Wasmer CLI" +categories = ["wasm", "command-line-interface"] +keywords = ["wasm", "webassembly", "cli"] +readme = "README.md" +default-run = "wasmer" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[[bin]] +name = "wasmer" +path = "src/bin/wasmer.rs" +doc = false +required-features = ["backend"] + +[[bin]] +name = "wasmer-headless" +path = "src/bin/wasmer_headless.rs" +doc = false +required-features = ["headless"] + +[features] +# Don't add the compiler features in default, please add them on the Makefile +# since we might want to autoconfigure them depending on the availability on the host. +default = [ + "sys", + "wat", + "wast", + "compiler", + "journal", + "wasmer-artifact-create", + "static-artifact-create", +] + +# Tun-tap client for connecting to Wasmer Edge VPNs +tun-tap = ["dep:tun-tap", "virtual-net/tokio-tungstenite", "tokio-tungstenite", "mio", "futures-util", "mac_address", "dep:interfaces"] +journal = ["wasmer-wasix/journal"] +fuse = ["dep:fuse", "dep:time01", "dep:shared-buffer", "dep:rkyv"] +backend = [] +coredump = ["wasm-coredump-builder"] +sys = ["compiler", "wasmer-vm"] +jsc = ["backend", "wasmer/jsc", "wasmer/std"] +wast = ["wasmer-wast"] +host-net = ["virtual-net/host-net"] +wat = ["wasmer/wat"] +compiler = ["backend", "wasmer/compiler", "wasmer-compiler/translator", "wasmer-compiler/compiler"] +wasmer-artifact-create = ["compiler", "wasmer/wasmer-artifact-load", "wasmer/wasmer-artifact-create", "wasmer-compiler/wasmer-artifact-load", "wasmer-compiler/wasmer-artifact-create", "wasmer-object"] +static-artifact-create = ["compiler", "wasmer/static-artifact-load", "wasmer/static-artifact-create", "wasmer-compiler/static-artifact-load", "wasmer-compiler/static-artifact-create", "wasmer-object"] +wasmer-artifact-load = ["compiler", "wasmer/wasmer-artifact-load", "wasmer-compiler/wasmer-artifact-load"] +static-artifact-load = ["compiler", "wasmer/static-artifact-load", "wasmer-compiler/static-artifact-load"] +singlepass = ["wasmer-compiler-singlepass", "compiler"] +cranelift = ["wasmer-compiler-cranelift", "compiler"] +llvm = ["wasmer-compiler-llvm", "compiler"] +disable-all-logging = ["wasmer-wasix/disable-all-logging", "log/release_max_level_off"] +headless = [] +headless-minimal = ["headless", "disable-all-logging"] + +# Optional +enable-serde = ["wasmer/enable-serde", "wasmer-vm/enable-serde", "wasmer-compiler/enable-serde", "wasmer-wasix/enable-serde"] + +[dependencies] +# Repo-local dependencies. + +wasmer = { version = "=4.2.8", path = "../api", default-features = false } +wasmer-compiler = { version = "=4.2.8", path = "../compiler", features = [ + "compiler", +], optional = true } +wasmer-compiler-cranelift = { version = "=4.2.8", path = "../compiler-cranelift", optional = true } +wasmer-compiler-singlepass = { version = "=4.2.8", path = "../compiler-singlepass", optional = true } +wasmer-compiler-llvm = { version = "=4.2.8", path = "../compiler-llvm", optional = true } +wasmer-emscripten = { version = "=4.2.8", path = "../emscripten" } +wasmer-vm = { version = "=4.2.8", path = "../vm", optional = true } +wasmer-wasix = { version = "0.18.3", path = "../wasix", features = [ + "logging", + "webc_runner_rt_wcgi", + "webc_runner_rt_dcgi", + "webc_runner_rt_dproxy", + "webc_runner_rt_emscripten", + "host-fs", +] } +wasmer-wast = { version = "=4.2.8", path = "../../tests/lib/wast", optional = true } +wasmer-types = { version = "=4.2.8", path = "../types", features = [ + "enable-serde", +] } +wasmer-registry = { version = "5.10.4", path = "../registry", features = [ + "build-package", + "clap", +] } +wasmer-object = { version = "=4.2.8", path = "../object", optional = true } +virtual-fs = { version = "0.11.2", path = "../virtual-fs", default-features = false, features = [ + "host-fs", +] } +virtual-net = { version = "0.6.3", path = "../virtual-net" } +virtual-mio = { version = "0.3.1", path = "../virtual-io" } + +# Wasmer-owned dependencies. + +webc = { workspace = true } +wasmer-api = { version = "=0.0.25", path = "../backend-api" } +edge-schema.workspace = true +edge-util = { version = "=0.1.0" } + +# Used by the mount command + +shared-buffer = { workspace = true, optional = true } +rkyv = { workspace = true, optional = true } +fuse = { version = "0.3", optional = true } +time01 = { package = "time", version = "0.1.45", optional = true } + + +# Third-party dependencies. + +is-terminal = "0.4.7" +colored = "2.0" +anyhow = "1.0" + +# For the inspect subcommand +bytesize = "1.0" +cfg-if = "1.0" +tempfile = "3.6.0" +serde = { version = "1.0.147", features = ["derive"] } +dirs = { version = "4.0" } +serde_json = { version = "1.0" } +target-lexicon = { version = "0.12", features = ["std"] } +wasmer-toml = { workspace = true } +indexmap = "1.9.2" +walkdir = "2.3.2" +regex = "1.6.0" +toml = "0.5.9" +url = "2.3.1" +libc = { version = "^0.2", default-features = false } +parking_lot = "0.12" +dialoguer = "0.11.0" +tldextract = "0.6.0" +hex = "0.4.3" +flate2 = "1.0.25" +cargo_metadata = "0.15.2" +tar = "0.4.38" +bytes = "1" +thiserror = "1.0.37" +log = "0.4.17" +semver = "1.0.14" +pathdiff = "0.2.1" +sha2 = "0.10.6" +object = "0.32.0" +wasm-coredump-builder = { version = "0.1.11", optional = true } +tracing = { version = "0.1" } +tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "json"] } +async-trait = "0.1.68" +tokio = { version = "1.28.1", features = ["macros", "rt-multi-thread"] } +once_cell = "1.17.1" +indicatif = "0.17.5" +opener = "0.6.1" +normpath = "=1.1.1" +hyper = { version = "0.14.27", features = ["server"] } +http = "0.2.9" +futures = "0.3.29" +humantime = "2.1.0" +interfaces = { version = "0.0.9", optional = true } + +uuid = { version = "1.3.0", features = ["v4"] } +time = { version = "0.3.17", features = ["macros"] } +serde_yaml = "0.8.26" +comfy-table = "7.0.1" + + +# Used by tuntap and connect +futures-util = { version = "0.3", optional = true } +mio = { version = "0.8", optional = true } +tokio-tungstenite = { version = "0.20.1", features = [ + "rustls-tls-webpki-roots", +], optional = true } +mac_address = { version = "1.1.5", optional = true } +tun-tap = { version = "0.1.3", features = ["tokio"], optional = true } + +# NOTE: Must use different features for clap because the "color" feature does not +# work on wasi due to the anstream dependency not compiling. +[target.'cfg(not(target_family = "wasm"))'.dependencies] +clap = { version = "4.2.8", features = ["derive", "env"] } +[target.'cfg(target_family = "wasm")'.dependencies] +clap = { version = "4.2.8", default-features = false, features = [ + "std", + "help", + "usage", + "error-context", + "suggestions", + "derive", + "env", +] } + +[target.'cfg(not(target_arch = "riscv64"))'.dependencies] +reqwest = { version = "^0.11", default-features = false, features = [ + "rustls-tls", + "json", + "multipart", + "gzip", +] } + +[target.'cfg(target_arch = "riscv64")'.dependencies] +reqwest = { version = "^0.11", default-features = false, features = [ + "native-tls", + "json", + "multipart", +] } + +[build-dependencies] +chrono = { version = "^0.4", default-features = false, features = [ + "std", + "clock", +] } + +[target.'cfg(target_os = "linux")'.dependencies] +unix_mode = "0.1.3" + + +[dev-dependencies] +assert_cmd = "2.0.11" +predicates = "3.0.3" +pretty_assertions = "1.3.0" + +[target.'cfg(target_os = "windows")'.dependencies] +colored = "2.0.0" + +[package.metadata.binstall] +pkg-fmt = "tgz" + +[package.metadata.binstall.overrides.aarch64-apple-darwin] +pkg-url = "{ repo }/releases/download/v{ version }/wasmer-darwin-arm64.{ archive-format }" +bin-dir = "bin/{ bin }" + +[package.metadata.binstall.overrides.x86_64-apple-darwin] +pkg-url = "{ repo }/releases/download/v{ version }/wasmer-darwin-amd64.{ archive-format }" +bin-dir = "bin/{ bin }" + +[package.metadata.binstall.overrides.aarch64-unknown-linux-gnu] +pkg-url = "{ repo }/releases/download/v{ version }/wasmer-linux-aarch64.{ archive-format }" +bin-dir = "bin/{ bin }" + +[package.metadata.binstall.overrides.riscv64gc-unknown-linux-gnu] +pkg-url = "{ repo }/releases/download/v{ version }/wasmer-linux-riscv64gc.{ archive-format }" +bin-dir = "bin/{ bin }" + +[package.metadata.binstall.overrides.x86_64-unknown-linux-gnu] +pkg-url = "{ repo }/releases/download/v{ version }/wasmer-linux-amd64.{ archive-format }" +bin-dir = "bin/{ bin }" + +[package.metadata.binstall.overrides.x86_64-unknown-linux-musl] +pkg-url = "{ repo }/releases/download/v{ version }/wasmer-linux-musl-amd64.{ archive-format }" +bin-dir = "bin/{ bin }" + +[package.metadata.binstall.overrides.x86_64-pc-windows-msvc] +pkg-url = "{ repo }/releases/download/v{ version }/wasmer-windows-amd64.{ archive-format }" +bin-dir = "bin/{ bin }.exe" + +[package.metadata.docs.rs] +rustc-args = ["--cfg", "docsrs"] diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/README.md b/arbitrator/tools/module_roots/wasmer/lib/cli/README.md new file mode 100644 index 0000000..b6c913e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/README.md @@ -0,0 +1,63 @@ +# `wasmer-cli` [![Build Status](https://github.com/wasmerio/wasmer/workflows/build/badge.svg?style=flat-square)](https://github.com/wasmerio/wasmer/actions?query=workflow%3Abuild) [![Join Wasmer Slack](https://img.shields.io/static/v1?label=Slack&message=join%20chat&color=brighgreen&style=flat-square)](https://slack.wasmer.io) [![MIT License](https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square)](https://github.com/wasmerio/wasmer/blob/master/LICENSE) + +This crate is the Wasmer CLI. + +The recommended way to install `wasmer` is via the [wasmer-installer](https://github.com/wasmerio/wasmer-install). + +However, you can also install `wasmer` via Cargo (you will need to specify the compilers to use): + +```bash +cargo install wasmer-cli --features "singlepass,cranelift" +``` + +Or by building it inside the codebase: + +```bash +cargo build --release --features "singlepass,cranelift" +``` + +## Features + +The Wasmer supports the following features: +* `wat` (default): support for executing WebAssembly text files. +* `wast`(default): support for running wast test files. +* `cache` (default): support or automatically caching compiled artifacts. +* `wasi` (default): support for [WASI]. +* `emscripten` (default): support for [Emscripten]. +* `singlepass`: support for the [Singlepass compiler]. +* `cranelift`: support for the [Cranelift compiler]. +* `llvm`: support for the [LLVM compiler]. + +[WASI]: https://github.com/wasmerio/wasmer/tree/master/lib/wasi/ +[Emscripten]: https://github.com/wasmerio/wasmer/tree/master/lib/emscripten/ +[Singlepass compiler]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-singlepass/ +[Cranelift compiler]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-cranelift/ +[LLVM compiler]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-llvm/ + +## CLI commands + +Once you have Wasmer installed, you can start executing WebAssembly files easily: + +Get the current Wasmer version: + +```bash +wasmer -V +``` + +Execute a WebAssembly file: + +```bash +wasmer run myfile.wasm +``` + +Compile a WebAssembly file: + +```bash +wasmer compile myfile.wasm -o myfile.wasmu +``` + +Run a compiled WebAssembly file (fastest): + +```bash +wasmer run myfile.wasmu +``` diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/build.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/build.rs new file mode 100644 index 0000000..be9c016 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/build.rs @@ -0,0 +1,27 @@ +use std::process::Command; + +use chrono::prelude::*; + +pub fn main() { + // Set WASMER_GIT_HASH + let git_hash = Command::new("git") + .args(["rev-parse", "HEAD"]) + .output() + .ok() + .and_then(|output| String::from_utf8(output.stdout).ok()) + .unwrap_or_default(); + println!("cargo:rustc-env=WASMER_BUILD_GIT_HASH={}", git_hash); + + if git_hash.len() > 5 { + println!( + "cargo:rustc-env=WASMER_BUILD_GIT_HASH_SHORT={}", + &git_hash[..7] + ); + } else { + println!("cargo:rustc-env=WASMER_BUILD_GIT_HASH_SHORT=???????"); + } + + let utc: DateTime = Utc::now(); + let date = utc.format("%Y-%m-%d").to_string(); + println!("cargo:rustc-env=WASMER_BUILD_DATE={}", date); +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/bin/wasmer.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/bin/wasmer.rs new file mode 100644 index 0000000..482342e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/bin/wasmer.rs @@ -0,0 +1,8 @@ +#[cfg(not(feature = "backend"))] +compile_error!( + "Either enable at least one backend, or compile the wasmer-headless binary instead.\nWith cargo, you can provide a compiler option with the --features flag.\n\nExample values:\n\n\t\t--features cranelift,singlepass\n\t\t--features jsc\n\n\n" +); + +fn main() { + wasmer_cli::run_cli(); +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/bin/wasmer_headless.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/bin/wasmer_headless.rs new file mode 100644 index 0000000..ca6849f --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/bin/wasmer_headless.rs @@ -0,0 +1,3 @@ +fn main() { + wasmer_cli::run_cli(); +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/c_gen/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/c_gen/mod.rs new file mode 100644 index 0000000..9ae88bb --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/c_gen/mod.rs @@ -0,0 +1,548 @@ +//! A convenient little abstraction for building up C expressions and generating +//! simple C code. + +#![allow(dead_code)] + +pub mod staticlib_header; + +/// An identifier in C. +pub type CIdent = String; + +/// A Type in the C language. +#[derive(Debug, Clone, Default)] +#[allow(dead_code)] +pub enum CType { + /// C `void` type. + #[default] + Void, + /// A pointer to some other type. + PointerTo { + /// Whether the pointer is `const`. + is_const: bool, + /// The type that the pointer points to. + inner: Box, + }, + /// C 8 bit unsigned integer type. + U8, + /// C 16 bit unsigned integer type. + U16, + /// C 32 bit unsigned integer type. + U32, + /// C 64 bit unsigned integer type. + U64, + /// C pointer sized unsigned integer type. + USize, + /// C 8 bit signed integer type. + I8, + /// C 16 bit signed integer type. + I16, + /// C 32 bit signed integer type. + I32, + /// C 64 bit signed integer type. + I64, + /// C pointer sized signed integer type. + ISize, + /// A function or function pointer. + Function { + /// The arguments the function takes. + arguments: Vec, + /// The return value if it has one + /// + /// None is equivalent to Some(Box(Ctype::Void)). + return_value: Option>, + }, + /// C constant array. + Array { + /// The type of the array. + inner: Box, + }, + /// A user defined type. + TypeDef(String), +} + +impl CType { + /// Convenience function to get a mutable void pointer type. + pub fn void_ptr() -> Self { + CType::PointerTo { + is_const: false, + inner: Box::new(CType::Void), + } + } + + /// Convenience function to get a const void pointer type. + #[allow(dead_code)] + pub fn const_void_ptr() -> Self { + CType::PointerTo { + is_const: true, + inner: Box::new(CType::Void), + } + } + + /// Generate the C source code for a type into the given `String`. + fn generate_c(&self, w: &mut String) { + match &self { + Self::Void => { + w.push_str("void"); + } + Self::PointerTo { is_const, inner } => { + if *is_const { + w.push_str("const "); + } + inner.generate_c(w); + w.push('*'); + } + Self::U8 => { + w.push_str("unsigned char"); + } + Self::U16 => { + w.push_str("unsigned short"); + } + Self::U32 => { + w.push_str("unsigned int"); + } + Self::U64 => { + w.push_str("unsigned long long"); + } + Self::USize => { + w.push_str("unsigned size_t"); + } + Self::I8 => { + w.push_str("char"); + } + Self::I16 => { + w.push_str("short"); + } + Self::I32 => { + w.push_str("int"); + } + Self::I64 => { + w.push_str("long long"); + } + Self::ISize => { + w.push_str("size_t"); + } + Self::Function { + arguments, + return_value, + } => { + // function with no, name, assume it's a function pointer + #[allow(clippy::borrowed_box)] + let ret: CType = return_value + .as_ref() + .map(|i: &Box| (**i).clone()) + .unwrap_or_default(); + ret.generate_c(w); + w.push(' '); + w.push_str("(*)"); + w.push('('); + match arguments.len() { + l if l > 1 => { + for arg in &arguments[..arguments.len() - 1] { + arg.generate_c(w); + w.push_str(", "); + } + arguments.last().unwrap().generate_c(w); + } + 1 => { + arguments[0].generate_c(w); + } + _ => {} + } + w.push(')'); + } + Self::Array { inner } => { + inner.generate_c(w); + w.push_str("[]"); + } + Self::TypeDef(inner) => { + w.push_str(inner); + } + } + } + + /// Generate the C source code for a type with a nameinto the given `String`. + fn generate_c_with_name(&self, name: &str, w: &mut String) { + match &self { + Self::PointerTo { .. } + | Self::TypeDef { .. } + | Self::Void + | Self::U8 + | Self::U16 + | Self::U32 + | Self::U64 + | Self::USize + | Self::I8 + | Self::I16 + | Self::I32 + | Self::I64 + | Self::ISize => { + self.generate_c(w); + w.push(' '); + w.push_str(name); + } + Self::Function { + arguments, + return_value, + } => { + #[allow(clippy::borrowed_box)] + let ret: CType = return_value + .as_ref() + .map(|i: &Box| (**i).clone()) + .unwrap_or_default(); + ret.generate_c(w); + w.push(' '); + w.push_str(name); + w.push('('); + match arguments.len() { + l if l > 1 => { + for arg in &arguments[..arguments.len() - 1] { + arg.generate_c(w); + w.push_str(", "); + } + arguments.last().unwrap().generate_c(w); + } + 1 => { + arguments[0].generate_c(w); + } + _ => {} + } + w.push(')'); + } + Self::Array { inner } => { + inner.generate_c(w); + w.push(' '); + w.push_str(name); + w.push_str("[]"); + } + } + } +} + +/// A statement in the C programming language. This may not be exact to what an +/// AST would look like or what the C standard says about the C language, it's +/// simply a structed way to organize data for generating C code. +#[derive(Debug, Clone)] +pub enum CStatement { + /// A declaration of some kind. + Declaration { + /// The name of the thing being declared. + name: CIdent, + /// Whether the thing being declared is `extern`. + is_extern: bool, + /// Whether the thing being declared is `const`. + is_const: bool, + /// The type of the thing being declared. + ctype: CType, + /// The definition of the thing being declared. + /// + /// This is useful for initializing constant arrays, for example. + definition: Option>, + }, + + /// A literal array of CStatements. + LiteralArray { + /// The contents of the array. + items: Vec, + }, + + /// A literal constant value, passed through directly as a string. + LiteralConstant { + /// The raw value acting as a constant. + value: String, + }, + + /// A C-style cast + Cast { + /// The type to cast to. + target_type: CType, + /// The thing being cast. + expression: Box, + }, + + /// Typedef one type to another. + TypeDef { + /// The type of the thing being typedef'd. + source_type: CType, + /// The new name by which this type may be called. + new_name: CIdent, + }, +} + +impl CStatement { + /// Generate C source code for the given CStatement. + fn generate_c(&self, w: &mut String) { + match &self { + Self::Declaration { + name, + is_extern, + is_const, + ctype, + definition, + } => { + if *is_const { + w.push_str("const "); + } + if *is_extern { + w.push_str("extern "); + } + ctype.generate_c_with_name(name, w); + if let Some(def) = definition { + w.push_str(" = "); + def.generate_c(w); + } + w.push(';'); + w.push('\n'); + } + Self::LiteralArray { items } => { + w.push('{'); + if !items.is_empty() { + w.push('\n'); + } + for item in items { + w.push('\t'); + item.generate_c(w); + w.push(','); + w.push('\n'); + } + w.push('}'); + } + Self::LiteralConstant { value } => { + w.push_str(value); + } + Self::Cast { + target_type, + expression, + } => { + w.push('('); + target_type.generate_c(w); + w.push(')'); + w.push(' '); + expression.generate_c(w); + } + Self::TypeDef { + source_type, + new_name, + } => { + w.push_str("typedef "); + // leaky abstraction / hack, doesn't fully solve the problem + if let CType::Function { .. } = source_type { + source_type.generate_c_with_name(&format!("(*{})", new_name), w); + } else { + source_type.generate_c(w); + w.push(' '); + w.push_str(new_name); + } + w.push(';'); + w.push('\n'); + } + } + } +} + +/// Generate C source code from some `CStatements` into a String. +// TODO: add config section +pub fn generate_c(statements: &[CStatement]) -> String { + let mut out = String::new(); + for statement in statements { + statement.generate_c(&mut out); + } + out +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn generate_types() { + macro_rules! assert_c_type { + ($ctype:expr, $expected:expr) => { + let mut w = String::new(); + let ctype = $ctype; + ctype.generate_c(&mut w); + assert_eq!(w, $expected); + }; + } + + assert_c_type!(CType::Void, "void"); + assert_c_type!(CType::void_ptr(), "void*"); + assert_c_type!(CType::const_void_ptr(), "const void*"); + assert_c_type!(CType::U8, "unsigned char"); + assert_c_type!(CType::U16, "unsigned short"); + assert_c_type!(CType::U32, "unsigned int"); + assert_c_type!(CType::U64, "unsigned long long"); + assert_c_type!(CType::USize, "unsigned size_t"); + assert_c_type!(CType::I8, "char"); + assert_c_type!(CType::I16, "short"); + assert_c_type!(CType::I32, "int"); + assert_c_type!(CType::I64, "long long"); + assert_c_type!(CType::ISize, "size_t"); + assert_c_type!(CType::TypeDef("my_type".to_string()), "my_type"); + assert_c_type!( + CType::Function { + arguments: vec![CType::U8, CType::ISize], + return_value: None + }, + "void (*)(unsigned char, size_t)" + ); + assert_c_type!( + CType::Function { + arguments: vec![], + return_value: Some(Box::new(CType::ISize)) + }, + "size_t (*)()" + ); + assert_c_type!( + CType::PointerTo { + is_const: true, + inner: Box::new(CType::PointerTo { + is_const: false, + inner: Box::new(CType::U32) + }) + }, + "const unsigned int**" + ); + // TODO: test more complicated const correctness rules: there are bugs relating to it. + } + + #[test] + fn generate_types_with_names() { + macro_rules! assert_c_type { + ($ctype:expr, $name:literal, $expected:expr) => { + let mut w = String::new(); + let ctype = $ctype; + ctype.generate_c_with_name($name, &mut w); + assert_eq!(w, $expected); + }; + } + + assert_c_type!(CType::Void, "main", "void main"); + assert_c_type!(CType::void_ptr(), "data", "void* data"); + assert_c_type!(CType::const_void_ptr(), "data", "const void* data"); + assert_c_type!(CType::U8, "data", "unsigned char data"); + assert_c_type!(CType::U16, "data", "unsigned short data"); + assert_c_type!(CType::U32, "data", "unsigned int data"); + assert_c_type!(CType::U64, "data", "unsigned long long data"); + assert_c_type!(CType::USize, "data", "unsigned size_t data"); + assert_c_type!(CType::I8, "data", "char data"); + assert_c_type!(CType::I16, "data", "short data"); + assert_c_type!(CType::I32, "data", "int data"); + assert_c_type!(CType::I64, "data", "long long data"); + assert_c_type!(CType::ISize, "data", "size_t data"); + assert_c_type!( + CType::TypeDef("my_type".to_string()), + "data", + "my_type data" + ); + assert_c_type!( + CType::Function { + arguments: vec![CType::U8, CType::ISize], + return_value: None + }, + "my_func", + "void my_func(unsigned char, size_t)" + ); + assert_c_type!( + CType::Function { + arguments: vec![], + return_value: Some(Box::new(CType::ISize)) + }, + "my_func", + "size_t my_func()" + ); + assert_c_type!( + CType::PointerTo { + is_const: true, + inner: Box::new(CType::PointerTo { + is_const: false, + inner: Box::new(CType::U32) + }) + }, + "data", + "const unsigned int** data" + ); + // TODO: test more complicated const correctness rules: there are bugs relating to it. + } + + #[test] + fn generate_expressions_works() { + macro_rules! assert_c_expr { + ($cexpr:expr, $expected:expr) => { + let mut w = String::new(); + let cexpr = $cexpr; + cexpr.generate_c(&mut w); + assert_eq!(w, $expected); + }; + } + + assert_c_expr!( + CStatement::LiteralConstant { + value: "\"Hello, world!\"".to_string() + }, + "\"Hello, world!\"" + ); + assert_c_expr!( + CStatement::TypeDef { + source_type: CType::Function { + arguments: vec![CType::I32, CType::I32], + return_value: None, + }, + new_name: "my_func_ptr".to_string(), + }, + "typedef void (*my_func_ptr)(int, int);\n" + ); + assert_c_expr!( + CStatement::LiteralArray { + items: vec![ + CStatement::LiteralConstant { + value: "1".to_string() + }, + CStatement::LiteralConstant { + value: "2".to_string() + }, + CStatement::LiteralConstant { + value: "3".to_string() + }, + ] + }, + "{\n\t1,\n\t2,\n\t3,\n}" + ); + assert_c_expr!(CStatement::LiteralArray { items: vec![] }, "{}"); + assert_c_expr!( + CStatement::Declaration { + name: "my_array".to_string(), + is_extern: false, + is_const: true, + ctype: CType::Array { + inner: Box::new(CType::I32) + }, + definition: Some(Box::new(CStatement::LiteralArray { + items: vec![ + CStatement::LiteralConstant { + value: "1".to_string() + }, + CStatement::LiteralConstant { + value: "2".to_string() + }, + CStatement::LiteralConstant { + value: "3".to_string() + }, + ] + })) + }, + "const int my_array[] = {\n\t1,\n\t2,\n\t3,\n};\n" + ); + assert_c_expr!( + CStatement::Declaration { + name: "my_array".to_string(), + is_extern: true, + is_const: true, + ctype: CType::Array { + inner: Box::new(CType::I32) + }, + definition: None, + }, + "const extern int my_array[];\n" + ); + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/c_gen/staticlib_header.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/c_gen/staticlib_header.rs new file mode 100644 index 0000000..27c10c6 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/c_gen/staticlib_header.rs @@ -0,0 +1,301 @@ +//! Generate a header file for the static object file produced. + +use wasmer_types::{ModuleInfo, Symbol, SymbolRegistry}; + +use super::{generate_c, CStatement, CType}; + +/// Helper functions to simplify the usage of the static artifact. +fn gen_helper_functions(atom_name: &str, module_name: &str) -> String { + format!(" + wasm_byte_vec_t generate_serialized_data_{atom_name}() {{ + // We need to pass all the bytes as one big buffer so we have to do all this logic to memcpy + // the various pieces together from the generated header file. + // + // We should provide a `deseralize_vectored` function to avoid requiring this extra work. + + char* byte_ptr = (char*)&{module_name}[0]; + + size_t num_function_pointers + = sizeof(function_pointers_{atom_name}) / sizeof(void*); + size_t num_function_trampolines + = sizeof(function_trampolines_{atom_name}) / sizeof(void*); + size_t num_dynamic_function_trampoline_pointers + = sizeof(dynamic_function_trampoline_pointers_{atom_name}) / sizeof(void*); + + + size_t buffer_size = module_bytes_len_{atom_name} + + sizeof(size_t) + sizeof(function_pointers_{atom_name}) + + sizeof(size_t) + sizeof(function_trampolines_{atom_name}) + + sizeof(size_t) + sizeof(dynamic_function_trampoline_pointers_{atom_name}); + + char* memory_buffer = (char*) malloc(buffer_size); + size_t current_offset = 0; + + memcpy(memory_buffer + current_offset, byte_ptr, module_bytes_len_{atom_name}); + current_offset += module_bytes_len_{atom_name}; + + memcpy(memory_buffer + current_offset, (void*)&num_function_pointers, sizeof(size_t)); + current_offset += sizeof(size_t); + + memcpy(memory_buffer + current_offset, (void*)&function_pointers_{atom_name}[0], sizeof(function_pointers_{atom_name})); + current_offset += sizeof(function_pointers_{atom_name}); + + memcpy(memory_buffer + current_offset, (void*)&num_function_trampolines, sizeof(size_t)); + current_offset += sizeof(size_t); + + memcpy(memory_buffer + current_offset, (void*)&function_trampolines_{atom_name}[0], sizeof(function_trampolines_{atom_name})); + current_offset += sizeof(function_trampolines_{atom_name}); + + memcpy(memory_buffer + current_offset, (void*)&num_dynamic_function_trampoline_pointers, sizeof(size_t)); + current_offset += sizeof(size_t); + + memcpy(memory_buffer + current_offset, (void*)&dynamic_function_trampoline_pointers_{atom_name}[0], sizeof(dynamic_function_trampoline_pointers_{atom_name})); + current_offset += sizeof(dynamic_function_trampoline_pointers_{atom_name}); + + wasm_byte_vec_t module_byte_vec = {{ + .size = buffer_size, + .data = memory_buffer, + }}; + return module_byte_vec; + }} + + wasm_module_t* wasmer_object_module_new_{atom_name}(wasm_store_t* store, const char* wasm_name) {{ + // wasm_name intentionally unused for now: will be used in the future. + wasm_byte_vec_t module_byte_vec = generate_serialized_data_{atom_name}(); + wasm_module_t* module = wasm_module_deserialize(store, &module_byte_vec); + free(module_byte_vec.data); + + return module; + }} + ") +} + +/// Generate the header file that goes with the generated object file. +pub fn generate_header_file( + atom_name: &str, + module_info: &ModuleInfo, + symbol_registry: &dyn SymbolRegistry, + metadata_length: usize, +) -> String { + let mut c_statements = vec![ + CStatement::LiteralConstant { + value: "#include \"wasmer.h\"\n#include \n#include \n\n" + .to_string(), + }, + CStatement::LiteralConstant { + value: "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n".to_string(), + }, + CStatement::Declaration { + name: format!("module_bytes_len_{atom_name}"), + is_extern: false, + is_const: true, + ctype: CType::U32, + definition: Some(Box::new(CStatement::LiteralConstant { + value: metadata_length.to_string(), + })), + }, + CStatement::Declaration { + name: symbol_registry.symbol_to_name(Symbol::Metadata), + is_extern: true, + is_const: true, + ctype: CType::Array { + inner: Box::new(CType::U8), + }, + definition: None, + }, + ]; + let function_declarations = module_info + .functions + .iter() + .filter_map(|(f_index, sig_index)| { + Some((module_info.local_func_index(f_index)?, sig_index)) + }) + .map(|(function_local_index, _sig_index)| { + let function_name = + symbol_registry.symbol_to_name(Symbol::LocalFunction(function_local_index)); + // TODO: figure out the signature here too + CStatement::Declaration { + name: function_name, + is_extern: true, + is_const: false, + ctype: CType::Function { + arguments: vec![CType::Void], + return_value: None, + }, + definition: None, + } + }); + c_statements.push(CStatement::LiteralConstant { + value: r#" +// Compiled Wasm function pointers ordered by function index: the order they +// appeared in in the Wasm module. +"# + .to_string(), + }); + c_statements.extend(function_declarations); + + // function pointer array + { + let function_pointer_array_statements = module_info + .functions + .iter() + .filter_map(|(f_index, sig_index)| { + Some((module_info.local_func_index(f_index)?, sig_index)) + }) + .map(|(function_local_index, _sig_index)| { + let function_name = + symbol_registry.symbol_to_name(Symbol::LocalFunction(function_local_index)); + // TODO: figure out the signature here too + + CStatement::Cast { + target_type: CType::void_ptr(), + expression: Box::new(CStatement::LiteralConstant { + value: function_name, + }), + } + }) + .collect::>(); + + c_statements.push(CStatement::Declaration { + name: format!("function_pointers_{atom_name}"), + is_extern: false, + is_const: true, + ctype: CType::Array { + inner: Box::new(CType::void_ptr()), + }, + definition: Some(Box::new(CStatement::LiteralArray { + items: function_pointer_array_statements, + })), + }); + } + + let func_trampoline_declarations = + module_info + .signatures + .iter() + .map(|(sig_index, _func_type)| { + let function_name = + symbol_registry.symbol_to_name(Symbol::FunctionCallTrampoline(sig_index)); + + CStatement::Declaration { + name: function_name, + is_extern: true, + is_const: false, + ctype: CType::Function { + arguments: vec![CType::void_ptr(), CType::void_ptr(), CType::void_ptr()], + return_value: None, + }, + definition: None, + } + }); + c_statements.push(CStatement::LiteralConstant { + value: r#" +// Trampolines (functions by which we can call into Wasm) ordered by signature. +// There is 1 trampoline per function signature in the order they appear in +// the Wasm module. +"# + .to_string(), + }); + c_statements.extend(func_trampoline_declarations); + + // function trampolines + { + let function_trampoline_statements = module_info + .signatures + .iter() + .map(|(sig_index, _vm_shared_index)| { + let function_name = + symbol_registry.symbol_to_name(Symbol::FunctionCallTrampoline(sig_index)); + CStatement::LiteralConstant { + value: function_name, + } + }) + .collect::>(); + + c_statements.push(CStatement::Declaration { + name: format!("function_trampolines_{atom_name}"), + is_extern: false, + is_const: true, + ctype: CType::Array { + inner: Box::new(CType::void_ptr()), + }, + definition: Some(Box::new(CStatement::LiteralArray { + items: function_trampoline_statements, + })), + }); + } + + let dyn_func_declarations = module_info + .functions + .keys() + .take(module_info.num_imported_functions) + .map(|func_index| { + let function_name = + symbol_registry.symbol_to_name(Symbol::DynamicFunctionTrampoline(func_index)); + // TODO: figure out the signature here + CStatement::Declaration { + name: function_name, + is_extern: true, + is_const: false, + ctype: CType::Function { + arguments: vec![CType::void_ptr(), CType::void_ptr(), CType::void_ptr()], + return_value: None, + }, + definition: None, + } + }); + c_statements.push(CStatement::LiteralConstant { + value: r#" +// Dynamic trampolines are per-function and are used for each function where +// the type signature is not known statically. In this case, this corresponds to +// the imported functions. +"# + .to_string(), + }); + c_statements.extend(dyn_func_declarations); + + c_statements.push(CStatement::TypeDef { + source_type: CType::Function { + arguments: vec![CType::void_ptr(), CType::void_ptr(), CType::void_ptr()], + return_value: None, + }, + new_name: "dyn_func_trampoline_t".to_string(), + }); + + // dynamic function trampoline pointer array + { + let dynamic_function_trampoline_statements = module_info + .functions + .keys() + .take(module_info.num_imported_functions) + .map(|func_index| { + let function_name = + symbol_registry.symbol_to_name(Symbol::DynamicFunctionTrampoline(func_index)); + CStatement::LiteralConstant { + value: function_name, + } + }) + .collect::>(); + c_statements.push(CStatement::Declaration { + name: format!("dynamic_function_trampoline_pointers_{atom_name}"), + is_extern: false, + is_const: true, + ctype: CType::Array { + inner: Box::new(CType::TypeDef("dyn_func_trampoline_t".to_string())), + }, + definition: Some(Box::new(CStatement::LiteralArray { + items: dynamic_function_trampoline_statements, + })), + }); + } + + c_statements.push(CStatement::LiteralConstant { + value: gen_helper_functions(atom_name, &symbol_registry.symbol_to_name(Symbol::Metadata)), + }); + + c_statements.push(CStatement::LiteralConstant { + value: "\n#ifdef __cplusplus\n}\n#endif\n\n".to_string(), + }); + + generate_c(&c_statements) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/add.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/add.rs new file mode 100644 index 0000000..44cba7a --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/add.rs @@ -0,0 +1,209 @@ +use std::process::{Command, Stdio}; + +use anyhow::{Context, Error}; +use clap::Parser; +use wasmer_registry::{wasmer_env::WasmerEnv, Bindings, ProgrammingLanguage}; + +/// Add a Wasmer package's bindings to your application. +#[derive(Debug, Parser)] +pub struct Add { + #[clap(flatten)] + env: WasmerEnv, + /// Add the JavaScript bindings using "npm install". + #[clap(long, groups = &["bindings", "js"])] + npm: bool, + /// Add the JavaScript bindings using "yarn add". + #[clap(long, groups = &["bindings", "js"])] + yarn: bool, + /// Add the JavaScript bindings using "pnpm add". + #[clap(long, groups = &["bindings", "js"])] + pnpm: bool, + /// Add the package as a dev-dependency. + #[clap(long, requires = "js")] + dev: bool, + /// Add the Python bindings using "pip install". + #[clap(long, groups = &["bindings", "py"])] + pip: bool, + /// The packages to add (e.g. "wasmer/wasmer-pack@0.5.0" or "python/python") + packages: Vec, +} + +impl Add { + /// Execute [`Add`]. + pub fn execute(&self) -> Result<(), Error> { + anyhow::ensure!(!self.packages.is_empty(), "No packages specified"); + + let registry = self + .env + .registry_endpoint() + .context("Unable to determine which registry to use")?; + + let bindings = self.lookup_bindings(registry.as_str())?; + + let mut cmd = self.target()?.command(&bindings)?; + cmd.stdin(Stdio::null()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()); + + println!("Running: {cmd:?}"); + + let status = cmd.status().with_context(|| { + format!( + "Unable to start \"{:?}\". Is it installed?", + cmd.get_program() + ) + })?; + + anyhow::ensure!(status.success(), "Command failed: {:?}", cmd); + + Ok(()) + } + + fn lookup_bindings(&self, registry: &str) -> Result, Error> { + println!("Querying Wasmer for package bindings"); + + let mut bindings_to_add = Vec::new(); + let language = self.target()?.language(); + + for pkg in &self.packages { + let bindings = lookup_bindings_for_package(registry, pkg, &language) + .with_context(|| format!("Unable to find bindings for {pkg}"))?; + bindings_to_add.push(bindings); + } + + Ok(bindings_to_add) + } + + fn target(&self) -> Result { + match (self.pip, self.npm, self.yarn, self.pnpm) { + (false, false, false, false) => Err(anyhow::anyhow!( + "at least one of --npm, --pip, --yarn or --pnpm has to be specified" + )), + (true, false, false, false) => Ok(Target::Pip), + (false, true, false, false) => Ok(Target::Npm { dev: self.dev }), + (false, false, true, false) => Ok(Target::Yarn { dev: self.dev }), + (false, false, false, true) => Ok(Target::Pnpm { dev: self.dev }), + _ => Err(anyhow::anyhow!( + "only one of --npm, --pip or --yarn has to be specified" + )), + } + } +} + +fn lookup_bindings_for_package( + registry: &str, + pkg: &wasmer_registry::Package, + language: &ProgrammingLanguage, +) -> Result { + let all_bindings = + wasmer_registry::list_bindings(registry, &pkg.package(), pkg.version.as_deref())?; + + match all_bindings.iter().find(|b| b.language == *language) { + Some(b) => { + let Bindings { url, generator, .. } = b; + log::debug!("Found {pkg} bindings generated by {generator} at {url}"); + + Ok(b.clone()) + } + None => { + if all_bindings.is_empty() { + anyhow::bail!("The package doesn't contain any bindings"); + } else { + todo!(); + } + } + } +} + +#[derive(Debug, Copy, Clone)] +enum Target { + Pip, + Yarn { dev: bool }, + Npm { dev: bool }, + Pnpm { dev: bool }, +} + +impl Target { + fn language(self) -> ProgrammingLanguage { + match self { + Target::Pip => ProgrammingLanguage::PYTHON, + Target::Pnpm { .. } | Target::Yarn { .. } | Target::Npm { .. } => { + ProgrammingLanguage::JAVASCRIPT + } + } + } + + /// Construct a command which we can run to add packages. + /// + /// This deliberately runs the command using the OS shell instead of + /// invoking the tool directly. That way we can handle when a version + /// manager (e.g. `nvm` or `asdf`) replaces the tool with a script (e.g. + /// `npm.cmd` or `yarn.ps1`). + /// + /// See for more. + fn command(self, packages: &[Bindings]) -> Result { + let command_line = match self { + Target::Pip => { + if Command::new("pip").arg("--version").output().is_ok() { + "pip install" + } else if Command::new("pip3").arg("--version").output().is_ok() { + "pip3 install" + } else if Command::new("python").arg("--version").output().is_ok() { + "python -m pip install" + } else if Command::new("python3").arg("--version").output().is_ok() { + "python3 -m pip install" + } else { + return Err(anyhow::anyhow!( + "neither pip, pip3, python or python3 installed" + )); + } + } + Target::Yarn { dev } => { + if Command::new("yarn").arg("--version").output().is_err() { + return Err(anyhow::anyhow!("yarn not installed")); + } + if dev { + "yarn add --dev" + } else { + "yarn add" + } + } + Target::Npm { dev } => { + if Command::new("npm").arg("--version").output().is_err() { + return Err(anyhow::anyhow!("npm not installed")); + } + if dev { + "npm install --dev" + } else { + "npm install" + } + } + Target::Pnpm { dev } => { + if Command::new("pnpm").arg("--version").output().is_err() { + return Err(anyhow::anyhow!("pnpm not installed")); + } + if dev { + "pnpm add --dev" + } else { + "pnpm add" + } + } + }; + let mut command_line = command_line.to_string(); + + for pkg in packages { + command_line.push(' '); + command_line.push_str(&pkg.url); + } + + if cfg!(windows) { + let mut cmd = Command::new("cmd"); + cmd.arg("/C").arg(command_line); + Ok(cmd) + } else { + let mut cmd = Command::new("sh"); + cmd.arg("-c").arg(command_line); + Ok(cmd) + } + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/create.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/create.rs new file mode 100644 index 0000000..b8ca499 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/create.rs @@ -0,0 +1,717 @@ +//! Create a new Edge app. + +use std::path::PathBuf; + +use anyhow::{bail, Context}; +use colored::Colorize; +use dialoguer::Confirm; +use edge_schema::schema::StringWebcIdent; +use is_terminal::IsTerminal; +use wasmer_api::{ + types::{DeployAppVersion, Package, UserWithNamespaces}, + WasmerClient, +}; + +use crate::{ + commands::{ + app::{deploy_app_verbose, AppConfigV1, DeployAppOpts, WaitMode}, + AsyncCliCommand, + }, + opts::{ApiOpts, ItemFormatOpts}, + utils::package_wizard::{CreateMode, PackageType, PackageWizard}, +}; + +/// Create a new Edge app. +#[derive(clap::Parser, Debug)] +pub struct CmdAppCreate { + #[clap(name = "type", short = 't', long)] + template: Option, + + #[clap(long)] + publish_package: bool, + + /// Skip local schema validation. + #[clap(long)] + pub no_validate: bool, + + /// Do not prompt for user input. + #[clap(long)] + pub non_interactive: bool, + + /// Do not interact with any APIs. + #[clap(long)] + pub offline: bool, + + /// The owner of the app. + #[clap(long)] + pub owner: Option, + + /// Name to use when creating a new package. + #[clap(long)] + pub new_package_name: Option, + + /// The name of the app (can be changed later) + #[clap(long)] + pub name: Option, + + /// The path to a YAML file the app config. + #[clap(long)] + pub path: Option, + + /// Do not wait for the app to become reachable. + #[clap(long)] + pub no_wait: bool, + + // Common args. + #[clap(flatten)] + #[allow(missing_docs)] + pub api: ApiOpts, + + #[clap(flatten)] + #[allow(missing_docs)] + pub fmt: ItemFormatOpts, + + /// Name of the package to use. + #[clap(long, short = 'p')] + pub package: Option, +} + +/// App type. +#[derive(clap::ValueEnum, Clone, Copy, Debug)] +pub enum AppType { + /// A HTTP server. + #[clap(name = "http")] + HttpServer, + /// A static website. + #[clap(name = "static-website")] + StaticWebsite, + /// Wraps another package to run in the browser. + #[clap(name = "browser-shell")] + BrowserShell, + /// Winter-js based JS-Worker + #[clap(name = "js-worker")] + JsWorker, + /// Python worker + #[clap(name = "py-application")] + PyApplication, +} + +struct AppCreator { + package: Option, + new_package_name: Option, + app_name: Option, + type_: AppType, + interactive: bool, + dir: PathBuf, + owner: String, + api: Option, + user: Option, + local_package: Option<(PathBuf, wasmer_toml::Manifest)>, +} + +struct AppCreatorOutput { + app: AppConfigV1, + api_pkg: Option, + local_package: Option<(PathBuf, wasmer_toml::Manifest)>, +} + +impl AppCreator { + async fn build_browser_shell_app(self) -> Result { + const WASM_BROWSER_CONTAINER_PACKAGE: &str = "wasmer/wasmer-sh"; + const WASM_BROWSER_CONTAINER_VERSION: &str = "0.2"; + + eprintln!("A browser web shell wraps another package and runs it in the browser"); + eprintln!("Select the package to wrap."); + + let (inner_pkg, _inner_pkg_api) = crate::utils::prompt_for_package( + "Package", + None, + Some(crate::utils::PackageCheckMode::MustExist), + self.api.as_ref(), + ) + .await?; + + eprintln!("What should be the name of the wrapper package?"); + + let default_name = format!("{}-webshell", inner_pkg.0.name); + let outer_pkg_name = + crate::utils::prompts::prompt_for_ident("Package name", Some(&default_name))?; + let outer_pkg_full_name = format!("{}/{}", self.owner, outer_pkg_name); + + eprintln!("What should be the name of the app?"); + + let default_name = if outer_pkg_name.ends_with("webshell") { + format!("{}-{}", self.owner, outer_pkg_name) + } else { + format!("{}-{}-webshell", self.owner, outer_pkg_name) + }; + let app_name = crate::utils::prompts::prompt_for_ident("App name", Some(&default_name))?; + + // Build the package. + + let public_dir = self.dir.join("public"); + if !public_dir.exists() { + std::fs::create_dir_all(&public_dir)?; + } + + let init = serde_json::json!({ + "init": format!("{}/{}", inner_pkg.0.namespace, inner_pkg.0.name), + "prompt": inner_pkg.0.name, + "no_welcome": true, + "connect": format!("wss://{app_name}.wasmer.app/.well-known/edge-vpn"), + }); + let init_path = public_dir.join("init.json"); + std::fs::write(&init_path, init.to_string()) + .with_context(|| format!("Failed to write to '{}'", init_path.display()))?; + + let package = wasmer_toml::PackageBuilder::new( + outer_pkg_full_name, + "0.1.0".parse().unwrap(), + format!("{} web shell", inner_pkg.0.name), + ) + .rename_commands_to_raw_command_name(false) + .build()?; + + let manifest = wasmer_toml::ManifestBuilder::new(package) + .with_dependency( + WASM_BROWSER_CONTAINER_PACKAGE, + WASM_BROWSER_CONTAINER_VERSION.to_string().parse().unwrap(), + ) + .map_fs("public", PathBuf::from("public")) + .build()?; + + let manifest_path = self.dir.join("wasmer.toml"); + + let raw = manifest.to_string()?; + eprintln!( + "Writing wasmer.toml package to '{}'", + manifest_path.display() + ); + std::fs::write(&manifest_path, raw)?; + + let app_cfg = AppConfigV1 { + app_id: None, + name: app_name, + owner: Some(self.owner.clone()), + cli_args: None, + env: Default::default(), + volumes: None, + domains: None, + scaling: None, + package: edge_schema::schema::StringWebcIdent(edge_schema::schema::WebcIdent { + repository: None, + namespace: self.owner, + name: outer_pkg_name, + tag: None, + }), + capabilities: None, + scheduled_tasks: None, + debug: Some(false), + extra: Default::default(), + }; + + Ok(AppCreatorOutput { + app: app_cfg, + api_pkg: None, + local_package: Some((self.dir, manifest)), + }) + } + + async fn build_app(self) -> Result { + let package_opt: Option = if let Some(package) = self.package { + Some(package.parse()?) + } else if let Some((_, local)) = self.local_package.as_ref() { + let full = format!("{}@{}", local.package.name, local.package.version); + let mut pkg_ident = StringWebcIdent::parse(&local.package.name) + .with_context(|| format!("local package manifest has invalid name: '{full}'"))?; + + // Pin the version. + pkg_ident.0.tag = Some(local.package.version.to_string()); + + if self.interactive { + eprintln!("Found local package: '{}'", full.green()); + + let msg = format!("Use package '{pkg_ident}'"); + + let should_use = Confirm::new() + .with_prompt(&msg) + .interact_opt()? + .unwrap_or_default(); + + if should_use { + Some(pkg_ident) + } else { + None + } + } else { + Some(pkg_ident) + } + } else { + None + }; + + let (pkg, api_pkg, local_package) = if let Some(pkg) = package_opt { + if let Some(api) = &self.api { + let p2 = wasmer_api::query::get_package( + api, + format!("{}/{}", pkg.0.namespace, pkg.0.name), + ) + .await?; + + (pkg, p2, self.local_package) + } else { + (pkg, None, self.local_package) + } + } else { + eprintln!("No package found or specified."); + + let ty = match self.type_ { + AppType::HttpServer => None, + AppType::StaticWebsite => Some(PackageType::StaticWebsite), + AppType::BrowserShell => None, + AppType::JsWorker => Some(PackageType::JsWorker), + AppType::PyApplication => Some(PackageType::PyApplication), + }; + + let create_mode = match ty { + Some(PackageType::StaticWebsite) + | Some(PackageType::JsWorker) + | Some(PackageType::PyApplication) => CreateMode::Create, + // Only static website creation is currently supported. + _ => CreateMode::SelectExisting, + }; + + let w = PackageWizard { + path: self.dir.clone(), + name: self.new_package_name.clone(), + type_: ty, + create_mode, + namespace: Some(self.owner.clone()), + namespace_default: self.user.as_ref().map(|u| u.username.clone()), + user: self.user.clone(), + }; + + let output = w.run(self.api.as_ref()).await?; + ( + output.ident, + output.api, + output + .local_path + .and_then(move |x| Some((x, output.local_manifest?))), + ) + }; + + let name = if let Some(name) = self.app_name { + name + } else { + let default = match self.type_ { + AppType::HttpServer | AppType::StaticWebsite => { + format!("{}-{}", pkg.0.namespace, pkg.0.name) + } + AppType::JsWorker | AppType::PyApplication => { + format!("{}-{}-worker", pkg.0.namespace, pkg.0.name) + } + AppType::BrowserShell => { + format!("{}-{}-webshell", pkg.0.namespace, pkg.0.name) + } + }; + + dialoguer::Input::new() + .with_prompt("What should be the name of the app? .wasmer.app") + .with_initial_text(default) + .interact_text() + .unwrap() + }; + + let cli_args = match self.type_ { + AppType::PyApplication => Some(vec!["/src/main.py".to_string()]), + AppType::JsWorker => Some(vec!["/src/index.js".to_string()]), + _ => None, + }; + + // TODO: check if name already exists. + let cfg = AppConfigV1 { + app_id: None, + owner: Some(self.owner.clone()), + volumes: None, + name, + env: Default::default(), + scaling: None, + // CLI args are only set for JS and Py workers for now. + cli_args, + // TODO: allow setting the description. + // description: Some("".to_string()), + package: pkg.clone(), + capabilities: None, + scheduled_tasks: None, + debug: Some(false), + domains: None, + extra: Default::default(), + }; + + Ok(AppCreatorOutput { + app: cfg, + api_pkg, + local_package, + }) + } +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdAppCreate { + type Output = (AppConfigV1, Option); + + async fn run_async(self) -> Result<(AppConfigV1, Option), anyhow::Error> { + let interactive = self.non_interactive == false && std::io::stdin().is_terminal(); + + let base_path = if let Some(p) = self.path { + p + } else { + std::env::current_dir()? + }; + + let (base_dir, appcfg_path) = if base_path.is_file() { + let dir = base_path + .canonicalize()? + .parent() + .context("could not determine parent directory")? + .to_owned(); + + (dir, base_path) + } else if base_path.is_dir() { + let full = base_path.join(AppConfigV1::CANONICAL_FILE_NAME); + (base_path, full) + } else { + bail!("No such file or directory: '{}'", base_path.display()); + }; + + if appcfg_path.is_file() { + bail!( + "App configuration file already exists at '{}'", + appcfg_path.display() + ); + } + + let api = if self.offline { + None + } else { + Some(self.api.client()?) + }; + + let user = if let Some(api) = &api { + let u = wasmer_api::query::current_user_with_namespaces( + api, + Some(wasmer_api::types::GrapheneRole::Admin), + ) + .await?; + Some(u) + } else { + None + }; + + let type_ = match self.template { + Some(t) => t, + None => { + if interactive { + let index = dialoguer::Select::new() + .with_prompt("App type") + .default(0) + .items(&[ + "Static website", + "HTTP server", + "Browser shell", + "JS Worker (WinterJS)", + "Python Application", + ]) + .interact()?; + match index { + 0 => AppType::StaticWebsite, + 1 => AppType::HttpServer, + 2 => AppType::BrowserShell, + 3 => AppType::JsWorker, + 4 => AppType::PyApplication, + x => panic!("unhandled app type index '{x}'"), + } + } else { + bail!("No app type specified: use --type XXX"); + } + } + }; + + let owner = if let Some(owner) = self.owner { + owner + } else if interactive { + crate::utils::prompts::prompt_for_namespace( + "Who should own this package?", + None, + user.as_ref(), + )? + } else { + bail!("No owner specified: use --owner XXX"); + }; + + let allow_local_package = match type_ { + AppType::HttpServer => true, + AppType::StaticWebsite => true, + AppType::BrowserShell => false, + AppType::JsWorker => true, + AppType::PyApplication => true, + }; + + let local_package = if allow_local_package { + match crate::utils::load_package_manifest(&base_dir) { + Ok(Some(p)) => Some(p), + Ok(None) => None, + Err(err) => { + eprintln!( + "{warning}: could not load package manifest: {err}", + warning = "Warning".yellow(), + ); + None + } + } + } else { + None + }; + + let creator = AppCreator { + app_name: self.name, + new_package_name: self.new_package_name, + package: self.package, + type_, + interactive, + dir: base_dir, + owner: owner.clone(), + api, + user, + local_package, + }; + + let output = match type_ { + AppType::HttpServer + | AppType::StaticWebsite + | AppType::JsWorker + | AppType::PyApplication => creator.build_app().await?, + AppType::BrowserShell => creator.build_browser_shell_app().await?, + }; + + let AppCreatorOutput { + app: cfg, + api_pkg, + local_package, + .. + } = output; + + let deploy_now = if self.offline { + false + } else if self.non_interactive { + true + } else { + Confirm::new() + .with_prompt("Would you like to publish the app now?".to_string()) + .interact()? + }; + + // Make sure to write out the app.yaml to avoid not creating it when the + // publish or deploy step fails. + // (the later flow only writes a new app.yaml after a success) + let raw_app_config = cfg.clone().to_yaml()?; + std::fs::write(&appcfg_path, raw_app_config).with_context(|| { + format!("could not write app config to '{}'", appcfg_path.display()) + })?; + + let (final_config, app_version) = if deploy_now { + eprintln!("Creating the app..."); + + let api = self.api.client()?; + + if api_pkg.is_none() { + if let Some((path, manifest)) = &local_package { + eprintln!("Publishing package..."); + let manifest = manifest.clone(); + crate::utils::republish_package_with_bumped_version(&api, path, manifest) + .await?; + } + } + + let raw_config = cfg.clone().to_yaml()?; + std::fs::write(&appcfg_path, raw_config).with_context(|| { + format!("could not write config to '{}'", appcfg_path.display()) + })?; + + let wait_mode = if self.no_wait { + WaitMode::Deployed + } else { + WaitMode::Reachable + }; + + let opts = DeployAppOpts { + app: &cfg, + original_config: None, + allow_create: true, + make_default: true, + owner: Some(owner.clone()), + wait: wait_mode, + }; + let (_app, app_version) = deploy_app_verbose(&api, opts).await?; + + let new_cfg = super::app_config_from_api(&app_version)?; + (new_cfg, Some(app_version)) + } else { + (cfg, None) + }; + + eprintln!("Writing app config to '{}'", appcfg_path.display()); + let raw_final_config = final_config.clone().to_yaml()?; + std::fs::write(&appcfg_path, raw_final_config) + .with_context(|| format!("could not write config to '{}'", appcfg_path.display()))?; + + eprintln!("To (re)deploy your app, run 'wasmer deploy'"); + + Ok((final_config, app_version)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_app_create_static_site_offline() { + let dir = tempfile::tempdir().unwrap(); + + let cmd = CmdAppCreate { + template: Some(AppType::StaticWebsite), + publish_package: false, + no_validate: false, + non_interactive: true, + offline: true, + owner: Some("testuser".to_string()), + new_package_name: Some("static-site-1".to_string()), + name: Some("static-site-1".to_string()), + path: Some(dir.path().to_owned()), + no_wait: true, + api: ApiOpts::default(), + fmt: ItemFormatOpts::default(), + package: None, + }; + cmd.run_async().await.unwrap(); + + let app = std::fs::read_to_string(dir.path().join("app.yaml")).unwrap(); + assert_eq!( + app, + r#"--- +kind: wasmer.io/App.v0 +name: static-site-1 +owner: testuser +package: testuser/static-site-1@0.1.0 +debug: false +"#, + ); + } + + #[tokio::test] + async fn test_app_create_offline_with_package() { + let dir = tempfile::tempdir().unwrap(); + + let cmd = CmdAppCreate { + template: Some(AppType::HttpServer), + publish_package: false, + no_validate: false, + non_interactive: true, + offline: true, + owner: Some("wasmer".to_string()), + new_package_name: None, + name: Some("testapp".to_string()), + path: Some(dir.path().to_owned()), + no_wait: true, + api: ApiOpts::default(), + fmt: ItemFormatOpts::default(), + package: Some("wasmer/testpkg".to_string()), + }; + cmd.run_async().await.unwrap(); + + let app = std::fs::read_to_string(dir.path().join("app.yaml")).unwrap(); + assert_eq!( + app, + r#"--- +kind: wasmer.io/App.v0 +name: testapp +owner: wasmer +package: wasmer/testpkg +debug: false +"#, + ); + } + #[tokio::test] + async fn test_app_create_js_worker() { + let dir = tempfile::tempdir().unwrap(); + + let cmd = CmdAppCreate { + template: Some(AppType::JsWorker), + publish_package: false, + no_validate: false, + non_interactive: true, + offline: true, + owner: Some("wasmer".to_string()), + new_package_name: None, + name: Some("test-js-worker".to_string()), + path: Some(dir.path().to_owned()), + no_wait: true, + api: ApiOpts::default(), + fmt: ItemFormatOpts::default(), + package: Some("wasmer/test-js-worker".to_string()), + }; + cmd.run_async().await.unwrap(); + + let app = std::fs::read_to_string(dir.path().join("app.yaml")).unwrap(); + assert_eq!( + app, + r#"--- +kind: wasmer.io/App.v0 +name: test-js-worker +owner: wasmer +package: wasmer/test-js-worker +cli_args: + - /src/index.js +debug: false +"#, + ); + } + + #[tokio::test] + async fn test_app_create_py_worker() { + let dir = tempfile::tempdir().unwrap(); + + let cmd = CmdAppCreate { + template: Some(AppType::PyApplication), + publish_package: false, + no_validate: false, + non_interactive: true, + offline: true, + owner: Some("wasmer".to_string()), + new_package_name: None, + name: Some("test-py-worker".to_string()), + path: Some(dir.path().to_owned()), + no_wait: true, + api: ApiOpts::default(), + fmt: ItemFormatOpts::default(), + package: Some("wasmer/test-py-worker".to_string()), + }; + cmd.run_async().await.unwrap(); + + let app = std::fs::read_to_string(dir.path().join("app.yaml")).unwrap(); + assert_eq!( + app, + r#"--- +kind: wasmer.io/App.v0 +name: test-py-worker +owner: wasmer +package: wasmer/test-py-worker +cli_args: + - /src/main.py +debug: false +"#, + ); + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/delete.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/delete.rs new file mode 100644 index 0000000..fa6d0b1 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/delete.rs @@ -0,0 +1,61 @@ +//! Delete an Edge app. + +use dialoguer::Confirm; +use is_terminal::IsTerminal; + +use super::util::AppIdentOpts; +use crate::{commands::AsyncCliCommand, opts::ApiOpts}; + +/// Show an app. +#[derive(clap::Parser, Debug)] +pub struct CmdAppDelete { + #[clap(flatten)] + api: ApiOpts, + + #[clap(long)] + non_interactive: bool, + + #[clap(flatten)] + ident: AppIdentOpts, +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdAppDelete { + type Output = (); + + async fn run_async(self) -> Result<(), anyhow::Error> { + let interactive = std::io::stdin().is_terminal() && !self.non_interactive; + let client = self.api.client()?; + + eprintln!("Looking up the app..."); + let (_ident, app) = self.ident.load_app(&client).await?; + + if interactive { + let should_use = Confirm::new() + .with_prompt(&format!( + "Really delete the app '{}/{}'? (id: {})", + app.owner.global_name, + app.name, + app.id.inner() + )) + .interact()?; + + if !should_use { + eprintln!("App will not be deleted."); + return Ok(()); + } + } + + eprintln!( + "Deleting app {}/{} (id: {})...", + app.owner.global_name, + app.name, + app.id.inner(), + ); + wasmer_api::query::delete_app(&client, app.id.into_inner()).await?; + + eprintln!("App '{}/{}' was deleted!", app.owner.global_name, app.name); + + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/get.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/get.rs new file mode 100644 index 0000000..5c16636 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/get.rs @@ -0,0 +1,38 @@ +//! Get information about an edge app. + +use wasmer_api::types::DeployApp; + +use super::util::AppIdentOpts; +use crate::{ + commands::AsyncCliCommand, + opts::{ApiOpts, ItemFormatOpts}, +}; + +/// Show an app. +#[derive(clap::Parser, Debug)] +pub struct CmdAppGet { + #[clap(flatten)] + #[allow(missing_docs)] + pub api: ApiOpts, + #[clap(flatten)] + #[allow(missing_docs)] + pub fmt: ItemFormatOpts, + + #[clap(flatten)] + #[allow(missing_docs)] + pub ident: AppIdentOpts, +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdAppGet { + type Output = DeployApp; + + async fn run_async(self) -> Result { + let client = self.api.client()?; + let (_ident, app) = self.ident.load_app(&client).await?; + + println!("{}", self.fmt.format.render(&app)); + + Ok(app) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/info.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/info.rs new file mode 100644 index 0000000..5455bfd --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/info.rs @@ -0,0 +1,38 @@ +//! Show short information about an Edge app. + +use super::util::AppIdentOpts; +use crate::{commands::AsyncCliCommand, opts::ApiOpts}; + +/// Show short information about an Edge app. +/// +/// Use `app get` to get more detailed information. +#[derive(clap::Parser, Debug)] +pub struct CmdAppInfo { + #[clap(flatten)] + api: ApiOpts, + #[clap(flatten)] + ident: AppIdentOpts, +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdAppInfo { + type Output = (); + + async fn run_async(self) -> Result<(), anyhow::Error> { + let client = self.api.client()?; + let (_ident, app) = self.ident.load_app(&client).await?; + + let app_url = app.url; + let versioned_url = app.active_version.url; + let dashboard_url = app.admin_url; + + println!(" App Info "); + println!("> App Name: {}", app.name); + println!("> Namespace: {}", app.owner.global_name); + println!("> App URL: {}", app_url); + println!("> Versioned URL: {}", versioned_url); + println!("> Admin dashboard: {}", dashboard_url); + + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/list.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/list.rs new file mode 100644 index 0000000..053879e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/list.rs @@ -0,0 +1,104 @@ +//! List Edge apps. + +use std::pin::Pin; + +use futures::{Stream, StreamExt}; +use wasmer_api::types::DeployApp; + +use crate::{ + commands::AsyncCliCommand, + opts::{ApiOpts, ListFormatOpts}, +}; + +/// List apps. +#[derive(clap::Parser, Debug)] +pub struct CmdAppList { + #[clap(flatten)] + fmt: ListFormatOpts, + #[clap(flatten)] + api: ApiOpts, + + /// Get apps in a specific namespace. + /// + /// Will fetch the apps owned by the current user otherwise. + #[clap(short = 'n', long)] + namespace: Option, + + /// Get all apps that are accessible by the current user, including apps + /// directly owned by the user and apps in namespaces the user can access. + #[clap(short = 'a', long)] + all: bool, + + /// Maximum number of apps to display + #[clap(long, default_value = "1000")] + max: usize, + + /// Asks whether to display the next page or not + #[clap(long, default_value = "false")] + paging_mode: bool, +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdAppList { + type Output = (); + + async fn run_async(self) -> Result<(), anyhow::Error> { + let client = self.api.client()?; + + let apps_stream: Pin< + Box, anyhow::Error>> + Send + Sync>, + > = if let Some(ns) = self.namespace.clone() { + Box::pin(wasmer_api::query::namespace_apps(&client, ns).await) + } else if self.all { + Box::pin(wasmer_api::query::user_accessible_apps(&client).await?) + } else { + Box::pin(wasmer_api::query::user_apps(&client).await) + }; + + let mut apps_stream = std::pin::pin!(apps_stream); + + let mut rem = self.max; + + let mut display_apps = vec![]; + + 'list: while let Some(apps) = apps_stream.next().await { + let mut apps = apps?; + + let limit = std::cmp::min(apps.len(), rem); + + if limit == 0 { + break; + } + + rem -= limit; + + if self.paging_mode { + println!("{}", self.fmt.format.render(&apps)); + + loop { + println!("next page? [y, n]"); + + let mut rsp = String::new(); + std::io::stdin().read_line(&mut rsp)?; + + if rsp.trim() == "y" { + continue 'list; + } + if rsp.trim() == "n" { + break 'list; + } + + println!("uknown response: {rsp}"); + } + } + + display_apps.extend(apps.drain(..limit)); + } + + if !display_apps.is_empty() { + println!("{}", self.fmt.format.render(&display_apps)); + } + + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/logs.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/logs.rs new file mode 100644 index 0000000..d6792ad --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/logs.rs @@ -0,0 +1,204 @@ +//! Show logs for an Edge app. + +use comfy_table::Table; +use edge_schema::pretty_duration::parse_timestamp_or_relative_time; +use futures::StreamExt; +use time::{format_description::well_known::Rfc3339, OffsetDateTime}; +use wasmer_api::types::{Log, LogStream}; + +use crate::{ + opts::{ApiOpts, ListFormatOpts}, + utils::{render::CliRender, Identifier}, +}; + +#[derive(Debug, PartialEq, Eq, Clone, Copy, clap::ValueEnum)] +pub enum LogStreamArg { + Stdout, + Stderr, +} + +/// Show an app. +#[derive(clap::Parser, Debug)] +pub struct CmdAppLogs { + #[clap(flatten)] + api: ApiOpts, + #[clap(flatten)] + fmt: ListFormatOpts, + + /// The date of the earliest log entry. + /// + /// Defaults to the last 10 minutes. + /// + /// Format: + /// * RFC 3339 (`2006-01-02T03:04:05-07:00`) + /// * RFC 2822 (`Mon, 02 Jan 2006 03:04:05 MST`) + /// * Simple date (`2022-11-11`) + /// * Unix timestamp (`1136196245`) + /// * Relative time (`10m` / `-1h`, `1d1h30s`) + // TODO: should default to trailing logs once trailing is implemented. + #[clap(long, value_parser = parse_timestamp_or_relative_time)] + from: Option, + + /// The date of the latest log entry. + /// + /// Format: + /// * RFC 3339 (`2006-01-02T03:04:05-07:00`) + /// * RFC 2822 (`Mon, 02 Jan 2006 03:04:05 MST`) + /// * Simple date (`2022-11-11`) + /// * Unix timestamp (`1136196245`) + /// * Relative time (`10m` / `1h`, `1d1h30s`) + #[clap(long, value_parser = parse_timestamp_or_relative_time)] + until: Option, + + /// Maximum log lines to fetch. + /// Defaults to 1000. + #[clap(long, default_value = "1000")] + max: usize, + + #[clap(long, default_value = "false")] + watch: bool, + + /// The name of the app. + /// + /// Eg: + /// - name (assumes current user) + /// - namespace/name + /// - namespace/name@version + ident: Identifier, + + /// Streams of logs to display + #[clap(long, value_delimiter = ',', value_enum)] + streams: Option>, +} + +#[async_trait::async_trait] +impl crate::commands::AsyncCliCommand for CmdAppLogs { + type Output = (); + + async fn run_async(self) -> Result<(), anyhow::Error> { + let client = self.api.client()?; + + let Identifier { + name, + owner, + version, + } = &self.ident; + + let owner = match owner { + Some(owner) => owner.to_string(), + None => { + let user = wasmer_api::query::current_user_with_namespaces(&client, None).await?; + user.username + } + }; + + let from = self + .from + .unwrap_or_else(|| OffsetDateTime::now_utc() - time::Duration::minutes(10)); + + tracing::info!( + package.name=%self.ident.name, + package.owner=%owner, + package.version=self.ident.version.as_deref(), + range.start=%from, + range.end=self.until.map(|ts| ts.to_string()), + "Fetching logs", + ); + + let (stdout, stderr) = self + .streams + .map(|s| { + let mut stdout = false; + let mut stderr = false; + + for stream in s { + if matches!(stream, LogStreamArg::Stdout) { + stdout = true; + } else if matches!(stream, LogStreamArg::Stderr) { + stderr = true; + } + } + + (stdout, stderr) + }) + .unwrap_or_default(); + + let streams = Vec::from(match (stdout, stderr) { + (true, true) | (false, false) => &[LogStream::Stdout, LogStream::Stderr][..], + (true, false) => &[LogStream::Stdout][..], + (false, true) => &[LogStream::Stderr][..], + }); + + let logs_stream = wasmer_api::query::get_app_logs_paginated( + &client, + name.clone(), + owner.to_string(), + version.clone(), + from, + self.until, + self.watch, + Some(streams), + ) + .await; + + let mut logs_stream = std::pin::pin!(logs_stream); + + let mut rem = self.max; + + while let Some(logs) = logs_stream.next().await { + let mut logs = logs?; + + let limit = std::cmp::min(logs.len(), rem); + + let logs: Vec<_> = logs.drain(..limit).collect(); + + if !logs.is_empty() { + let rendered = self.fmt.format.render(&logs); + println!("{rendered}"); + + rem -= limit; + } + + if !self.watch || rem == 0 { + break; + } + } + + Ok(()) + } +} + +impl CliRender for Log { + fn render_item_table(&self) -> String { + let mut table = Table::new(); + + let Log { message, timestamp } = self; + + table.add_rows([ + vec![ + "Timestamp".to_string(), + datetime_from_unix(*timestamp).format(&Rfc3339).unwrap(), + ], + vec!["Message".to_string(), message.to_string()], + ]); + table.to_string() + } + + fn render_list_table(items: &[Self]) -> String { + let mut table = Table::new(); + table.set_header(vec!["Timestamp".to_string(), "Message".to_string()]); + + for item in items { + table.add_row([ + datetime_from_unix(item.timestamp).format(&Rfc3339).unwrap(), + item.message.clone(), + ]); + } + table.to_string() + } +} + +fn datetime_from_unix(timestamp: f64) -> OffsetDateTime { + OffsetDateTime::from_unix_timestamp_nanos(timestamp as i128) + .expect("Timestamp should always be valid") +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/mod.rs new file mode 100644 index 0000000..3743f87 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/mod.rs @@ -0,0 +1,243 @@ +//! Edge app commands. + +pub mod create; +pub mod delete; +pub mod get; +pub mod info; +pub mod list; +pub mod logs; +pub mod version; + +mod util; + +use std::{io::Write, time::Duration}; + +use anyhow::{bail, Context}; +use edge_schema::schema::AppConfigV1; +use wasmer_api::{ + types::{DeployApp, DeployAppVersion}, + WasmerClient, +}; + +use crate::commands::AsyncCliCommand; + +/// Manage Wasmer Deploy apps. +#[derive(clap::Subcommand, Debug)] +pub enum CmdApp { + Get(get::CmdAppGet), + Info(info::CmdAppInfo), + List(list::CmdAppList), + Logs(logs::CmdAppLogs), + Create(create::CmdAppCreate), + Delete(delete::CmdAppDelete), + #[clap(subcommand)] + Version(version::CmdAppVersion), +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdApp { + type Output = (); + + async fn run_async(self) -> Result<(), anyhow::Error> { + match self { + Self::Get(cmd) => { + cmd.run_async().await?; + Ok(()) + } + Self::Info(cmd) => { + cmd.run_async().await?; + Ok(()) + } + Self::Create(cmd) => { + cmd.run_async().await?; + Ok(()) + } + Self::List(cmd) => cmd.run_async().await, + Self::Logs(cmd) => cmd.run_async().await, + Self::Delete(cmd) => cmd.run_async().await, + Self::Version(cmd) => cmd.run_async().await, + } + } +} + +pub struct DeployAppOpts<'a> { + pub app: &'a AppConfigV1, + // Original raw yaml config. + // Present here to enable forwarding unknown fields to the backend, which + // preserves forwards-compatibility for schema changes. + pub original_config: Option, + pub allow_create: bool, + pub make_default: bool, + pub owner: Option, + pub wait: WaitMode, +} + +pub async fn deploy_app( + client: &WasmerClient, + opts: DeployAppOpts<'_>, +) -> Result { + let app = opts.app; + + let config_value = app.clone().to_yaml_value()?; + let final_config = if let Some(old) = &opts.original_config { + crate::utils::merge_yaml_values(old, &config_value) + } else { + config_value + }; + let mut raw_config = serde_yaml::to_string(&final_config)?.trim().to_string(); + raw_config.push('\n'); + + // TODO: respect allow_create flag + + let version = wasmer_api::query::publish_deploy_app( + client, + wasmer_api::types::PublishDeployAppVars { + config: raw_config, + name: app.name.clone().into(), + owner: opts.owner.map(|o| o.into()), + make_default: Some(opts.make_default), + }, + ) + .await + .context("could not create app in the backend")?; + + Ok(version) +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum WaitMode { + /// Wait for the app to be deployed. + Deployed, + /// Wait for the app to be deployed and ready. + Reachable, +} + +/// Same as [Self::deploy], but also prints verbose information. +pub async fn deploy_app_verbose( + client: &WasmerClient, + opts: DeployAppOpts<'_>, +) -> Result<(DeployApp, DeployAppVersion), anyhow::Error> { + let owner = &opts.owner.clone().or_else(|| opts.app.owner.clone()); + let app = &opts.app; + + let pretty_name = if let Some(owner) = &owner { + format!("{}/{}", owner, app.name) + } else { + app.name.clone() + }; + + let make_default = opts.make_default; + + eprintln!("Deploying app {pretty_name}...\n"); + + let wait = opts.wait; + let version = deploy_app(client, opts).await?; + + let app_id = version + .app + .as_ref() + .context("app field on app version is empty")? + .id + .inner() + .to_string(); + + let app = wasmer_api::query::get_app_by_id(client, app_id.clone()) + .await + .context("could not fetch app from backend")?; + + let full_name = format!("{}/{}", app.owner.global_name, app.name); + + eprintln!(" ✅ App {full_name} was successfully deployed!"); + eprintln!(); + eprintln!("> App URL: {}", app.url); + eprintln!("> Versioned URL: {}", version.url); + eprintln!("> Admin dashboard: {}", app.admin_url); + + match wait { + WaitMode::Deployed => {} + WaitMode::Reachable => { + eprintln!(); + eprintln!("Waiting for new deployment to become available..."); + eprintln!("(You can safely stop waiting now with CTRL-C)"); + + let stderr = std::io::stderr(); + + tokio::time::sleep(Duration::from_secs(2)).await; + + let start = tokio::time::Instant::now(); + let client = reqwest::Client::new(); + + let check_url = if make_default { &app.url } else { &version.url }; + + let mut sleep_millis: u64 = 1_000; + loop { + let total_elapsed = start.elapsed(); + if total_elapsed > Duration::from_secs(60 * 5) { + eprintln!(); + bail!("\nApp still not reachable after 5 minutes..."); + } + + { + let mut lock = stderr.lock(); + write!(&mut lock, ".").unwrap(); + lock.flush().unwrap(); + } + + let request_start = tokio::time::Instant::now(); + + match client.get(check_url).send().await { + Ok(res) => { + let header = res + .headers() + .get(edge_util::headers::HEADER_APP_VERSION_ID) + .and_then(|x| x.to_str().ok()) + .unwrap_or_default(); + + if header == version.id.inner() { + eprintln!("\nNew version is now reachable at {check_url}"); + eprintln!("Deployment complete"); + break; + } + + tracing::debug!( + current=%header, + expected=%app.active_version.id.inner(), + "app is not at the right version yet", + ); + } + Err(err) => { + tracing::debug!(?err, "health check request failed"); + } + }; + + let elapsed: u64 = request_start + .elapsed() + .as_millis() + .try_into() + .unwrap_or_default(); + let to_sleep = Duration::from_millis(sleep_millis.saturating_sub(elapsed)); + tokio::time::sleep(to_sleep).await; + sleep_millis = (sleep_millis * 2).max(10_000); + } + } + } + + Ok((app, version)) +} + +pub fn app_config_from_api(version: &DeployAppVersion) -> Result { + let app_id = version + .app + .as_ref() + .context("app field on app version is empty")? + .id + .inner() + .to_string(); + + let cfg = &version.user_yaml_config; + let mut cfg = AppConfigV1::parse_yaml(cfg) + .context("could not parse app config from backend app version")?; + + cfg.app_id = Some(app_id); + Ok(cfg) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/util.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/util.rs new file mode 100644 index 0000000..53d5987 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/util.rs @@ -0,0 +1,186 @@ +use anyhow::{bail, Context}; +use edge_schema::schema::AppConfigV1; +use wasmer_api::{ + global_id::{GlobalId, NodeKind}, + types::DeployApp, + WasmerClient, +}; + +/// App identifier. +/// +/// Can be either a namespace/name a plain name or an app id. +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum AppIdent { + AppId(String), + NamespacedName(String, String), + Alias(String), +} + +impl AppIdent { + /// Resolve an app identifier through the API. + pub async fn resolve(&self, client: &WasmerClient) -> Result { + match self { + AppIdent::AppId(app_id) => wasmer_api::query::get_app_by_id(client, app_id.clone()) + .await + .with_context(|| format!("Could not find app with id '{}'", app_id)), + AppIdent::Alias(name) => wasmer_api::query::get_app_by_alias(client, name.clone()) + .await? + .with_context(|| format!("Could not find app with name '{name}'")), + AppIdent::NamespacedName(owner, name) => { + wasmer_api::query::get_app(client, owner.clone(), name.clone()) + .await? + .with_context(|| format!("Could not find app '{owner}/{name}'")) + } + } + } +} + +impl std::str::FromStr for AppIdent { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + if let Some((namespace, name)) = s.split_once('/') { + if namespace.is_empty() { + bail!("invalid app identifier '{s}': namespace can not be empty"); + } + if name.is_empty() { + bail!("invalid app identifier '{s}': name can not be empty"); + } + + Ok(Self::NamespacedName( + namespace.to_string(), + name.to_string(), + )) + } else if let Ok(id) = GlobalId::parse_prefixed(s) { + if id.kind() == NodeKind::DeployApp { + Ok(Self::AppId(s.to_string())) + } else { + bail!( + "invalid app identifier '{s}': expected an app id, but id is of type {kind}", + kind = id.kind(), + ); + } + } else { + Ok(Self::Alias(s.to_string())) + } + } +} + +pub fn get_app_config_from_current_dir() -> Result<(AppConfigV1, std::path::PathBuf), anyhow::Error> +{ + // read the information from local `app.yaml + let current_dir = std::env::current_dir()?; + let app_config_path = current_dir.join(AppConfigV1::CANONICAL_FILE_NAME); + + if !app_config_path.exists() || !app_config_path.is_file() { + bail!( + "Could not find app.yaml at path: '{}'.\nPlease specify an app like 'wasmer app get /' or 'wasmer app get `'", + app_config_path.display() + ); + } + // read the app.yaml + let raw_app_config = std::fs::read_to_string(&app_config_path) + .with_context(|| format!("Could not read file '{}'", app_config_path.display()))?; + + // parse the app.yaml + let config = AppConfigV1::parse_yaml(&raw_app_config) + .map_err(|err| anyhow::anyhow!("Could not parse app.yaml: {err:?}"))?; + + Ok((config, app_config_path)) +} + +/// Options for identifying an app. +/// +/// Provides convenience methods for resolving an app identifier or loading it +/// from a local app.yaml. +#[derive(clap::Parser, Debug)] +pub struct AppIdentOpts { + /// Identifier of the application. + /// + /// NOTE: If not specified, the command will look for an app config file in + /// the current directory. + /// + /// Valid input: + /// - namespace/app-name + /// - app-alias + /// - App ID + pub app_ident: Option, +} + +// Allowing because this is not performance-critical at all. +#[allow(clippy::large_enum_variant)] +pub enum ResolvedAppIdent { + Ident(AppIdent), + Config { + ident: AppIdent, + config: AppConfigV1, + path: std::path::PathBuf, + }, +} + +impl ResolvedAppIdent { + pub fn ident(&self) -> &AppIdent { + match self { + Self::Ident(ident) => ident, + Self::Config { ident, .. } => ident, + } + } +} + +impl AppIdentOpts { + pub fn resolve_static(&self) -> Result { + if let Some(id) = &self.app_ident { + return Ok(ResolvedAppIdent::Ident(id.clone())); + } + + // Try to load from local. + let (config, path) = get_app_config_from_current_dir()?; + + let ident = if let Some(id) = &config.app_id { + AppIdent::AppId(id.clone()) + } else { + AppIdent::Alias(config.name.clone()) + }; + + Ok(ResolvedAppIdent::Config { + ident, + config, + path, + }) + } + + /// Load the specified app from the API. + pub async fn load_app( + &self, + client: &WasmerClient, + ) -> Result<(ResolvedAppIdent, DeployApp), anyhow::Error> { + let id = self.resolve_static()?; + let app = id.ident().resolve(client).await?; + + Ok((id, app)) + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use super::*; + + #[test] + fn test_app_ident() { + assert_eq!( + AppIdent::from_str("da_MRrWI0t5U582").unwrap(), + AppIdent::AppId("da_MRrWI0t5U582".to_string()), + ); + assert_eq!( + AppIdent::from_str("lala").unwrap(), + AppIdent::Alias("lala".to_string()), + ); + + assert_eq!( + AppIdent::from_str("alpha/beta").unwrap(), + AppIdent::NamespacedName("alpha".to_string(), "beta".to_string()), + ); + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/version/activate.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/version/activate.rs new file mode 100644 index 0000000..e54341e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/version/activate.rs @@ -0,0 +1,43 @@ +use crate::{ + commands::AsyncCliCommand, + opts::{ApiOpts, ItemFormatOpts}, +}; + +/// Switch the active version of an app. (rollback / rollforward) +#[derive(clap::Parser, Debug)] +pub struct CmdAppVersionActivate { + #[clap(flatten)] + #[allow(missing_docs)] + pub api: ApiOpts, + #[clap(flatten)] + #[allow(missing_docs)] + pub fmt: ItemFormatOpts, + + /// App version ID to activate. + /// + /// This must be the unique version ID, not the version name! + /// Eg: dav_xYzaB1aaaaax + pub version: String, +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdAppVersionActivate { + type Output = (); + + async fn run_async(self) -> Result<(), anyhow::Error> { + let client = self.api.client()?; + + let app = wasmer_api::query::app_version_activate(&client, self.version).await?; + + eprintln!( + "Changed active version of app '{}/{}' from '{}' to '{}' (id: {})", + app.owner.global_name, + app.name, + app.active_version.version, + app.active_version.version, + app.active_version.id.inner() + ); + + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/version/get.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/version/get.rs new file mode 100644 index 0000000..c13f569 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/version/get.rs @@ -0,0 +1,48 @@ +use anyhow::Context; + +use crate::{ + commands::{app::util::AppIdentOpts, AsyncCliCommand}, + opts::{ApiOpts, ItemFormatOpts}, +}; + +/// Show information for a specific app version. +#[derive(clap::Parser, Debug)] +pub struct CmdAppVersionGet { + #[clap(flatten)] + #[allow(missing_docs)] + pub api: ApiOpts, + #[clap(flatten)] + #[allow(missing_docs)] + pub fmt: ItemFormatOpts, + + /// *Name* of the version - NOT the unique version id! + #[clap(long)] + pub name: String, + + #[clap(flatten)] + #[allow(missing_docs)] + pub ident: AppIdentOpts, +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdAppVersionGet { + type Output = (); + + async fn run_async(self) -> Result<(), anyhow::Error> { + let client = self.api.client()?; + let (_ident, app) = self.ident.load_app(&client).await?; + + let version = wasmer_api::query::get_app_version( + &client, + app.owner.global_name, + app.name, + self.name.clone(), + ) + .await? + .with_context(|| format!("Could not find app version '{}'", self.name))?; + + println!("{}", self.fmt.format.render(&version)); + + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/version/list.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/version/list.rs new file mode 100644 index 0000000..c184cd0 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/version/list.rs @@ -0,0 +1,107 @@ +use wasmer_api::types::{DeployAppVersionsSortBy, GetDeployAppVersionsVars}; + +use crate::{ + commands::{app::util::AppIdentOpts, AsyncCliCommand}, + opts::{ApiOpts, ListFormatOpts}, +}; + +/// List versions of an app. +#[derive(clap::Parser, Debug)] +pub struct CmdAppVersionList { + #[clap(flatten)] + #[allow(missing_docs)] + pub api: ApiOpts, + #[allow(missing_docs)] + #[clap(flatten)] + pub fmt: ListFormatOpts, + + /// Get all versions of the app. + /// Overrides pagination flags (--max, --offset). + #[clap(short = 'a', long)] + all: bool, + + /// Pagination offset - get versions after this offset. + /// + /// See also: --max, --before, --after + #[clap(long)] + offset: Option, + + /// Maximum number of items to return. + /// + /// See also: --offset, --before, --after + #[clap(long)] + max: Option, + + /// Pagination cursor - get versions before this version. + /// + /// See also: --max, --offset, --after + #[clap(long)] + before: Option, + + /// Pagination cursor - get versions after this version. + /// + /// See also: --max, --offset, --before + #[clap(long)] + after: Option, + + #[clap(long)] + sort: Option, + + #[clap(flatten)] + #[allow(missing_docs)] + pub ident: AppIdentOpts, +} + +#[derive(Clone, Copy, clap::ValueEnum, Debug)] +enum Sort { + Newest, + Oldest, +} + +impl From for DeployAppVersionsSortBy { + fn from(val: Sort) -> Self { + match val { + Sort::Newest => DeployAppVersionsSortBy::Newest, + Sort::Oldest => DeployAppVersionsSortBy::Oldest, + } + } +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdAppVersionList { + type Output = (); + + async fn run_async(self) -> Result<(), anyhow::Error> { + let client = self.api.client()?; + let (_ident, app) = self.ident.load_app(&client).await?; + + let versions = if self.all { + wasmer_api::query::all_app_versions(&client, app.owner.global_name, app.name).await? + } else { + let vars = GetDeployAppVersionsVars { + owner: app.owner.global_name, + name: app.name, + offset: self.offset.map(|x| x as i32), + before: self.before, + after: self.after, + first: self.max.map(|x| x as i32), + last: None, + sort_by: self.sort.map(|x| x.into()), + }; + + let versions = + wasmer_api::query::get_deploy_app_versions(&client, vars.clone()).await?; + + versions + .edges + .into_iter() + .flatten() + .filter_map(|edge| edge.node) + .collect::>() + }; + + println!("{}", self.fmt.format.render(&versions)); + + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/version/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/version/mod.rs new file mode 100644 index 0000000..86315bb --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/app/version/mod.rs @@ -0,0 +1,28 @@ +//! Edge app version management. + +use crate::commands::AsyncCliCommand; + +pub mod activate; +pub mod get; +pub mod list; + +/// Manage app versions. +#[derive(clap::Subcommand, Debug)] +pub enum CmdAppVersion { + Get(get::CmdAppVersionGet), + List(list::CmdAppVersionList), + Activate(activate::CmdAppVersionActivate), +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdAppVersion { + type Output = (); + + async fn run_async(self) -> Result<(), anyhow::Error> { + match self { + Self::Get(cmd) => cmd.run_async().await, + Self::List(cmd) => cmd.run_async().await, + Self::Activate(cmd) => cmd.run_async().await, + } + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/binfmt.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/binfmt.rs new file mode 100644 index 0000000..271386e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/binfmt.rs @@ -0,0 +1,155 @@ +use std::{ + env, fs, + io::Write, + os::unix::{ffi::OsStrExt, fs::MetadataExt}, + path::{Path, PathBuf}, +}; + +use anyhow::{bail, Context, Result}; +use clap::Parser; +use Action::*; + +#[derive(Debug, Parser, Clone, Copy)] +enum Action { + /// Register wasmer as binfmt interpreter + Register, + /// Unregister a binfmt interpreter for wasm32 + Unregister, + /// Soft unregister, and register + Reregister, +} + +/// Unregister and/or register wasmer as binfmt interpreter +/// +/// Check the wasmer repository for a systemd service definition example +/// to automate the process at start-up. +#[derive(Debug, Parser)] +pub struct Binfmt { + // Might be better to traverse the mount list + /// Mount point of binfmt_misc fs + #[clap(long, default_value = "/proc/sys/fs/binfmt_misc/")] + binfmt_misc: PathBuf, + + #[clap(subcommand)] + action: Action, +} + +// Quick safety check: +// This folder isn't world writeable (or else its sticky bit is set), and neither are its parents. +// +// If somebody mounted /tmp wrong, this might result in a TOCTOU problem. +fn seccheck(path: &Path) -> Result<()> { + if let Some(parent) = path.parent() { + seccheck(parent)?; + } + let m = std::fs::metadata(path) + .with_context(|| format!("Can't check permissions of {}", path.to_string_lossy()))?; + use unix_mode::*; + anyhow::ensure!( + !is_allowed(Accessor::Other, Access::Write, m.mode()) || is_sticky(m.mode()), + "{} is world writeable and not sticky", + path.to_string_lossy() + ); + Ok(()) +} + +impl Binfmt { + /// The filename used to register the wasmer CLI as a binfmt interpreter. + pub const FILENAME: &str = "wasmer-binfmt-interpreter"; + + /// execute [Binfmt] + pub fn execute(&self) -> Result<()> { + if !self.binfmt_misc.exists() { + panic!("{} does not exist", self.binfmt_misc.to_string_lossy()); + } + let temp_dir; + let specs = match self.action { + Register | Reregister => { + temp_dir = tempfile::tempdir().context("Make temporary directory")?; + seccheck(temp_dir.path())?; + let bin_path_orig: PathBuf = env::current_exe() + .and_then(|p| p.canonicalize()) + .context("Cannot get path to wasmer executable")?; + let bin_path = temp_dir.path().join(Binfmt::FILENAME); + fs::copy(bin_path_orig, &bin_path).context("Copy wasmer binary to temp folder")?; + let bin_path = fs::canonicalize(&bin_path).with_context(|| { + format!( + "Couldn't get absolute path for {}", + bin_path.to_string_lossy() + ) + })?; + Some([ + [ + b":wasm32:M::\\x00asm\\x01\\x00\\x00::".as_ref(), + bin_path.as_os_str().as_bytes(), + b":PFC", + ] + .concat(), + [ + b":wasm32-wat:E::wat::".as_ref(), + bin_path.as_os_str().as_bytes(), + b":PFC", + ] + .concat(), + ]) + } + _ => None, + }; + let wasm_registration = self.binfmt_misc.join("wasm32"); + let wat_registration = self.binfmt_misc.join("wasm32-wat"); + match self.action { + Reregister | Unregister => { + let unregister = [wasm_registration, wat_registration] + .iter() + .map(|registration| { + if registration.exists() { + let mut registration = fs::OpenOptions::new() + .write(true) + .open(registration) + .context("Open existing binfmt entry to remove")?; + registration + .write_all(b"-1") + .context("Couldn't write binfmt unregister request")?; + Ok(true) + } else { + eprintln!( + "Warning: {} does not exist, not unregistered.", + registration.to_string_lossy() + ); + Ok(false) + } + }) + .collect::>() + .into_iter() + .collect::>>()?; + if let (Unregister, false) = (self.action, unregister.into_iter().any(|b| b)) { + bail!("Nothing unregistered"); + } + } + _ => (), + }; + if let Some(specs) = specs { + if cfg!(target_env = "gnu") { + // Approximate. ELF parsing for a proper check feels like overkill here. + eprintln!("Warning: wasmer has been compiled for glibc, and is thus likely dynamically linked. Invoking wasm binaries in chroots or mount namespaces (lxc, docker, ...) may not work."); + } + specs + .iter() + .map(|spec| { + let register = self.binfmt_misc.join("register"); + let mut register = fs::OpenOptions::new() + .write(true) + .open(register) + .context("Open binfmt misc for registration")?; + register + .write_all(spec) + .context("Couldn't register binfmt")?; + Ok(()) + }) + .collect::>() + .into_iter() + .collect::>>()?; + } + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/cache.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/cache.rs new file mode 100644 index 0000000..bd6ff0f --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/cache.rs @@ -0,0 +1,51 @@ +use std::{fs, path::Path}; + +use anyhow::Result; +use clap::Parser; +use wasmer_registry::wasmer_env::WasmerEnv; + +#[derive(Debug, Parser)] +/// The options for the `wasmer cache` subcommand +pub struct Cache { + #[clap(flatten)] + env: WasmerEnv, + /// The operation to perform. + #[clap(subcommand)] + cmd: Cmd, +} + +impl Cache { + /// Execute the cache command + pub fn execute(&self) -> Result<()> { + let cache_dir = self.env.cache_dir(); + + match self.cmd { + Cmd::Clean => { + clean(&cache_dir)?; + } + Cmd::Dir => { + println!("{}", self.env.cache_dir().display()); + } + } + + Ok(()) + } +} + +#[derive(Debug, Copy, Clone, Parser)] +enum Cmd { + /// Clear the cache + Clean, + /// Display the location of the cache + Dir, +} + +fn clean(cache_dir: &Path) -> Result<()> { + if cache_dir.exists() { + fs::remove_dir_all(cache_dir)?; + } + fs::create_dir_all(cache_dir)?; + eprintln!("Wasmer cache cleaned successfully."); + + Ok(()) +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/compile.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/compile.rs new file mode 100644 index 0000000..9739b28 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/compile.rs @@ -0,0 +1,86 @@ +use std::path::PathBuf; + +use anyhow::{Context, Result}; +use clap::Parser; +use wasmer::*; + +use crate::{store::StoreOptions, warning}; + +#[derive(Debug, Parser)] +/// The options for the `wasmer compile` subcommand +pub struct Compile { + /// Input file + #[clap(name = "FILE")] + path: PathBuf, + + /// Output file + #[clap(name = "OUTPUT PATH", short = 'o')] + output: PathBuf, + + /// Compilation Target triple + #[clap(long = "target")] + target_triple: Option, + + #[clap(flatten)] + store: StoreOptions, + + #[clap(short = 'm')] + cpu_features: Vec, +} + +impl Compile { + /// Runs logic for the `compile` subcommand + pub fn execute(&self) -> Result<()> { + self.inner_execute() + .context(format!("failed to compile `{}`", self.path.display())) + } + + fn inner_execute(&self) -> Result<()> { + let target = self + .target_triple + .as_ref() + .map(|target_triple| { + let mut features = self + .cpu_features + .clone() + .into_iter() + .fold(CpuFeature::set(), |a, b| a | b); + // Cranelift requires SSE2, so we have this "hack" for now to facilitate + // usage + if target_triple.architecture == Architecture::X86_64 { + features |= CpuFeature::SSE2; + } + Target::new(target_triple.clone(), features) + }) + .unwrap_or_default(); + let (store, compiler_type) = self.store.get_store_for_target(target.clone())?; + let output_filename = self + .output + .file_stem() + .map(|osstr| osstr.to_string_lossy().to_string()) + .unwrap_or_default(); + // wasmu stands for "WASM Universal" + let recommended_extension = "wasmu"; + match self.output.extension() { + Some(ext) => { + if ext != recommended_extension { + warning!("the output file has a wrong extension. We recommend using `{}.{}` for the chosen target", &output_filename, &recommended_extension) + } + } + None => { + warning!("the output file has no extension. We recommend using `{}.{}` for the chosen target", &output_filename, &recommended_extension) + } + } + println!("Compiler: {}", compiler_type.to_string()); + println!("Target: {}", target.triple()); + + let module = Module::from_file(&store, &self.path)?; + module.serialize_to_file(&self.output)?; + eprintln!( + "✔ File compiled successfully to `{}`.", + self.output.display(), + ); + + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/config.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/config.rs new file mode 100644 index 0000000..261a189 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/config.rs @@ -0,0 +1,304 @@ +use std::str::ParseBoolError; + +use anyhow::{Context, Result}; +use clap::Parser; +use wasmer_registry::{wasmer_env::WasmerEnv, WasmerConfig}; + +use crate::VERSION; + +#[derive(Debug, Parser)] +/// The options for the `wasmer config` subcommand: `wasmer config get --OPTION` or `wasmer config set [FLAG]` +pub struct Config { + #[clap(flatten)] + env: WasmerEnv, + + #[clap(flatten)] + flags: Flags, + /// Subcommand for `wasmer config get | set` + #[clap(subcommand)] + set: Option, +} + +/// Normal configuration +#[derive(Debug, Parser)] +pub struct Flags { + /// Print the installation prefix. + #[clap(long, conflicts_with = "pkg_config")] + prefix: bool, + + /// Directory containing Wasmer executables. + #[clap(long, conflicts_with = "pkg_config")] + bindir: bool, + + /// Directory containing Wasmer headers. + #[clap(long, conflicts_with = "pkg_config")] + includedir: bool, + + /// Directory containing Wasmer libraries. + #[clap(long, conflicts_with = "pkg_config")] + libdir: bool, + + /// Libraries needed to link against Wasmer components. + #[clap(long, conflicts_with = "pkg_config")] + libs: bool, + + /// C compiler flags for files that include Wasmer headers. + #[clap(long, conflicts_with = "pkg_config")] + cflags: bool, + + /// Print the path to the wasmer configuration file where all settings are stored + #[clap(long, conflicts_with = "pkg_config")] + config_path: bool, + + /// Outputs the necessary details for compiling + /// and linking a program to Wasmer, using the `pkg-config` format. + #[clap(long)] + pkg_config: bool, +} + +/// Subcommand for `wasmer config set` +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)] +pub enum GetOrSet { + /// `wasmer config get $KEY` + #[clap(subcommand)] + Get(RetrievableConfigField), + /// `wasmer config set $KEY $VALUE` + #[clap(subcommand)] + Set(StorableConfigField), +} + +/// Subcommand for `wasmer config get` +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)] +pub enum RetrievableConfigField { + /// Print the registry URL of the currently active registry + #[clap(name = "registry.url")] + RegistryUrl, + /// Print the token for the currently active registry or nothing if not logged in + #[clap(name = "registry.token")] + RegistryToken, + /// Print whether telemetry is currently enabled + #[clap(name = "telemetry.enabled")] + TelemetryEnabled, + /// Print whether update notifications are enabled + #[clap(name = "update-notifications.enabled")] + UpdateNotificationsEnabled, + /// Print the proxy URL + #[clap(name = "proxy.url")] + ProxyUrl, +} + +/// Setting that can be stored in the wasmer config +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)] +pub enum StorableConfigField { + /// Set the registry URL of the currently active registry + #[clap(name = "registry.url")] + RegistryUrl(SetRegistryUrl), + /// Set the token for the currently active registry or nothing if not logged in + #[clap(name = "registry.token")] + RegistryToken(SetRegistryToken), + /// Set whether telemetry is currently enabled + #[clap(name = "telemetry.enabled")] + TelemetryEnabled(SetTelemetryEnabled), + /// Set whether update notifications are enabled + #[clap(name = "update-notifications.enabled")] + UpdateNotificationsEnabled(SetUpdateNotificationsEnabled), + /// Set the active proxy URL + #[clap(name = "proxy.url")] + ProxyUrl(SetProxyUrl), +} + +/// Set the current active registry URL +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)] +pub struct SetRegistryUrl { + /// Url of the registry + #[clap(name = "URL")] + pub url: String, +} + +/// Set or change the token for the current active registry +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)] +pub struct SetRegistryToken { + /// Token to set + #[clap(name = "TOKEN")] + pub token: String, +} + +/// Set if update notifications are enabled +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)] +pub struct SetUpdateNotificationsEnabled { + /// Whether to enable update notifications + /// + /// ("true" | "false") + #[clap(name = "ENABLED")] + pub enabled: BoolString, +} + +/// "true" or "false" for handling input in the CLI +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct BoolString(pub bool); + +impl std::str::FromStr for BoolString { + type Err = ParseBoolError; + fn from_str(s: &str) -> Result { + Ok(Self(bool::from_str(s)?)) + } +} + +/// Set if telemetry is enabled +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)] +pub struct SetTelemetryEnabled { + /// Whether to enable telemetry + /// + /// ("true" | "false") + #[clap(name = "ENABLED")] + pub enabled: BoolString, +} + +/// Set if a proxy URL should be used +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)] +pub struct SetProxyUrl { + /// Set if a proxy URL should be used (empty = unset proxy) + #[clap(name = "URL")] + pub url: String, +} + +impl Config { + /// Runs logic for the `config` subcommand + pub fn execute(&self) -> Result<()> { + self.inner_execute() + .context("failed to retrieve the wasmer config".to_string()) + } + + fn inner_execute(&self) -> Result<()> { + if let Some(s) = self.set.as_ref() { + return s.execute(&self.env); + } + + let flags = &self.flags; + + let prefix = self.env.dir(); + + let prefixdir = prefix.display().to_string(); + let bindir = prefix.join("bin").display().to_string(); + let includedir = prefix.join("include").display().to_string(); + let libdir = prefix.join("lib").display().to_string(); + let cflags = format!("-I{}", includedir); + let libs = format!("-L{} -lwasmer", libdir); + + if flags.pkg_config { + println!("prefix={}", prefixdir); + println!("exec_prefix={}", bindir); + println!("includedir={}", includedir); + println!("libdir={}", libdir); + println!(); + println!("Name: wasmer"); + println!("Description: The Wasmer library for running WebAssembly"); + println!("Version: {}", VERSION); + println!("Cflags: {}", cflags); + println!("Libs: {}", libs); + return Ok(()); + } + + if flags.prefix { + println!("{}", prefixdir); + } + if flags.bindir { + println!("{}", bindir); + } + if flags.includedir { + println!("{}", includedir); + } + if flags.libdir { + println!("{}", libdir); + } + if flags.libs { + println!("{}", libs); + } + if flags.cflags { + println!("{}", cflags); + } + + if flags.config_path { + let path = WasmerConfig::get_file_location(self.env.dir()); + println!("{}", path.display()); + } + + Ok(()) + } +} + +impl GetOrSet { + fn execute(&self, env: &WasmerEnv) -> Result<()> { + let config_file = WasmerConfig::get_file_location(env.dir()); + let mut config = env.config()?; + + match self { + GetOrSet::Get(g) => match g { + RetrievableConfigField::RegistryUrl => { + println!("{}", config.registry.get_current_registry()); + } + RetrievableConfigField::RegistryToken => { + if let Some(s) = config + .registry + .get_login_token_for_registry(&config.registry.get_current_registry()) + { + println!("{s}"); + } + } + RetrievableConfigField::TelemetryEnabled => { + println!("{:?}", config.telemetry_enabled); + } + RetrievableConfigField::UpdateNotificationsEnabled => { + println!("{:?}", config.update_notifications_enabled); + } + RetrievableConfigField::ProxyUrl => { + if let Some(s) = config.proxy.url.as_ref() { + println!("{s}"); + } else { + println!("none"); + } + } + }, + GetOrSet::Set(s) => { + match s { + StorableConfigField::RegistryUrl(s) => { + config.registry.set_current_registry(&s.url); + let current_registry = config.registry.get_current_registry(); + if let Some(u) = wasmer_registry::utils::get_username(¤t_registry) + .ok() + .and_then(|o| o) + { + println!( + "Successfully logged into registry {current_registry:?} as user {u:?}" + ); + } + } + StorableConfigField::RegistryToken(t) => { + config.registry.set_login_token_for_registry( + &config.registry.get_current_registry(), + &t.token, + wasmer_registry::config::UpdateRegistry::LeaveAsIs, + ); + } + StorableConfigField::TelemetryEnabled(t) => { + config.telemetry_enabled = t.enabled.0; + } + StorableConfigField::ProxyUrl(p) => { + if p.url == "none" || p.url.is_empty() { + config.proxy.url = None; + } else { + config.proxy.url = Some(p.url.clone()); + } + } + StorableConfigField::UpdateNotificationsEnabled(u) => { + config.update_notifications_enabled = u.enabled.0; + } + } + config + .save(config_file) + .with_context(|| anyhow::anyhow!("could not save config file"))?; + } + } + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/connect.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/connect.rs new file mode 100644 index 0000000..42211ac --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/connect.rs @@ -0,0 +1,100 @@ +use std::net::IpAddr; + +use super::AsyncCliCommand; +use crate::opts::ApiOpts; + +/// Connects to the Wasmer Edge distributed network. +#[derive(clap::Parser, Debug)] +pub struct CmdConnect { + #[clap(flatten)] + api: ApiOpts, + + /// Runs in promiscuous mode + #[clap(long)] + pub promiscuous: bool, + /// Prints the network token rather than connecting + #[clap(long)] + pub print: bool, + /// Skips bringing the interface up using the `ip` tool. + #[clap(long)] + pub leave_down: bool, + /// Do not modify the postfix of the URL before connecting + #[clap(long)] + pub leave_postfix: bool, + /// One or more static IP address to assign the interface + #[clap(long)] + pub ip: Vec, + /// Thr URL we will be connecting to + #[clap(index = 1)] + pub url: url::Url, +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdConnect { + type Output = (); + + #[cfg(all(target_os = "linux", feature = "tun-tap"))] + async fn run_async(mut self) -> Result<(), anyhow::Error> { + use edge_schema::{AppId, NetworkIdEncodingMethod, WELL_KNOWN_VPN}; + use virtual_mio::Selector; + + use crate::net::TunTapSocket; + + // If the URL does not include the well known postfix then add it + if !self.leave_postfix { + self.url.set_path(WELL_KNOWN_VPN); + } + + if self.print { + println!("websocket-url: {}", self.url.as_str()); + return Ok(()); + } + + print!("Connecting..."); + let socket = TunTapSocket::create( + Selector::new(), + self.url.clone(), + self.leave_down == false, + self.ip, + ) + .await + .map_err(|err| { + println!("failed"); + err + })?; + println!("\rConnected to {} ", self.url.as_str()); + + for cidr in socket.ips().iter() { + println!("Your IP: {}/{}", cidr.ip, cidr.prefix); + } + for route in socket.routes().iter() { + println!( + "Gateway: {}/{} -> {}", + route.cidr.ip, route.cidr.prefix, route.via_router + ); + } + for cidr in socket.ips().iter() { + if let Some((app_id, _)) = + AppId::from_ip(&cidr.ip, NetworkIdEncodingMethod::PrivateProjection) + { + let ip = app_id.into_ip( + cidr.ip, + 0x00_1001, + NetworkIdEncodingMethod::PrivateProjection, + ); + println!("Instance: {}/{}", ip, cidr.prefix); + } + } + println!("Press ctrl-c to terminate"); + socket.await?; + + Ok(()) + } + + #[cfg(not(all(target_os = "linux", feature = "tun-tap")))] + async fn run_async(self) -> Result<(), anyhow::Error> { + Err(anyhow::anyhow!( + "This CLI does not support the 'connect' command: only available on Linux (feature: tun-tap)" + )) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/container/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/container/mod.rs new file mode 100644 index 0000000..533d1ad --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/container/mod.rs @@ -0,0 +1,12 @@ +mod unpack; + +pub use unpack::PackageUnpack; + +/// Container related commands. (inspecting, unpacking, ...) +#[derive(clap::Subcommand, Debug)] +// Allowing missing_docs because the comment would override the doc comment on +// the command struct. +#[allow(missing_docs)] +pub enum Container { + Unpack(PackageUnpack), +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/container/unpack.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/container/unpack.rs new file mode 100644 index 0000000..0e851c6 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/container/unpack.rs @@ -0,0 +1,118 @@ +use std::path::PathBuf; + +use anyhow::Context; +use dialoguer::console::{style, Emoji}; +use indicatif::ProgressBar; + +/// Extract contents of a container to a directory. +#[derive(clap::Parser, Debug)] +pub struct PackageUnpack { + /// The output directory. + #[clap(short = 'o', long)] + out_dir: PathBuf, + + /// Overwrite existing directories/files. + #[clap(long)] + overwrite: bool, + + /// Run the unpack command without any output + #[clap(long)] + pub quiet: bool, + + /// Path to the package. + package_path: PathBuf, +} + +static PACKAGE_EMOJI: Emoji<'_, '_> = Emoji("đŸ“Ļ ", ""); +static EXTRACTED_TO_EMOJI: Emoji<'_, '_> = Emoji("📂 ", ""); + +impl PackageUnpack { + pub(crate) fn execute(&self) -> Result<(), anyhow::Error> { + // Setup the progress bar + let pb = if self.quiet { + ProgressBar::hidden() + } else { + ProgressBar::new_spinner() + }; + + pb.println(format!( + "{} {}Unpacking...", + style("[1/2]").bold().dim(), + PACKAGE_EMOJI + )); + + let pkg = webc::compat::Container::from_disk(&self.package_path).with_context(|| { + format!( + "could not open package at '{}'", + self.package_path.display() + ) + })?; + + let outdir = &self.out_dir; + std::fs::create_dir_all(outdir) + .with_context(|| format!("could not create output directory '{}'", outdir.display()))?; + + pkg.unpack(outdir, self.overwrite) + .with_context(|| "could not extract package".to_string())?; + + pb.println(format!( + "{} {}Extracted package contents to '{}'", + style("[2/2]").bold().dim(), + EXTRACTED_TO_EMOJI, + self.out_dir.display() + )); + + pb.finish(); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Download a package from the dev registry. + #[test] + fn test_cmd_package_extract() { + let dir = tempfile::tempdir().unwrap(); + + let package_path = std::env::var("CARGO_MANIFEST_DIR").map(PathBuf::from).unwrap() + .parent().unwrap() + .parent().unwrap() + .join("tests/integration/cli/tests/webc/hello-0.1.0-665d2ddc-80e6-4845-85d3-4587b1693bb7.webc"); + + assert!(package_path.is_file()); + + let cmd = PackageUnpack { + out_dir: dir.path().to_owned(), + overwrite: false, + package_path, + quiet: true, + }; + + cmd.execute().unwrap(); + + let mut items = std::fs::read_dir(dir.path()) + .unwrap() + .map(|x| { + x.unwrap() + .path() + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_string() + }) + .collect::>(); + items.sort(); + assert_eq!( + items, + vec![ + "atom".to_string(), + "manifest.json".to_string(), + "metadata".to_string(), + ] + ); + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/create_exe.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/create_exe.rs new file mode 100644 index 0000000..24c1dc6 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/create_exe.rs @@ -0,0 +1,2304 @@ +//! Create a standalone native executable for a given Wasm file. + +use std::{ + collections::BTreeMap, + env, + path::{Path, PathBuf}, + process::{Command, Stdio}, +}; + +use anyhow::{anyhow, bail, Context, Result}; +use clap::Parser; +use serde::{Deserialize, Serialize}; +use tar::Archive; +use wasmer::sys::Artifact; +use wasmer::*; +use wasmer_object::{emit_serialized, get_object_for_target}; +use wasmer_types::{compilation::symbols::ModuleMetadataSymbolRegistry, ModuleInfo}; +use webc::{ + compat::{Container, Volume as WebcVolume}, + PathSegments, +}; + +use self::utils::normalize_atom_name; +use crate::{common::normalize_path, store::CompilerOptions}; + +const LINK_SYSTEM_LIBRARIES_WINDOWS: &[&str] = &["userenv", "Ws2_32", "advapi32", "bcrypt"]; + +const LINK_SYSTEM_LIBRARIES_UNIX: &[&str] = &["dl", "m", "pthread"]; + +#[derive(Debug, Parser)] +/// The options for the `wasmer create-exe` subcommand +pub struct CreateExe { + /// Input file + #[clap(name = "FILE")] + path: PathBuf, + + /// Output file + #[clap(name = "OUTPUT PATH", short = 'o')] + output: PathBuf, + + /// Optional directorey used for debugging: if present, will output the zig command + /// for reproducing issues in a debug directory + #[clap(long, name = "DEBUG PATH")] + debug_dir: Option, + + /// Prefix for every input file, e.g. "wat2wasm:sha256abc123" would + /// prefix every function in the wat2wasm input object with the "sha256abc123" hash + /// + /// If only a single value is given without containing a ":", this value is used for + /// all input files. If no value is given, the prefix is always equal to + /// the sha256 of the input .wasm file + #[clap( + long, + use_value_delimiter = true, + value_delimiter = ',', + name = "FILE:PREFIX:PATH" + )] + precompiled_atom: Vec, + + /// Compilation Target triple + /// + /// Accepted target triple values must follow the + /// ['target_lexicon'](https://crates.io/crates/target-lexicon) crate format. + /// + /// The recommended targets we try to support are: + /// + /// - "x86_64-linux-gnu" + /// - "aarch64-linux-gnu" + /// - "x86_64-apple-darwin" + /// - "arm64-apple-darwin" + /// - "x86_64-windows-gnu" + #[clap(long = "target")] + target_triple: Option, + + /// Can specify either a release version (such as "3.0.1") or a URL to a tarball to use + /// for linking. By default, create-exe will always pull the latest release tarball from GitHub, + /// this flag can be used to override that behaviour. + #[clap(long, name = "URL_OR_RELEASE_VERSION")] + use_wasmer_release: Option, + + #[clap(long, short = 'm', number_of_values = 1)] + cpu_features: Vec, + + /// Additional libraries to link against. + /// This is useful for fixing linker errors that may occur on some systems. + #[clap(long, short = 'l')] + libraries: Vec, + + #[clap(flatten)] + cross_compile: CrossCompile, + + #[clap(flatten)] + compiler: CompilerOptions, +} + +/// Url or version to download the release from +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum UrlOrVersion { + /// URL to download + Url(url::Url), + /// Release version to download + Version(semver::Version), +} + +impl UrlOrVersion { + fn from_str(s: &str) -> Result { + let mut err; + let s = s.strip_prefix('v').unwrap_or(s); + match url::Url::parse(s) { + Ok(o) => return Ok(Self::Url(o)), + Err(e) => { + err = anyhow::anyhow!("could not parse as URL: {e}"); + } + } + + match semver::Version::parse(s) { + Ok(o) => return Ok(Self::Version(o)), + Err(e) => { + err = anyhow::anyhow!("could not parse as URL or version: {e}").context(err); + } + } + + Err(err) + } +} + +// Cross-compilation options with `zig` +#[derive(Debug, Clone, Default, Parser)] +pub(crate) struct CrossCompile { + /// Use the system linker instead of zig for linking + #[clap(long)] + use_system_linker: bool, + + /// Cross-compilation library path (path to libwasmer.a / wasmer.lib) + #[clap(long = "library-path")] + library_path: Option, + + /// Cross-compilation tarball library path + #[clap(long = "tarball")] + tarball: Option, + + /// Specify `zig` binary path (defaults to `zig` in $PATH if not present) + #[clap(long = "zig-binary-path", env)] + zig_binary_path: Option, +} + +#[derive(Debug)] +pub(crate) struct CrossCompileSetup { + pub(crate) target: Triple, + pub(crate) zig_binary_path: Option, + pub(crate) library: PathBuf, +} + +/// Given a pirita file, determines whether the file has one +/// default command as an entrypoint or multiple (need to be specified via --command) +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Entrypoint { + /// Compiled atom files to link into the final binary + pub atoms: Vec, + /// Volume objects (if any) to link into the final binary + pub volumes: Vec, +} + +/// Command entrypoint for multiple commands +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct CommandEntrypoint { + /// Command name + pub command: String, + /// Atom name + pub atom: String, + /// Path to the object file, relative to the entrypoint.json parent dir + pub path: PathBuf, + /// Optional path to the static_defs.h header file, relative to the entrypoint.json parent dir + pub header: Option, + /// Module info, set when the wasm file is compiled + pub module_info: Option, +} + +/// Volume object file (name + path to object file) +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Volume { + /// Volume name + pub name: String, + /// Path to volume fileblock object file + pub obj_file: PathBuf, +} + +impl CreateExe { + /// Runs logic for the `compile` subcommand + pub fn execute(&self) -> Result<()> { + let path = normalize_path(&format!("{}", self.path.display())); + let target_triple = self.target_triple.clone().unwrap_or_else(Triple::host); + let mut cc = self.cross_compile.clone(); + let target = utils::target_triple_to_target(&target_triple, &self.cpu_features); + + let starting_cd = env::current_dir()?; + let input_path = starting_cd.join(path); + let output_path = starting_cd.join(&self.output); + + let url_or_version = match self + .use_wasmer_release + .as_deref() + .map(UrlOrVersion::from_str) + { + Some(Ok(o)) => Some(o), + Some(Err(e)) => return Err(e), + None => None, + }; + + let cross_compilation = + utils::get_cross_compile_setup(&mut cc, &target_triple, &starting_cd, url_or_version)?; + + if input_path.is_dir() { + return Err(anyhow::anyhow!("input path cannot be a directory")); + } + + let (store, compiler_type) = self.compiler.get_store_for_target(target.clone())?; + + println!("Compiler: {}", compiler_type.to_string()); + println!("Target: {}", target.triple()); + println!( + "Using path `{}` as libwasmer path.", + cross_compilation.library.display() + ); + + if !cross_compilation.library.exists() { + return Err(anyhow::anyhow!("library path does not exist")); + } + + let temp = tempfile::tempdir(); + let tempdir = match self.debug_dir.as_ref() { + Some(s) => s.clone(), + None => temp?.path().to_path_buf(), + }; + std::fs::create_dir_all(&tempdir)?; + + let atoms = if let Ok(pirita) = Container::from_disk(&input_path) { + // pirita file + compile_pirita_into_directory( + &pirita, + &tempdir, + &self.compiler, + &self.cpu_features, + &cross_compilation.target, + &self.precompiled_atom, + AllowMultiWasm::Allow, + self.debug_dir.is_some(), + ) + } else { + // wasm file + prepare_directory_from_single_wasm_file( + &input_path, + &tempdir, + &self.compiler, + &cross_compilation.target, + &self.cpu_features, + &self.precompiled_atom, + self.debug_dir.is_some(), + ) + }?; + + get_module_infos(&store, &tempdir, &atoms)?; + let mut entrypoint = get_entrypoint(&tempdir)?; + create_header_files_in_dir(&tempdir, &mut entrypoint, &atoms, &self.precompiled_atom)?; + link_exe_from_dir( + &tempdir, + output_path, + &cross_compilation, + &self.libraries, + self.debug_dir.is_some(), + &atoms, + &self.precompiled_atom, + )?; + + if self.target_triple.is_some() { + eprintln!( + "✔ Cross-compiled executable for `{}` target compiled successfully to `{}`.", + target.triple(), + self.output.display(), + ); + } else { + eprintln!( + "✔ Native executable compiled successfully to `{}`.", + self.output.display(), + ); + } + + Ok(()) + } +} + +fn write_entrypoint(directory: &Path, entrypoint: &Entrypoint) -> Result<(), anyhow::Error> { + std::fs::write( + directory.join("entrypoint.json"), + serde_json::to_string_pretty(&entrypoint).unwrap(), + ) + .map_err(|e| { + anyhow::anyhow!( + "cannot create entrypoint.json dir in {}: {e}", + directory.display() + ) + }) +} + +fn get_entrypoint(directory: &Path) -> Result { + let entrypoint_json = + std::fs::read_to_string(directory.join("entrypoint.json")).map_err(|e| { + anyhow::anyhow!( + "could not read entrypoint.json in {}: {e}", + directory.display() + ) + })?; + + let entrypoint: Entrypoint = serde_json::from_str(&entrypoint_json).map_err(|e| { + anyhow::anyhow!( + "could not parse entrypoint.json in {}: {e}", + directory.display() + ) + })?; + + if entrypoint.atoms.is_empty() { + return Err(anyhow::anyhow!("file has no atoms to compile")); + } + + Ok(entrypoint) +} + +/// In pirita mode, specifies whether multi-atom +/// pirita files should be allowed or rejected +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum AllowMultiWasm { + /// allow + Allow, + /// reject + Reject(Option), +} + +/// Given a pirita file, compiles the .wasm files into the target directory +#[allow(clippy::too_many_arguments)] +pub(super) fn compile_pirita_into_directory( + pirita: &Container, + target_dir: &Path, + compiler: &CompilerOptions, + cpu_features: &[CpuFeature], + triple: &Triple, + prefixes: &[String], + allow_multi_wasm: AllowMultiWasm, + debug: bool, +) -> anyhow::Result)>> { + let all_atoms = match &allow_multi_wasm { + AllowMultiWasm::Allow | AllowMultiWasm::Reject(None) => { + pirita.atoms().into_iter().collect::>() + } + AllowMultiWasm::Reject(Some(s)) => { + let atom = pirita + .get_atom(s) + .with_context(|| format!("could not find atom \"{s}\"",))?; + vec![(s.to_string(), atom)] + } + }; + + allow_multi_wasm.validate(&all_atoms)?; + + std::fs::create_dir_all(target_dir) + .map_err(|e| anyhow::anyhow!("cannot create / dir in {}: {e}", target_dir.display()))?; + + let target_dir = target_dir.canonicalize()?; + let target = &utils::target_triple_to_target(triple, cpu_features); + + std::fs::create_dir_all(target_dir.join("volumes")).map_err(|e| { + anyhow::anyhow!( + "cannot create /volumes dir in {}: {e}", + target_dir.display() + ) + })?; + + let volumes = pirita.volumes(); + let volume_bytes = volume_file_block(&volumes); + let volume_name = "VOLUMES"; + let volume_path = target_dir.join("volumes").join("volume.o"); + write_volume_obj(&volume_bytes, volume_name, &volume_path, target)?; + let volume_path = volume_path.canonicalize()?; + let volume_path = pathdiff::diff_paths(&volume_path, &target_dir).unwrap(); + + std::fs::create_dir_all(target_dir.join("atoms")).map_err(|e| { + anyhow::anyhow!("cannot create /atoms dir in {}: {e}", target_dir.display()) + })?; + + let mut atoms_from_file = Vec::new(); + let mut target_paths = Vec::new(); + + for (atom_name, atom_bytes) in all_atoms { + atoms_from_file.push((utils::normalize_atom_name(&atom_name), atom_bytes.to_vec())); + let atom_path = target_dir + .join("atoms") + .join(format!("{}.o", utils::normalize_atom_name(&atom_name))); + let header_path = { + std::fs::create_dir_all(target_dir.join("include")).map_err(|e| { + anyhow::anyhow!( + "cannot create /include dir in {}: {e}", + target_dir.display() + ) + })?; + + let header_path = target_dir.join("include").join(format!( + "static_defs_{}.h", + utils::normalize_atom_name(&atom_name) + )); + + Some(header_path) + }; + target_paths.push(( + atom_name.clone(), + utils::normalize_atom_name(&atom_name), + atom_path, + header_path, + )); + } + + let prefix_map = PrefixMapCompilation::from_input(&atoms_from_file, prefixes, false) + .with_context(|| anyhow::anyhow!("compile_pirita_into_directory"))?; + + let module_infos = compile_atoms( + &atoms_from_file, + &target_dir.join("atoms"), + compiler, + target, + &prefix_map, + debug, + )?; + + // target_dir + let mut atoms = Vec::new(); + for (command_name, atom_name, a, opt_header_path) in target_paths { + let mut atom_path = a; + let mut header_path = opt_header_path; + if let Ok(a) = atom_path.canonicalize() { + let opt_header_path = header_path.and_then(|p| p.canonicalize().ok()); + atom_path = pathdiff::diff_paths(&a, &target_dir).unwrap_or_else(|| a.clone()); + header_path = opt_header_path.and_then(|h| pathdiff::diff_paths(h, &target_dir)); + } + atoms.push(CommandEntrypoint { + // TODO: improve, "--command pip" should be able to invoke atom "python" with args "-m pip" + command: command_name, + atom: atom_name.clone(), + path: atom_path, + header: header_path, + module_info: module_infos.get(&atom_name).cloned(), + }); + } + + let entrypoint = Entrypoint { + atoms, + volumes: vec![Volume { + name: volume_name.to_string(), + obj_file: volume_path, + }], + }; + + write_entrypoint(&target_dir, &entrypoint)?; + + Ok(atoms_from_file) +} + +/// Serialize a set of volumes so they can be read by the C API. +/// +/// This is really backwards, but the only way to create a v1 volume "fileblock" +/// is by first reading every file in a [`webc::compat::Volume`], serializing it +/// to webc v1's binary form, parsing those bytes into a [`webc::v1::Volume`], +/// then constructing a dummy [`webc::v1::WebC`] object just so we can make a +/// copy of its file block. +fn volume_file_block(volumes: &BTreeMap) -> Vec { + let serialized_volumes: Vec<(&String, Vec)> = volumes + .iter() + .map(|(name, volume)| (name, serialize_volume_to_webc_v1(volume))) + .collect(); + + let parsed_volumes: indexmap::IndexMap> = serialized_volumes + .iter() + .filter_map(|(name, serialized_volume)| { + let volume = webc::v1::Volume::parse(serialized_volume).ok()?; + Some((name.to_string(), volume)) + }) + .collect(); + + let webc = webc::v1::WebC { + version: 0, + checksum: None, + signature: None, + manifest: webc::metadata::Manifest::default(), + atoms: webc::v1::Volume::default(), + volumes: parsed_volumes, + }; + + webc.get_volumes_as_fileblock() +} + +fn serialize_volume_to_webc_v1(volume: &WebcVolume) -> Vec { + fn read_dir( + volume: &WebcVolume, + path: &mut PathSegments, + files: &mut BTreeMap>, + ) { + for (segment, meta) in volume.read_dir(&*path).unwrap_or_default() { + path.push(segment); + + match meta { + webc::compat::Metadata::Dir => { + files.insert( + webc::v1::DirOrFile::Dir(path.to_string().into()), + Vec::new(), + ); + read_dir(volume, path, files); + } + webc::compat::Metadata::File { .. } => { + if let Some(contents) = volume.read_file(&*path) { + files.insert( + webc::v1::DirOrFile::File(path.to_string().into()), + contents.to_vec(), + ); + } + } + } + + path.pop(); + } + } + + let mut path = PathSegments::ROOT; + let mut files = BTreeMap::new(); + + read_dir(volume, &mut path, &mut files); + + webc::v1::Volume::serialize_files(files) +} + +impl AllowMultiWasm { + fn validate( + &self, + all_atoms: &Vec<(String, webc::compat::SharedBytes)>, + ) -> Result<(), anyhow::Error> { + if matches!(self, AllowMultiWasm::Reject(None)) && all_atoms.len() > 1 { + let keys = all_atoms + .iter() + .map(|(name, _)| name.clone()) + .collect::>(); + + return Err(anyhow::anyhow!( + "where is one of: {}", + keys.join(", ") + )) + .context(anyhow::anyhow!( + "note: use --atom to specify which atom to compile" + )) + .context(anyhow::anyhow!( + "cannot compile more than one atom at a time" + )); + } + + Ok(()) + } +} + +/// Prefix map used during compilation of object files +#[derive(Debug, Default, PartialEq)] +pub(crate) struct PrefixMapCompilation { + /// Sha256 hashes for the input files + input_hashes: BTreeMap, + /// Manual prefixes for input files (file:prefix) + manual_prefixes: BTreeMap, + /// Cached compilation objects for files on disk + #[allow(dead_code)] + compilation_objects: BTreeMap>, +} + +impl PrefixMapCompilation { + /// Sets up the prefix map from a collection like "sha123123" or "wasmfile:sha123123" or "wasmfile:/tmp/filepath/:sha123123" + fn from_input( + atoms: &[(String, Vec)], + prefixes: &[String], + only_validate_prefixes: bool, + ) -> Result { + // No atoms: no prefixes necessary + if atoms.is_empty() { + return Ok(Self::default()); + } + + // default case: no prefixes have been specified: + // for all atoms, calculate the sha256 + if prefixes.is_empty() { + return Ok(Self { + input_hashes: atoms + .iter() + .map(|(name, bytes)| (normalize_atom_name(name), Self::hash_for_bytes(bytes))) + .collect(), + manual_prefixes: BTreeMap::new(), + compilation_objects: BTreeMap::new(), + }); + } + + // if prefixes are specified, have to match the atom names exactly + if prefixes.len() != atoms.len() { + println!( + "WARNING: invalid mapping of prefix and atoms: expected prefixes for {} atoms, got {} prefixes", + atoms.len(), prefixes.len() + ); + } + + let available_atoms = atoms.iter().map(|(k, _)| k.clone()).collect::>(); + let mut manual_prefixes = BTreeMap::new(); + let mut compilation_objects = BTreeMap::new(); + + for p in prefixes.iter() { + // On windows, we have to account for paths like "C://", which would + // prevent a simple .split(":") or a regex + let prefix_split = Self::split_prefix(p); + + match prefix_split.as_slice() { + // ATOM:PREFIX:PATH + [atom, prefix, path] => { + if only_validate_prefixes { + // only insert the prefix in order to not error out of the fs::read(path) + manual_prefixes.insert(normalize_atom_name(atom), prefix.to_string()); + } else { + let atom_hash = atoms + .iter() + .find_map(|(name, _)| if normalize_atom_name(name) == normalize_atom_name(atom) { Some(prefix.to_string()) } else { None }) + .ok_or_else(|| anyhow::anyhow!("no atom {atom:?} found, for prefix {p:?}, available atoms are {available_atoms:?}"))?; + + let current_dir = std::env::current_dir().unwrap().canonicalize().unwrap(); + let path = current_dir.join(path.replace("./", "")); + let bytes = std::fs::read(&path).map_err(|e| { + anyhow::anyhow!("could not read file for atom {atom:?} (prefix {p}, path {} in dir {}): {e}", path.display(), current_dir.display()) + })?; + + compilation_objects.insert(normalize_atom_name(atom), bytes); + manual_prefixes.insert(normalize_atom_name(atom), atom_hash.to_string()); + } + } + // atom + path, but default SHA256 prefix + [atom, path] => { + let atom_hash = atoms + .iter() + .find_map(|(name, bytes)| if normalize_atom_name(name) == normalize_atom_name(atom) { Some(Self::hash_for_bytes(bytes)) } else { None }) + .ok_or_else(|| anyhow::anyhow!("no atom {atom:?} found, for prefix {p:?}, available atoms are {available_atoms:?}"))?; + manual_prefixes.insert(normalize_atom_name(atom), atom_hash.to_string()); + + if !only_validate_prefixes { + let current_dir = std::env::current_dir().unwrap().canonicalize().unwrap(); + let path = current_dir.join(path.replace("./", "")); + let bytes = std::fs::read(&path).map_err(|e| { + anyhow::anyhow!("could not read file for atom {atom:?} (prefix {p}, path {} in dir {}): {e}", path.display(), current_dir.display()) + })?; + compilation_objects.insert(normalize_atom_name(atom), bytes); + } + } + // only prefix if atoms.len() == 1 + [prefix] if atoms.len() == 1 => { + manual_prefixes.insert(normalize_atom_name(&atoms[0].0), prefix.to_string()); + } + _ => { + return Err(anyhow::anyhow!("invalid --precompiled-atom {p:?} - correct format is ATOM:PREFIX:PATH or ATOM:PATH")); + } + } + } + + Ok(Self { + input_hashes: BTreeMap::new(), + manual_prefixes, + compilation_objects, + }) + } + + fn split_prefix(s: &str) -> Vec { + let regex = + regex::Regex::new(r"^([a-zA-Z0-9\-_]+)(:([a-zA-Z0-9\.\-_]+))?(:(.+*))?").unwrap(); + let mut captures = regex + .captures(s.trim()) + .map(|c| { + c.iter() + .skip(1) + .flatten() + .map(|m| m.as_str().to_owned()) + .collect::>() + }) + .unwrap_or_default(); + if captures.is_empty() { + vec![s.to_string()] + } else if captures.len() == 5 { + captures.remove(1); + captures.remove(2); + captures + } else if captures.len() == 3 { + s.splitn(2, ':').map(|s| s.to_string()).collect() + } else { + captures + } + } + + pub(crate) fn hash_for_bytes(bytes: &[u8]) -> String { + use sha2::{Digest, Sha256}; + let mut hasher = Sha256::new(); + hasher.update(bytes); + let result = hasher.finalize(); + + hex::encode(&result[..]) + } + + fn get_prefix_for_atom(&self, atom_name: &str) -> Option { + self.manual_prefixes + .get(atom_name) + .or_else(|| self.input_hashes.get(atom_name)) + .cloned() + } + + #[allow(dead_code)] + fn get_compilation_object_for_atom(&self, atom_name: &str) -> Option<&[u8]> { + self.compilation_objects + .get(atom_name) + .map(|s| s.as_slice()) + } +} + +#[test] +fn test_prefix_parsing() { + let tempdir = tempfile::TempDir::new().unwrap(); + let path = tempdir.path(); + std::fs::write(path.join("test.obj"), b"").unwrap(); + let str1 = format!("ATOM_NAME:PREFIX:{}", path.join("test.obj").display()); + let prefix = + PrefixMapCompilation::from_input(&[("ATOM_NAME".to_string(), b"".to_vec())], &[str1], true); + assert_eq!( + prefix.unwrap(), + PrefixMapCompilation { + input_hashes: BTreeMap::new(), + manual_prefixes: vec![("ATOM_NAME".to_string(), "PREFIX".to_string())] + .into_iter() + .collect(), + compilation_objects: Vec::new().into_iter().collect(), + } + ); +} + +#[test] +fn test_split_prefix() { + let split = PrefixMapCompilation::split_prefix( + "qjs:abc123:C:\\Users\\felix\\AppData\\Local\\Temp\\.tmpoccCjV\\wasm.obj", + ); + assert_eq!( + split, + vec![ + "qjs".to_string(), + "abc123".to_string(), + "C:\\Users\\felix\\AppData\\Local\\Temp\\.tmpoccCjV\\wasm.obj".to_string(), + ] + ); + let split = PrefixMapCompilation::split_prefix("qjs:./tmp.obj"); + assert_eq!(split, vec!["qjs".to_string(), "./tmp.obj".to_string(),]); + let split2 = PrefixMapCompilation::split_prefix( + "qjs:abc123:/var/folders/65/2zzy98b16xz254jccxjzqb8w0000gn/T/.tmpNdgVaq/wasm.o", + ); + assert_eq!( + split2, + vec![ + "qjs".to_string(), + "abc123".to_string(), + "/var/folders/65/2zzy98b16xz254jccxjzqb8w0000gn/T/.tmpNdgVaq/wasm.o".to_string(), + ] + ); + let split3 = PrefixMapCompilation::split_prefix( + "qjs:/var/folders/65/2zzy98b16xz254jccxjzqb8w0000gn/T/.tmpNdgVaq/wasm.o", + ); + assert_eq!( + split3, + vec![ + "qjs".to_string(), + "/var/folders/65/2zzy98b16xz254jccxjzqb8w0000gn/T/.tmpNdgVaq/wasm.o".to_string(), + ] + ); +} + +fn compile_atoms( + atoms: &[(String, Vec)], + output_dir: &Path, + compiler: &CompilerOptions, + target: &Target, + prefixes: &PrefixMapCompilation, + debug: bool, +) -> Result, anyhow::Error> { + use std::{ + fs::File, + io::{BufWriter, Write}, + }; + + let mut module_infos = BTreeMap::new(); + for (a, data) in atoms { + let prefix = prefixes + .get_prefix_for_atom(&normalize_atom_name(a)) + .ok_or_else(|| anyhow::anyhow!("no prefix given for atom {a}"))?; + let atom_name = utils::normalize_atom_name(a); + let output_object_path = output_dir.join(format!("{atom_name}.o")); + if let Some(atom) = prefixes.get_compilation_object_for_atom(a) { + std::fs::write(&output_object_path, atom) + .map_err(|e| anyhow::anyhow!("{}: {e}", output_object_path.display()))?; + if debug { + println!("Using cached object file for atom {a:?}."); + } + continue; + } + let (engine, _) = compiler.get_engine_for_target(target.clone())?; + let engine_inner = engine.inner(); + let compiler = engine_inner.compiler()?; + let features = engine_inner.features(); + let tunables = engine.tunables(); + let (module_info, obj, _, _) = Artifact::generate_object( + compiler, + data, + Some(prefix.as_str()), + target, + tunables, + features, + )?; + module_infos.insert(atom_name, module_info); + // Write object file with functions + let mut writer = BufWriter::new(File::create(&output_object_path)?); + obj.write_stream(&mut writer) + .map_err(|err| anyhow::anyhow!(err.to_string()))?; + writer.flush()?; + } + + Ok(module_infos) +} + +/// Compile the C code. +fn run_c_compile( + path_to_c_src: &Path, + output_name: &Path, + target: &Triple, + debug: bool, + include_dirs: &[PathBuf], +) -> anyhow::Result<()> { + #[cfg(not(windows))] + let c_compiler = "cc"; + // We must use a C++ compiler on Windows because wasm.h uses `static_assert` + // which isn't available in `clang` on Windows. + #[cfg(windows)] + let c_compiler = "clang++"; + + let mut command = Command::new(c_compiler); + let mut command = command + .arg("-Wall") + .arg("-O2") + .arg("-c") + .arg(path_to_c_src) + .arg("-I") + .arg(utils::get_wasmer_include_directory()?); + + for i in include_dirs { + command = command.arg("-I"); + command = command.arg(normalize_path(&i.display().to_string())); + } + + // On some compiler -target isn't implemented + if *target != Triple::host() { + command = command.arg("-target").arg(format!("{}", target)); + } + + let command = command.arg("-o").arg(output_name); + + if debug { + println!("{command:#?}"); + } + + let output = command.output()?; + if debug { + eprintln!( + "run_c_compile: stdout: {}\n\nstderr: {}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + } + + if !output.status.success() { + bail!( + "C code compile failed with: stdout: {}\n\nstderr: {}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + } + + Ok(()) +} + +fn write_volume_obj( + volume_bytes: &[u8], + object_name: &str, + output_path: &Path, + target: &Target, +) -> anyhow::Result<()> { + use std::{ + fs::File, + io::{BufWriter, Write}, + }; + + let mut volumes_object = get_object_for_target(target.triple())?; + emit_serialized( + &mut volumes_object, + volume_bytes, + target.triple(), + object_name, + )?; + + let mut writer = BufWriter::new(File::create(output_path)?); + volumes_object + .write_stream(&mut writer) + .map_err(|err| anyhow::anyhow!(err.to_string()))?; + writer.flush()?; + drop(writer); + + Ok(()) +} + +/// Given a .wasm file, compiles the .wasm file into the target directory and creates the entrypoint.json +#[allow(clippy::too_many_arguments)] +pub(super) fn prepare_directory_from_single_wasm_file( + wasm_file: &Path, + target_dir: &Path, + compiler: &CompilerOptions, + triple: &Triple, + cpu_features: &[CpuFeature], + prefix: &[String], + debug: bool, +) -> anyhow::Result)>, anyhow::Error> { + let bytes = std::fs::read(wasm_file)?; + let target = &utils::target_triple_to_target(triple, cpu_features); + + std::fs::create_dir_all(target_dir) + .map_err(|e| anyhow::anyhow!("cannot create / dir in {}: {e}", target_dir.display()))?; + + let target_dir = target_dir.canonicalize()?; + + std::fs::create_dir_all(target_dir.join("atoms")).map_err(|e| { + anyhow::anyhow!("cannot create /atoms dir in {}: {e}", target_dir.display()) + })?; + + let mut atoms_from_file = Vec::new(); + let mut target_paths = Vec::new(); + + let all_files = vec![( + wasm_file + .file_stem() + .and_then(|f| f.to_str()) + .unwrap_or("main") + .to_string(), + bytes, + )]; + + for (atom_name, atom_bytes) in all_files.iter() { + atoms_from_file.push((atom_name.clone(), atom_bytes.to_vec())); + let atom_path = target_dir.join("atoms").join(format!("{atom_name}.o")); + target_paths.push((atom_name, atom_path)); + } + + let prefix_map = PrefixMapCompilation::from_input(&atoms_from_file, prefix, false) + .with_context(|| anyhow::anyhow!("prepare_directory_from_single_wasm_file"))?; + + let module_infos = compile_atoms( + &atoms_from_file, + &target_dir.join("atoms"), + compiler, + target, + &prefix_map, + debug, + )?; + + let mut atoms = Vec::new(); + for (atom_name, atom_path) in target_paths { + atoms.push(CommandEntrypoint { + // TODO: improve, "--command pip" should be able to invoke atom "python" with args "-m pip" + command: atom_name.clone(), + atom: atom_name.clone(), + path: atom_path, + header: None, + module_info: module_infos.get(atom_name).cloned(), + }); + } + + let entrypoint = Entrypoint { + atoms, + volumes: Vec::new(), + }; + + write_entrypoint(&target_dir, &entrypoint)?; + + Ok(all_files) +} + +// Given the input file paths, correctly resolves the .wasm files, +// reads the module info from the wasm module and writes the ModuleInfo for each file +// into the entrypoint.json file +fn get_module_infos( + store: &Store, + directory: &Path, + atoms: &[(String, Vec)], +) -> Result, anyhow::Error> { + let mut entrypoint = + get_entrypoint(directory).with_context(|| anyhow::anyhow!("get module infos"))?; + + let mut module_infos = BTreeMap::new(); + for (atom_name, atom_bytes) in atoms { + let module = Module::new(&store, atom_bytes.as_slice())?; + let module_info = module.info(); + if let Some(s) = entrypoint + .atoms + .iter_mut() + .find(|a| a.atom.as_str() == atom_name.as_str()) + { + s.module_info = Some(module_info.clone()); + module_infos.insert(atom_name.clone(), module_info.clone()); + } + } + + write_entrypoint(directory, &entrypoint)?; + + Ok(module_infos) +} + +/// Create the static_defs.h header files in the /include directory +pub(crate) fn create_header_files_in_dir( + directory: &Path, + entrypoint: &mut Entrypoint, + atoms: &[(String, Vec)], + prefixes: &[String], +) -> anyhow::Result<()> { + use object::{Object, ObjectSection}; + + std::fs::create_dir_all(directory.join("include")).map_err(|e| { + anyhow::anyhow!("cannot create /include dir in {}: {e}", directory.display()) + })?; + + let prefixes = PrefixMapCompilation::from_input(atoms, prefixes, false) + .with_context(|| anyhow::anyhow!("create_header_files_in_dir"))?; + + for atom in entrypoint.atoms.iter_mut() { + let atom_name = &atom.atom; + let prefix = prefixes + .get_prefix_for_atom(atom_name) + .ok_or_else(|| anyhow::anyhow!("cannot get prefix for atom {atom_name}"))?; + + let object_file_src = directory.join(&atom.path); + let object_file = std::fs::read(&object_file_src) + .map_err(|e| anyhow::anyhow!("could not read {}: {e}", object_file_src.display()))?; + let obj_file = object::File::parse(&*object_file)?; + let sections = obj_file + .sections() + .filter_map(|s| s.name().ok().map(|s| s.to_string())) + .collect::>(); + let section = obj_file + .section_by_name(".data") + .unwrap() + .data() + .map_err(|_| { + anyhow::anyhow!( + "missing section .data in object file {} (sections = {:#?}", + object_file_src.display(), + sections + ) + })?; + let metadata_length = section.len(); + + let module_info = atom + .module_info + .as_ref() + .ok_or_else(|| anyhow::anyhow!("no module info for atom {atom_name:?}"))?; + + let base_path = Path::new("include").join(format!("static_defs_{prefix}.h")); + let header_file_path = directory.join(&base_path); + + let header_file_src = crate::c_gen::staticlib_header::generate_header_file( + &prefix, + module_info, + &ModuleMetadataSymbolRegistry { + prefix: prefix.clone(), + }, + metadata_length, + ); + + std::fs::write(&header_file_path, &header_file_src).map_err(|e| { + anyhow::anyhow!( + "could not write static_defs.h for atom {atom_name} in generate-header step: {e}" + ) + })?; + + atom.header = Some(base_path); + } + + write_entrypoint(directory, entrypoint)?; + + Ok(()) +} + +/// Given a directory, links all the objects from the directory appropriately +fn link_exe_from_dir( + directory: &Path, + output_path: PathBuf, + cross_compilation: &CrossCompileSetup, + additional_libraries: &[String], + debug: bool, + atoms: &[(String, Vec)], + prefixes: &[String], +) -> anyhow::Result<()> { + let entrypoint = + get_entrypoint(directory).with_context(|| anyhow::anyhow!("link exe from dir"))?; + + let prefixes = PrefixMapCompilation::from_input(atoms, prefixes, false) + .with_context(|| anyhow::anyhow!("link_exe_from_dir"))?; + + let wasmer_main_c = generate_wasmer_main_c(&entrypoint, &prefixes).map_err(|e| { + anyhow::anyhow!( + "could not generate wasmer_main.c in dir {}: {e}", + directory.display() + ) + })?; + + std::fs::write(directory.join("wasmer_main.c"), wasmer_main_c.as_bytes()).map_err(|e| { + anyhow::anyhow!( + "could not write wasmer_main.c in dir {}: {e}", + directory.display() + ) + })?; + + let library_path = &cross_compilation.library; + + let mut object_paths = entrypoint + .atoms + .iter() + .filter_map(|a| directory.join(&a.path).canonicalize().ok()) + .collect::>(); + + object_paths.extend( + entrypoint + .volumes + .iter() + .filter_map(|v| directory.join(&v.obj_file).canonicalize().ok()), + ); + + let zig_triple = utils::triple_to_zig_triple(&cross_compilation.target); + let include_dirs = entrypoint + .atoms + .iter() + .filter_map(|a| { + Some( + directory + .join(a.header.as_deref()?) + .canonicalize() + .ok()? + .parent()? + .to_path_buf(), + ) + }) + .collect::>(); + + let mut include_dirs = include_dirs; + include_dirs.sort(); + include_dirs.dedup(); + + // On Windows, cross-compilation to Windows itself with zig does not work due + // to libunwind and libstdc++ not compiling, so we fake this special case of cross-compilation + // by falling back to the system compilation + system linker + if cross_compilation.target == Triple::host() + && cross_compilation.target.operating_system == OperatingSystem::Windows + { + run_c_compile( + &directory.join("wasmer_main.c"), + &directory.join("wasmer_main.o"), + &cross_compilation.target, + debug, + &include_dirs, + ) + .map_err(|e| { + anyhow::anyhow!( + "could not run c compile of wasmer_main.c in dir {}: {e}", + directory.display() + ) + })?; + } + + // compilation done, now link + if cross_compilation.zig_binary_path.is_none() + || (cross_compilation.target == Triple::host() + && cross_compilation.target.operating_system == OperatingSystem::Windows) + { + #[cfg(not(windows))] + let linker = "cc"; + #[cfg(windows)] + let linker = "clang"; + let optimization_flag = "-O2"; + + let object_path = match directory.join("wasmer_main.o").canonicalize() { + Ok(s) => s, + Err(_) => directory.join("wasmer_main.c"), + }; + + object_paths.push(object_path); + + return link_objects_system_linker( + library_path, + linker, + optimization_flag, + &include_dirs, + &object_paths, + &cross_compilation.target, + additional_libraries, + &output_path, + debug, + ); + } + + let zig_binary_path = cross_compilation + .zig_binary_path + .as_ref() + .ok_or_else(|| anyhow::anyhow!("could not find zig in $PATH {}", directory.display()))?; + + let mut cmd = Command::new(zig_binary_path); + cmd.arg("build-exe"); + cmd.arg("--verbose-cc"); + cmd.arg("--verbose-link"); + cmd.arg("-target"); + cmd.arg(&zig_triple); + + // On Windows, libstdc++ does not compile with zig due to zig-internal problems, + // so we link with only -lc + #[cfg(not(target_os = "windows"))] + if zig_triple.contains("windows") { + cmd.arg("-lc++"); + } else { + cmd.arg("-lc"); + } + + #[cfg(target_os = "windows")] + cmd.arg("-lc"); + + for include_dir in include_dirs { + cmd.arg("-I"); + cmd.arg(normalize_path(&format!("{}", include_dir.display()))); + } + + let mut include_path = library_path.clone(); + include_path.pop(); + include_path.pop(); + include_path.push("include"); + if !include_path.exists() { + // Can happen when we got the wrong library_path + return Err(anyhow::anyhow!("Wasmer include path {} does not exist, maybe library path {} is wrong (expected /lib/libwasmer.a)?", include_path.display(), library_path.display())); + } + cmd.arg("-I"); + cmd.arg(normalize_path(&format!("{}", include_path.display()))); + + // On Windows, libunwind does not compile, so we have + // to create binaries without libunwind. + #[cfg(not(target_os = "windows"))] + cmd.arg("-lunwind"); + + #[cfg(target_os = "windows")] + if !zig_triple.contains("windows") { + cmd.arg("-lunwind"); + } + + cmd.arg("-OReleaseSafe"); + cmd.arg("-fno-compiler-rt"); + cmd.arg("-fno-lto"); + #[cfg(target_os = "windows")] + let out_path = directory.join("wasmer_main.exe"); + #[cfg(not(target_os = "windows"))] + let out_path = directory.join("wasmer_main"); + cmd.arg(&format!("-femit-bin={}", out_path.display())); + cmd.args( + &object_paths + .iter() + .map(|o| normalize_path(&format!("{}", o.display()))) + .collect::>(), + ); + cmd.arg(&normalize_path(&format!("{}", library_path.display()))); + cmd.arg(normalize_path(&format!( + "{}", + directory + .join("wasmer_main.c") + .canonicalize() + .expect("could not find wasmer_main.c / wasmer_main.o") + .display() + ))); + + if zig_triple.contains("windows") { + let mut winsdk_path = library_path.clone(); + winsdk_path.pop(); + winsdk_path.pop(); + winsdk_path.push("winsdk"); + + let files_winsdk = std::fs::read_dir(winsdk_path) + .ok() + .map(|res| { + res.filter_map(|r| Some(normalize_path(&format!("{}", r.ok()?.path().display())))) + .collect::>() + }) + .unwrap_or_default(); + + cmd.args(files_winsdk); + } + + if zig_triple.contains("macos") { + // need to link with Security framework when using zig, but zig + // doesn't include it, so we need to bring a copy of the dtb file + // which is basicaly the collection of exported symbol for the libs + let framework = include_bytes!("security_framework.tgz").to_vec(); + // extract files + let tar = flate2::read::GzDecoder::new(framework.as_slice()); + let mut archive = Archive::new(tar); + // directory is a temp folder + archive.unpack(directory)?; + // add the framework to the link command + cmd.arg("-framework"); + cmd.arg("Security"); + cmd.arg(format!("-F{}", directory.display())); + } + + if debug { + println!("running cmd: {cmd:?}"); + cmd.stdout(Stdio::inherit()); + cmd.stderr(Stdio::inherit()); + } + + let compilation = cmd + .output() + .context(anyhow!("Could not execute `zig`: {cmd:?}"))?; + + if !compilation.status.success() { + return Err(anyhow::anyhow!(String::from_utf8_lossy( + &compilation.stderr + ) + .to_string())); + } + + // remove file if it exists - if not done, can lead to errors on copy + let output_path_normalized = normalize_path(&format!("{}", output_path.display())); + let _ = std::fs::remove_file(output_path_normalized); + std::fs::copy( + normalize_path(&format!("{}", out_path.display())), + normalize_path(&format!("{}", output_path.display())), + ) + .map_err(|e| { + anyhow::anyhow!( + "could not copy from {} to {}: {e}", + normalize_path(&format!("{}", out_path.display())), + normalize_path(&format!("{}", output_path.display())) + ) + })?; + + Ok(()) +} + +/// Link compiled objects using the system linker +#[allow(clippy::too_many_arguments)] +fn link_objects_system_linker( + libwasmer_path: &Path, + linker_cmd: &str, + optimization_flag: &str, + include_dirs: &[PathBuf], + object_paths: &[PathBuf], + target: &Triple, + additional_libraries: &[String], + output_path: &Path, + debug: bool, +) -> Result<(), anyhow::Error> { + let libwasmer_path = libwasmer_path + .canonicalize() + .context("Failed to find libwasmer")?; + let mut command = Command::new(linker_cmd); + let mut command = command + .arg("-Wall") + .arg(optimization_flag) + .args(object_paths.iter().map(|path| path.canonicalize().unwrap())) + .arg(&libwasmer_path); + + if *target != Triple::host() { + command = command.arg("-target"); + command = command.arg(format!("{}", target)); + } + + for include_dir in include_dirs { + command = command.arg("-I"); + command = command.arg(normalize_path(&format!("{}", include_dir.display()))); + } + let mut include_path = libwasmer_path.clone(); + include_path.pop(); + include_path.pop(); + include_path.push("include"); + if !include_path.exists() { + // Can happen when we got the wrong library_path + return Err(anyhow::anyhow!("Wasmer include path {} does not exist, maybe library path {} is wrong (expected /lib/libwasmer.a)?", include_path.display(), libwasmer_path.display())); + } + command = command.arg("-I"); + command = command.arg(normalize_path(&format!("{}", include_path.display()))); + + // Add libraries required per platform. + // We need userenv, sockets (Ws2_32), advapi32 for some system calls and bcrypt for random numbers. + let mut additional_libraries = additional_libraries.to_vec(); + if target.operating_system == OperatingSystem::Windows { + additional_libraries.extend(LINK_SYSTEM_LIBRARIES_WINDOWS.iter().map(|s| s.to_string())); + } else { + additional_libraries.extend(LINK_SYSTEM_LIBRARIES_UNIX.iter().map(|s| s.to_string())); + } + let link_against_extra_libs = additional_libraries.iter().map(|lib| format!("-l{}", lib)); + let command = command.args(link_against_extra_libs); + let command = command.arg("-o").arg(output_path); + if debug { + println!("{:#?}", command); + } + let output = command.output()?; + + if !output.status.success() { + bail!( + "linking failed with command line:{:#?} stdout: {}\n\nstderr: {}", + command, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr), + ); + } + Ok(()) +} + +/// Generate the wasmer_main.c that links all object files together +/// (depending on the object format / atoms number) +fn generate_wasmer_main_c( + entrypoint: &Entrypoint, + prefixes: &PrefixMapCompilation, +) -> Result { + use std::fmt::Write; + + const WASMER_MAIN_C_SOURCE: &str = include_str!("wasmer_create_exe_main.c"); + + // always with compile zig + static_defs.h + let atom_names = entrypoint + .atoms + .iter() + .map(|a| &a.command) + .collect::>(); + + let c_code_to_add = String::new(); + let mut c_code_to_instantiate = String::new(); + let mut deallocate_module = String::new(); + let mut extra_headers = Vec::new(); + + for a in atom_names.iter() { + let prefix = prefixes + .get_prefix_for_atom(&utils::normalize_atom_name(a)) + .ok_or_else(|| { + anyhow::anyhow!( + "cannot find prefix for atom {a} when generating wasmer_main.c ({:#?})", + prefixes + ) + })?; + let atom_name = prefix.clone(); + + extra_headers.push(format!("#include \"static_defs_{atom_name}.h\"")); + + write!(c_code_to_instantiate, " + wasm_module_t *atom_{atom_name} = wasmer_object_module_new_{atom_name}(store, \"{atom_name}\"); + if (!atom_{atom_name}) {{ + fprintf(stderr, \"Failed to create module from atom \\\"{a}\\\"\\n\"); + print_wasmer_error(); + return -1; + }} + ")?; + + write!(deallocate_module, "wasm_module_delete(atom_{atom_name});")?; + } + + let volumes_str = entrypoint + .volumes + .iter() + .map(|v| utils::normalize_atom_name(&v.name).to_uppercase()) + .map(|uppercase| { + format!( + "extern size_t {uppercase}_LENGTH asm(\"{uppercase}_LENGTH\");\r\nextern char {uppercase}_DATA asm(\"{uppercase}_DATA\");" + ) + }) + .collect::>(); + + let base_str = WASMER_MAIN_C_SOURCE; + let volumes_str = volumes_str.join("\r\n"); + let return_str = base_str + .replace( + "#define WASI", + if !volumes_str.trim().is_empty() { + "#define WASI\r\n#define WASI_PIRITA" + } else { + "#define WASI" + }, + ) + .replace("// DECLARE_MODULES", &c_code_to_add) + .replace("// DECLARE_VOLUMES", &volumes_str) + .replace( + "// SET_NUMBER_OF_COMMANDS", + &format!("number_of_commands = {};", atom_names.len()), + ) + .replace("// EXTRA_HEADERS", &extra_headers.join("\r\n")) + .replace("wasm_module_delete(module);", &deallocate_module); + + if atom_names.len() == 1 { + let prefix = prefixes + .get_prefix_for_atom(&utils::normalize_atom_name(atom_names[0])) + .ok_or_else(|| { + anyhow::anyhow!( + "cannot find prefix for atom {} when generating wasmer_main.c ({:#?})", + &atom_names[0], + prefixes + ) + })?; + write!(c_code_to_instantiate, "module = atom_{prefix};")?; + } else { + for a in atom_names.iter() { + let prefix = prefixes + .get_prefix_for_atom(&utils::normalize_atom_name(a)) + .ok_or_else(|| { + anyhow::anyhow!( + "cannot find prefix for atom {a} when generating wasmer_main.c ({:#?})", + prefixes + ) + })?; + writeln!( + c_code_to_instantiate, + "if (strcmp(selected_atom, \"{a}\") == 0) {{ module = atom_{}; }}", + prefix + )?; + } + } + + write!( + c_code_to_instantiate, + " + if (!module) {{ + fprintf(stderr, \"No --command given, available commands are:\\n\"); + fprintf(stderr, \"\\n\"); + {commands} + fprintf(stderr, \"\\n\"); + return -1; + }} + ", + commands = atom_names + .iter() + .map(|a| format!("fprintf(stderr, \" {a}\\n\");")) + .collect::>() + .join("\n") + )?; + + Ok(return_str.replace("// INSTANTIATE_MODULES", &c_code_to_instantiate)) +} + +#[allow(dead_code)] +pub(super) mod utils { + + use std::{ + ffi::OsStr, + path::{Path, PathBuf}, + }; + + use anyhow::{anyhow, Context}; + use target_lexicon::{Architecture, Environment, OperatingSystem, Triple}; + use wasmer_types::{CpuFeature, Target}; + + use super::{CrossCompile, CrossCompileSetup, UrlOrVersion}; + + pub(in crate::commands) fn target_triple_to_target( + target_triple: &Triple, + cpu_features: &[CpuFeature], + ) -> Target { + let mut features = cpu_features.iter().fold(CpuFeature::set(), |a, b| a | *b); + // Cranelift requires SSE2, so we have this "hack" for now to facilitate + // usage + if target_triple.architecture == Architecture::X86_64 { + features |= CpuFeature::SSE2; + } + Target::new(target_triple.clone(), features) + } + + pub(in crate::commands) fn get_cross_compile_setup( + cross_subc: &mut CrossCompile, + target_triple: &Triple, + starting_cd: &Path, + specific_release: Option, + ) -> Result { + let target = target_triple; + + if let Some(tarball_path) = cross_subc.tarball.as_mut() { + if tarball_path.is_relative() { + *tarball_path = starting_cd.join(&tarball_path); + if !tarball_path.exists() { + return Err(anyhow!( + "Tarball path `{}` does not exist.", + tarball_path.display() + )); + } else if tarball_path.is_dir() { + return Err(anyhow!( + "Tarball path `{}` is a directory.", + tarball_path.display() + )); + } + } + } + + let zig_binary_path = if !cross_subc.use_system_linker { + find_zig_binary(cross_subc.zig_binary_path.as_ref().and_then(|p| { + if p.is_absolute() { + p.canonicalize().ok() + } else { + starting_cd.join(p).canonicalize().ok() + } + })) + .ok() + } else { + None + }; + + let library = if let Some(v) = cross_subc.library_path.clone() { + Some(v.canonicalize().unwrap_or(v)) + } else if let Some(local_tarball) = cross_subc.tarball.as_ref() { + let (filename, tarball_dir) = find_filename(local_tarball, target)?; + Some(tarball_dir.join(filename)) + } else { + let wasmer_cache_dir = + if *target_triple == Triple::host() && std::env::var("WASMER_DIR").is_ok() { + wasmer_registry::WasmerConfig::get_wasmer_dir() + .map_err(|e| anyhow!("{e}")) + .map(|o| o.join("cache")) + } else { + get_libwasmer_cache_path() + } + .ok(); + + // check if the tarball for the target already exists locally + let local_tarball = wasmer_cache_dir.as_ref().and_then(|wc| { + let wasmer_cache = std::fs::read_dir(wc).ok()?; + wasmer_cache + .filter_map(|e| e.ok()) + .filter_map(|e| { + let path = format!("{}", e.path().display()); + if path.ends_with(".tar.gz") { + Some(e.path()) + } else { + None + } + }) + .find(|p| crate::commands::utils::filter_tarball(p, target)) + }); + + if let Some(UrlOrVersion::Url(wasmer_release)) = specific_release.as_ref() { + let tarball = super::http_fetch::download_url(wasmer_release.as_ref())?; + let (filename, tarball_dir) = find_filename(&tarball, target)?; + Some(tarball_dir.join(filename)) + } else if let Some(UrlOrVersion::Version(wasmer_release)) = specific_release.as_ref() { + let release = super::http_fetch::get_release(Some(wasmer_release.clone()))?; + let tarball = super::http_fetch::download_release(release, target.clone())?; + let (filename, tarball_dir) = find_filename(&tarball, target)?; + Some(tarball_dir.join(filename)) + } else if let Some(local_tarball) = local_tarball.as_ref() { + let (filename, tarball_dir) = find_filename(local_tarball, target)?; + Some(tarball_dir.join(filename)) + } else { + let release = super::http_fetch::get_release(None)?; + let tarball = super::http_fetch::download_release(release, target.clone())?; + let (filename, tarball_dir) = find_filename(&tarball, target)?; + Some(tarball_dir.join(filename)) + } + }; + + let library = library.ok_or_else(|| anyhow!("libwasmer.a / wasmer.lib not found"))?; + + let ccs = CrossCompileSetup { + target: target.clone(), + zig_binary_path, + library, + }; + Ok(ccs) + } + + pub(super) fn filter_tarball(p: &Path, target: &Triple) -> bool { + filter_tarball_internal(p, target).unwrap_or(false) + } + + fn filter_tarball_internal(p: &Path, target: &Triple) -> Option { + if !p.file_name()?.to_str()?.ends_with(".tar.gz") { + return None; + } + + if target.environment == Environment::Musl && !p.file_name()?.to_str()?.contains("musl") + || p.file_name()?.to_str()?.contains("musl") && target.environment != Environment::Musl + { + return None; + } + + if let Architecture::Aarch64(_) = target.architecture { + if !(p.file_name()?.to_str()?.contains("aarch64") + || p.file_name()?.to_str()?.contains("arm64")) + { + return None; + } + } + + if let Architecture::X86_64 = target.architecture { + if target.operating_system == OperatingSystem::Windows { + if !p.file_name()?.to_str()?.contains("gnu64") { + return None; + } + } else if !(p.file_name()?.to_str()?.contains("x86_64") + || p.file_name()?.to_str()?.contains("amd64")) + { + return None; + } + } + + if let OperatingSystem::Windows = target.operating_system { + if !p.file_name()?.to_str()?.contains("windows") { + return None; + } + } + + if let OperatingSystem::Darwin = target.operating_system { + if !(p.file_name()?.to_str()?.contains("apple") + || p.file_name()?.to_str()?.contains("darwin")) + { + return None; + } + } + + if let OperatingSystem::Linux = target.operating_system { + if !p.file_name()?.to_str()?.contains("linux") { + return None; + } + } + + Some(true) + } + + pub(super) fn find_filename( + local_tarball: &Path, + target: &Triple, + ) -> Result<(PathBuf, PathBuf), anyhow::Error> { + let target_file_path = local_tarball + .parent() + .and_then(|parent| Some(parent.join(local_tarball.file_stem()?))) + .unwrap_or_else(|| local_tarball.to_path_buf()); + + let target_file_path = target_file_path + .parent() + .and_then(|parent| Some(parent.join(target_file_path.file_stem()?))) + .unwrap_or_else(|| target_file_path.clone()); + + std::fs::create_dir_all(&target_file_path) + .map_err(|e| anyhow!("{e}")) + .with_context(|| anyhow!("{}", target_file_path.display()))?; + let files = + super::http_fetch::untar(local_tarball, &target_file_path).with_context(|| { + anyhow!( + "{} -> {}", + local_tarball.display(), + target_file_path.display() + ) + })?; + let tarball_dir = target_file_path.canonicalize().unwrap_or(target_file_path); + let file = find_libwasmer_in_files(target, &files)?; + Ok((file, tarball_dir)) + } + + fn find_libwasmer_in_files( + target: &Triple, + files: &[PathBuf], + ) -> Result { + let target_files = &[ + OsStr::new("libwasmer-headless.a"), + OsStr::new("wasmer-headless.lib"), + OsStr::new("libwasmer.a"), + OsStr::new("wasmer.lib"), + ]; + target_files + .iter() + .find_map(|q| { + files.iter().find(|f| f.file_name() == Some(*q)) + }) + .cloned() + .ok_or_else(|| { + anyhow!("Could not find libwasmer.a for {} target in the provided tarball path (files = {files:#?})", target) + }) + } + + pub(super) fn normalize_atom_name(s: &str) -> String { + s.chars() + .filter_map(|c| { + if char::is_alphabetic(c) { + Some(c) + } else if c == '-' || c == '_' { + Some('_') + } else { + None + } + }) + .collect() + } + + pub(super) fn triple_to_zig_triple(target_triple: &Triple) -> String { + let arch = match target_triple.architecture { + wasmer_types::Architecture::X86_64 => "x86_64".into(), + wasmer_types::Architecture::Aarch64(wasmer_types::Aarch64Architecture::Aarch64) => { + "aarch64".into() + } + v => v.to_string(), + }; + let os = match target_triple.operating_system { + wasmer_types::OperatingSystem::Linux => "linux".into(), + wasmer_types::OperatingSystem::Darwin => "macos".into(), + wasmer_types::OperatingSystem::Windows => "windows".into(), + v => v.to_string(), + }; + let env = match target_triple.environment { + wasmer_types::Environment::Musl => "musl", + wasmer_types::Environment::Gnu => "gnu", + wasmer_types::Environment::Msvc => "msvc", + _ => "none", + }; + format!("{}-{}-{}", arch, os, env) + } + + pub(super) fn get_wasmer_dir() -> anyhow::Result { + wasmer_registry::WasmerConfig::get_wasmer_dir().map_err(|e| anyhow!("{e}")) + } + + pub(super) fn get_wasmer_include_directory() -> anyhow::Result { + let mut path = get_wasmer_dir()?; + if path.clone().join("wasmer.h").exists() { + return Ok(path); + } + path.push("include"); + if !path.clone().join("wasmer.h").exists() { + if !path.exists() { + return Err(anyhow!("WASMER_DIR path {} does not exist", path.display())); + } + println!( + "wasmer.h does not exist in {}, will probably default to the system path", + path.canonicalize().unwrap().display() + ); + } + Ok(path) + } + + /// path to the static libwasmer + pub(super) fn get_libwasmer_path() -> anyhow::Result { + let path = get_wasmer_dir()?; + + // TODO: prefer headless Wasmer if/when it's a separate library. + #[cfg(not(windows))] + let libwasmer_static_name = "libwasmer.a"; + #[cfg(windows)] + let libwasmer_static_name = "libwasmer.lib"; + + if path.exists() && path.join(libwasmer_static_name).exists() { + Ok(path.join(libwasmer_static_name)) + } else { + Ok(path.join("lib").join(libwasmer_static_name)) + } + } + + /// path to library tarball cache dir + pub(super) fn get_libwasmer_cache_path() -> anyhow::Result { + let mut path = get_wasmer_dir()?; + path.push("cache"); + std::fs::create_dir_all(&path)?; + Ok(path) + } + + pub(super) fn get_zig_exe_str() -> &'static str { + #[cfg(target_os = "windows")] + { + "zig.exe" + } + #[cfg(not(target_os = "windows"))] + { + "zig" + } + } + + pub(super) fn find_zig_binary(path: Option) -> Result { + use std::env::split_paths; + #[cfg(unix)] + use std::os::unix::ffi::OsStrExt; + let path_var = std::env::var("PATH").unwrap_or_default(); + #[cfg(unix)] + let system_path_var = std::process::Command::new("getconf") + .args(["PATH"]) + .output() + .map(|output| output.stdout) + .unwrap_or_default(); + let retval = if let Some(p) = path { + if p.exists() { + p + } else { + return Err(anyhow!("Could not find `zig` binary in {}.", p.display())); + } + } else { + let mut retval = None; + for mut p in split_paths(&path_var).chain(split_paths( + #[cfg(unix)] + { + &OsStr::from_bytes(&system_path_var[..]) + }, + #[cfg(not(unix))] + { + OsStr::new("") + }, + )) { + p.push(get_zig_exe_str()); + if p.exists() { + retval = Some(p); + break; + } + } + retval.ok_or_else(|| anyhow!("Could not find `zig` binary in PATH."))? + }; + + let version = std::process::Command::new(&retval) + .arg("version") + .output() + .with_context(|| { + format!( + "Could not execute `zig` binary at path `{}`", + retval.display() + ) + })? + .stdout; + let version_slice = if let Some(pos) = version + .iter() + .position(|c| !(c.is_ascii_digit() || (*c == b'.'))) + { + &version[..pos] + } else { + &version[..] + }; + + let version_slice = String::from_utf8_lossy(version_slice); + let version_semver = semver::Version::parse(&version_slice) + .map_err(|e| anyhow!("could not parse zig version: {version_slice}: {e}"))?; + + if version_semver < semver::Version::parse("0.10.0").unwrap() { + Err(anyhow!("`zig` binary in PATH (`{}`) is not a new enough version (`{version_slice}`): please use version `0.10.0` or newer.", retval.display())) + } else { + Ok(retval) + } + } + + #[test] + fn test_filter_tarball() { + use std::str::FromStr; + let test_paths = [ + "/test/wasmer-darwin-amd64.tar.gz", + "/test/wasmer-darwin-arm64.tar.gz", + "/test/wasmer-linux-aarch64.tar.gz", + "/test/wasmer-linux-amd64.tar.gz", + "/test/wasmer-linux-musl-amd64.tar.gz", + "/test/wasmer-windows-amd64.tar.gz", + "/test/wasmer-windows-gnu64.tar.gz", + "/test/wasmer-windows.exe", + ]; + + let paths = test_paths.iter().map(Path::new).collect::>(); + assert_eq!( + paths + .iter() + .filter(|p| crate::commands::utils::filter_tarball( + p, + &Triple::from_str("x86_64-windows").unwrap() + )) + .collect::>(), + vec![&Path::new("/test/wasmer-windows-gnu64.tar.gz")], + ); + + let paths = test_paths.iter().map(Path::new).collect::>(); + assert_eq!( + paths + .iter() + .filter(|p| crate::commands::utils::filter_tarball( + p, + &Triple::from_str("x86_64-windows-gnu").unwrap() + )) + .collect::>(), + vec![&Path::new("/test/wasmer-windows-gnu64.tar.gz")], + ); + + let paths = test_paths.iter().map(Path::new).collect::>(); + assert_eq!( + paths + .iter() + .filter(|p| crate::commands::utils::filter_tarball( + p, + &Triple::from_str("x86_64-windows-msvc").unwrap() + )) + .collect::>(), + vec![&Path::new("/test/wasmer-windows-gnu64.tar.gz")], + ); + + assert_eq!( + paths + .iter() + .filter(|p| crate::commands::utils::filter_tarball( + p, + &Triple::from_str("x86_64-darwin").unwrap() + )) + .collect::>(), + vec![&Path::new("/test/wasmer-darwin-amd64.tar.gz")], + ); + + assert_eq!( + paths + .iter() + .filter(|p| crate::commands::utils::filter_tarball( + p, + &Triple::from_str("x86_64-unknown-linux-gnu").unwrap() + )) + .collect::>(), + vec![&Path::new("/test/wasmer-linux-amd64.tar.gz")], + ); + + assert_eq!( + paths + .iter() + .filter(|p| crate::commands::utils::filter_tarball( + p, + &Triple::from_str("x86_64-linux-gnu").unwrap() + )) + .collect::>(), + vec![&Path::new("/test/wasmer-linux-amd64.tar.gz")], + ); + + assert_eq!( + paths + .iter() + .filter(|p| crate::commands::utils::filter_tarball( + p, + &Triple::from_str("aarch64-linux-gnu").unwrap() + )) + .collect::>(), + vec![&Path::new("/test/wasmer-linux-aarch64.tar.gz")], + ); + + assert_eq!( + paths + .iter() + .filter(|p| crate::commands::utils::filter_tarball( + p, + &Triple::from_str("x86_64-windows-gnu").unwrap() + )) + .collect::>(), + vec![&Path::new("/test/wasmer-windows-gnu64.tar.gz")], + ); + + assert_eq!( + paths + .iter() + .filter(|p| crate::commands::utils::filter_tarball( + p, + &Triple::from_str("aarch64-darwin").unwrap() + )) + .collect::>(), + vec![&Path::new("/test/wasmer-darwin-arm64.tar.gz")], + ); + } + + #[test] + fn test_normalize_atom_name() { + assert_eq!( + normalize_atom_name("atom-name-with-dash"), + "atom_name_with_dash".to_string() + ); + } +} + +mod http_fetch { + use std::path::Path; + + use anyhow::{anyhow, Context, Result}; + + pub(super) fn get_release( + release_version: Option, + ) -> Result { + let uri = "https://api.github.com/repos/wasmerio/wasmer/releases"; + + // Increases rate-limiting in GitHub CI + let auth = std::env::var("GITHUB_TOKEN"); + + let client = reqwest::blocking::Client::new(); + let mut req = client.get(uri); + if let Ok(token) = auth { + req = req.header("Authorization", &format!("Bearer {token}")); + } + + let response = req + .header("User-Agent", "wasmerio") + .header("Accept", "application/vnd.github.v3+json") + .send() + .map_err(anyhow::Error::new) + .context("Could not lookup wasmer repository on Github.")?; + + let status = response.status(); + + let body = response + .bytes() + .map_err(anyhow::Error::new) + .context("Could not retrieve wasmer release history body")?; + + if status != reqwest::StatusCode::OK { + log::warn!( + "Warning: Github API replied with non-200 status code: {}. Response: {}", + status, + String::from_utf8_lossy(&body), + ); + } + + let mut response = serde_json::from_slice::(&body)?; + + if let Some(releases) = response.as_array_mut() { + releases.retain(|r| { + r["tag_name"].is_string() && !r["tag_name"].as_str().unwrap().is_empty() + }); + releases.sort_by_cached_key(|r| r["tag_name"].as_str().unwrap_or_default().to_string()); + match release_version { + Some(specific_version) => { + let mut all_versions = Vec::new(); + for r in releases.iter() { + if r["tag_name"].as_str().unwrap_or_default() + == specific_version.to_string() + { + return Ok(r.clone()); + } else { + all_versions + .push(r["tag_name"].as_str().unwrap_or_default().to_string()); + } + } + return Err(anyhow::anyhow!( + "could not find release version {}, available versions are: {}", + specific_version, + all_versions.join(", ") + )); + } + None => { + if let Some(latest) = releases.pop() { + return Ok(latest); + } + } + } + } + + Err(anyhow!( + "Could not get expected Github API response.\n\nReason: response format is not recognized:\n{response:#?}", + )) + } + + pub(super) fn download_release( + mut release: serde_json::Value, + target_triple: wasmer::Triple, + ) -> Result { + // Test if file has been already downloaded + if let Ok(mut cache_path) = super::utils::get_libwasmer_cache_path() { + let paths = std::fs::read_dir(&cache_path).and_then(|r| { + r.map(|res| res.map(|e| e.path())) + .collect::, std::io::Error>>() + }); + + if let Ok(mut entries) = paths { + entries.retain(|p| p.to_str().map(|p| p.ends_with(".tar.gz")).unwrap_or(false)); + entries.retain(|p| super::utils::filter_tarball(p, &target_triple)); + if !entries.is_empty() { + cache_path.push(&entries[0]); + if cache_path.exists() { + eprintln!( + "Using cached tarball to cache path `{}`.", + cache_path.display() + ); + return Ok(cache_path); + } + } + } + } + + let assets = match release["assets"].as_array_mut() { + Some(s) => s, + None => { + return Err(anyhow!( + "GitHub API: no [assets] array in JSON response for latest releases" + )); + } + }; + + assets.retain(|a| { + let name = match a["name"].as_str() { + Some(s) => s, + None => return false, + }; + super::utils::filter_tarball(Path::new(&name), &target_triple) + }); + + if assets.len() != 1 { + return Err(anyhow!( + "GitHub API: more that one release selected for target {target_triple}: {assets:?}" + )); + } + + let browser_download_url = if let Some(url) = assets[0]["browser_download_url"].as_str() { + url.to_string() + } else { + return Err(anyhow!( + "Could not get download url from Github API response." + )); + }; + + download_url(&browser_download_url) + } + + pub(crate) fn download_url( + browser_download_url: &str, + ) -> Result { + let filename = browser_download_url + .split('/') + .last() + .unwrap_or("output") + .to_string(); + + let download_tempdir = tempfile::TempDir::new()?; + let download_path = download_tempdir.path().join(&filename); + + let mut file = std::fs::File::create(&download_path)?; + log::debug!( + "Downloading {} to {}", + browser_download_url, + download_path.display() + ); + + let mut response = reqwest::blocking::Client::builder() + .redirect(reqwest::redirect::Policy::limited(10)) + .timeout(std::time::Duration::from_secs(10)) + .build() + .map_err(anyhow::Error::new) + .context("Could not lookup wasmer artifact on Github.")? + .get(browser_download_url) + .send() + .map_err(anyhow::Error::new) + .context("Could not lookup wasmer artifact on Github.")?; + + response + .copy_to(&mut file) + .map_err(|e| anyhow::anyhow!("{e}"))?; + + match super::utils::get_libwasmer_cache_path() { + Ok(mut cache_path) => { + cache_path.push(&filename); + if let Err(err) = std::fs::copy(&download_path, &cache_path) { + eprintln!( + "Could not store tarball to cache path `{}`: {}", + cache_path.display(), + err + ); + Err(anyhow!( + "Could not copy from {} to {}", + download_path.display(), + cache_path.display() + )) + } else { + eprintln!("Cached tarball to cache path `{}`.", cache_path.display()); + Ok(cache_path) + } + } + Err(err) => { + eprintln!( + "Could not determine cache path for downloaded binaries.: {}", + err + ); + Err(anyhow!("Could not determine libwasmer cache path")) + } + } + } + + use std::path::PathBuf; + + pub(crate) fn list_dir(target: &Path) -> Vec { + use walkdir::WalkDir; + WalkDir::new(target) + .into_iter() + .filter_map(|e| e.ok()) + .map(|entry| entry.path().to_path_buf()) + .collect() + } + + pub(super) fn untar(tarball: &Path, target: &Path) -> Result> { + let _ = std::fs::remove_dir(target); + wasmer_registry::try_unpack_targz(tarball, target, false)?; + Ok(list_dir(target)) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/create_obj.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/create_obj.rs new file mode 100644 index 0000000..22a2f11 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/create_obj.rs @@ -0,0 +1,170 @@ +#![allow(dead_code)] +//! Create a standalone native executable for a given Wasm file. + +use std::{env, path::PathBuf}; + +use anyhow::{Context, Result}; +use clap::Parser; +use wasmer::*; + +use crate::store::CompilerOptions; + +#[derive(Debug, Parser)] +/// The options for the `wasmer create-exe` subcommand +pub struct CreateObj { + /// Input file + #[clap(name = "FILE")] + path: PathBuf, + + /// Output file or directory if the input is a pirita file + #[clap(name = "OUTPUT_PATH", short = 'o')] + output: PathBuf, + + /// Optional directorey used for debugging: if present, will + /// output the files to a debug instead of a temp directory + #[clap(long, name = "DEBUG PATH")] + debug_dir: Option, + + /// Prefix for the function names in the input file in the compiled object file. + /// + /// Default value = sha256 of the input file + #[clap(long, name = "PREFIX")] + prefix: Option, + + /// Atom name to compile when compiling multi-atom pirita files + #[clap(long, name = "ATOM")] + atom: Option, + + /// Compilation Target triple + /// + /// Accepted target triple values must follow the + /// ['target_lexicon'](https://crates.io/crates/target-lexicon) crate format. + /// + /// The recommended targets we try to support are: + /// + /// - "x86_64-linux-gnu" + /// - "aarch64-linux-gnu" + /// - "x86_64-apple-darwin" + /// - "arm64-apple-darwin" + /// - "x86_64-windows-gnu" + #[clap(long = "target")] + target_triple: Option, + + #[clap(long, short = 'm', number_of_values = 1)] + cpu_features: Vec, + + #[clap(flatten)] + compiler: CompilerOptions, +} + +impl CreateObj { + /// Runs logic for the `create-obj` subcommand + pub fn execute(&self) -> Result<()> { + let path = crate::common::normalize_path(&format!("{}", self.path.display())); + let target_triple = self.target_triple.clone().unwrap_or_else(Triple::host); + let starting_cd = env::current_dir()?; + let input_path = starting_cd.join(path); + let temp_dir = tempfile::tempdir(); + let output_directory_path = match self.debug_dir.as_ref() { + Some(s) => s.clone(), + None => temp_dir?.path().to_path_buf(), + }; + std::fs::create_dir_all(&output_directory_path)?; + let prefix = match self.prefix.as_ref() { + Some(s) => vec![s.clone()], + None => Vec::new(), + }; + + let target = crate::commands::create_exe::utils::target_triple_to_target( + &target_triple, + &self.cpu_features, + ); + let (_, compiler_type) = self.compiler.get_store_for_target(target.clone())?; + println!("Compiler: {}", compiler_type.to_string()); + println!("Target: {}", target.triple()); + + let atoms = if let Ok(webc) = webc::compat::Container::from_disk(&input_path) { + crate::commands::create_exe::compile_pirita_into_directory( + &webc, + &output_directory_path, + &self.compiler, + &self.cpu_features, + &target_triple, + &prefix, + crate::commands::AllowMultiWasm::Reject(self.atom.clone()), + self.debug_dir.is_some(), + ) + } else { + crate::commands::create_exe::prepare_directory_from_single_wasm_file( + &input_path, + &output_directory_path, + &self.compiler, + &target_triple, + &self.cpu_features, + &prefix, + self.debug_dir.is_some(), + ) + }?; + + // Copy output files into target path, depending on whether + // there are one or many files being compiled + let file_paths = std::fs::read_dir(output_directory_path.join("atoms")) + .map_err(|e| { + anyhow::anyhow!( + "could not read {}: {e}", + output_directory_path.join("atoms").display() + ) + })? + .filter_map(|path| path.ok()?.path().canonicalize().ok()) + .collect::>(); + + if file_paths.is_empty() { + return Err(anyhow::anyhow!( + "could not compile object file: no output objects in {}", + output_directory_path.join("atoms").display() + )); + } + + if file_paths.len() == 1 { + if let Some(parent) = self.output.parent() { + std::fs::create_dir_all(parent)?; + } + std::fs::copy( + std::env::current_dir().unwrap().join(&file_paths[0]), + std::env::current_dir().unwrap().join(&self.output), + ) + .map_err(|e| { + anyhow::anyhow!( + "{} -> {}: {e}", + &file_paths[0].display(), + self.output.display() + ) + })?; + } else { + let keys = atoms + .iter() + .map(|(name, _)| name.clone()) + .collect::>(); + return Err(anyhow::anyhow!( + "where is one of: {}", + keys.join(", ") + )) + .context(anyhow::anyhow!( + "note: use --atom to specify which atom to compile" + )) + .context(anyhow::anyhow!( + "cannot compile more than one atom at a time" + )); + } + + let output_file = self.output.canonicalize().unwrap().display().to_string(); + let output_file = output_file + .strip_prefix(r"\\?\") + .unwrap_or(&output_file) + .to_string(); + + eprintln!("✔ Object compiled successfully to `{output_file}`"); + + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/deploy.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/deploy.rs new file mode 100644 index 0000000..febfe7e --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/deploy.rs @@ -0,0 +1,236 @@ +use std::{io::Write, path::PathBuf}; + +use anyhow::{bail, Context}; +use edge_schema::schema::AppConfigV1; +use is_terminal::IsTerminal; +use wasmer_api::types::DeployAppVersion; + +use crate::{ + commands::{ + app::{deploy_app_verbose, DeployAppOpts, WaitMode}, + AsyncCliCommand, + }, + opts::{ApiOpts, ItemFormatOpts}, +}; + +/// Start a remote SSH session. +#[derive(clap::Parser, Debug)] +pub struct CmdDeploy { + #[clap(flatten)] + pub api: ApiOpts, + #[clap(flatten)] + pub fmt: ItemFormatOpts, + + /// Skip local schema validation. + #[clap(long)] + pub no_validate: bool, + + /// Do not prompt for user input. + #[clap(long)] + pub non_interactive: bool, + + /// Automatically publish the package referenced by this app. + /// + /// Only works if the corresponding wasmer.toml is in the same directory. + #[clap(long)] + pub publish_package: bool, + + /// The path to the app.yaml file. + #[clap(long)] + pub path: Option, + + /// Do not wait for the app to become reachable. + #[clap(long)] + pub no_wait: bool, + + /// Do not make the new app version the default (active) version. + /// This is useful for testing a deployment first, before moving it to "production". + #[clap(long)] + pub no_default: bool, + + /// Do not persist the app version ID in the app.yaml. + #[clap(long)] + pub no_persist_id: bool, + + /// Specify the owner (user or namespace) of the app. + /// Will default to the currently logged in user, or the existing one + /// if the app can be found. + #[clap(long)] + pub owner: Option, +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdDeploy { + type Output = DeployAppVersion; + + async fn run_async(self) -> Result { + let client = self.api.client()?; + + let base_path = if let Some(p) = self.path { + p + } else { + std::env::current_dir()? + }; + let file_path = if base_path.is_file() { + base_path + } else if base_path.is_dir() { + let full = base_path.join(AppConfigV1::CANONICAL_FILE_NAME); + if !full.is_file() { + bail!("Could not find app.yaml at path: '{}'", full.display()); + } + full + } else { + bail!("No such file or directory: '{}'", base_path.display()); + }; + let abs_dir_path = file_path.canonicalize()?.parent().unwrap().to_owned(); + + let interactive = std::io::stdin().is_terminal() && !self.non_interactive; + + let raw_config = std::fs::read_to_string(&file_path) + .with_context(|| format!("Could not read file: '{}'", file_path.display()))?; + + let orig_config = AppConfigV1::parse_yaml(&raw_config)?; + eprintln!("Loaded app from: {}", file_path.display()); + + // Parse a raw value - will be used later for patching. + let orig_config_value: serde_yaml::Value = + serde_yaml::from_str(&raw_config).context("Could not parse app.yaml")?; + + let pkg_name = format!( + "{}/{}", + orig_config.package.0.namespace, orig_config.package.0.name + ); + + // Check for a wasmer.toml + + let local_manifest_path = abs_dir_path.join(crate::utils::DEFAULT_PACKAGE_MANIFEST_FILE); + let local_manifest = crate::utils::load_package_manifest(&local_manifest_path)? + .map(|x| x.1) + // Ignore local package if it is not referenced by the app. + .filter(|m| m.package.name == pkg_name); + + let new_package_manifest = if let Some(manifest) = local_manifest { + let should_publish = if self.publish_package { + true + } else if interactive { + eprintln!(); + dialoguer::Confirm::new() + .with_prompt(format!("Publish new version of package '{}'?", pkg_name)) + .interact_opt()? + .unwrap_or_default() + } else { + false + }; + + if should_publish { + eprintln!("Publishing package..."); + let new_manifest = crate::utils::republish_package_with_bumped_version( + &client, + &local_manifest_path, + manifest, + ) + .await?; + + eprint!("Waiting for package to become available..."); + std::io::stderr().flush().unwrap(); + + let start_wait = std::time::Instant::now(); + loop { + if start_wait.elapsed().as_secs() > 300 { + bail!("Timed out waiting for package to become available"); + } + + eprint!("."); + std::io::stderr().flush().unwrap(); + + let new_version_opt = wasmer_api::query::get_package_version( + &client, + new_manifest.package.name.clone(), + new_manifest.package.version.to_string(), + ) + .await; + + match new_version_opt { + Ok(Some(new_version)) => { + if new_version.distribution.pirita_sha256_hash.is_some() { + eprintln!(); + break; + } + } + Ok(None) => { + bail!("Error - could not query package info: package not found"); + } + Err(e) => { + bail!("Error - could not query package info: {e}"); + } + } + + tokio::time::sleep(std::time::Duration::from_secs(3)).await; + } + + eprintln!( + "Package '{}@{}' published successfully!", + new_manifest.package.name, new_manifest.package.version + ); + eprintln!(); + Some(new_manifest) + } else { + if interactive { + eprintln!(); + } + None + } + } else { + None + }; + + let config = if let Some(manifest) = new_package_manifest { + let pkg = format!("{}@{}", manifest.package.name, manifest.package.version); + AppConfigV1 { + package: pkg.parse()?, + ..orig_config + } + } else { + orig_config + }; + + let wait_mode = if self.no_wait { + WaitMode::Deployed + } else { + WaitMode::Reachable + }; + + let opts = DeployAppOpts { + app: &config, + original_config: Some(orig_config_value.clone()), + allow_create: true, + make_default: !self.no_default, + owner: self.owner, + wait: wait_mode, + }; + let (_app, app_version) = deploy_app_verbose(&client, opts).await?; + + let mut new_config = super::app::app_config_from_api(&app_version)?; + if self.no_persist_id { + new_config.app_id = None; + } + let new_config_value = new_config.to_yaml_value()?; + + // If the config changed, write it back. + if new_config_value != orig_config_value { + // We want to preserve unknown fields to allow for newer app.yaml + // settings without requring new CLI versions, so instead of just + // serializing the new config, we merge it with the old one. + let new_merged = crate::utils::merge_yaml_values(&orig_config_value, &new_config_value); + let new_config_raw = serde_yaml::to_string(&new_merged)?; + std::fs::write(&file_path, new_config_raw) + .with_context(|| format!("Could not write file: '{}'", file_path.display()))?; + } + + if self.fmt.format == crate::utils::render::ItemFormat::Json { + println!("{}", serde_json::to_string_pretty(&app_version)?); + } + + Ok(app_version) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/domain/get.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/domain/get.rs new file mode 100644 index 0000000..81e15a2 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/domain/get.rs @@ -0,0 +1,32 @@ +use crate::{ + commands::AsyncCliCommand, + opts::{ApiOpts, ItemTableFormatOpts}, +}; + +/// Show a domain +#[derive(clap::Parser, Debug)] +pub struct CmdDomainGet { + #[clap(flatten)] + fmt: ItemTableFormatOpts, + #[clap(flatten)] + api: ApiOpts, + + /// Name of the domain. + name: String, +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdDomainGet { + type Output = (); + + async fn run_async(self) -> Result<(), anyhow::Error> { + let client = self.api.client()?; + if let Some(domain) = wasmer_api::query::get_domain_with_records(&client, self.name).await? + { + println!("{}", self.fmt.format.render(&domain)); + } else { + anyhow::bail!("Domain not found"); + } + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/domain/list.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/domain/list.rs new file mode 100644 index 0000000..14ba9f6 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/domain/list.rs @@ -0,0 +1,38 @@ +use wasmer_api::types::GetAllDomainsVariables; + +use crate::{ + commands::AsyncCliCommand, + opts::{ApiOpts, ListFormatOpts}, +}; + +/// List domains. +#[derive(clap::Parser, Debug)] +pub struct CmdDomainList { + #[clap(flatten)] + fmt: ListFormatOpts, + #[clap(flatten)] + api: ApiOpts, + + /// Name of the namespace. + namespace: Option, +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdDomainList { + type Output = (); + + async fn run_async(self) -> Result<(), anyhow::Error> { + let client = self.api.client()?; + let domains = wasmer_api::query::get_all_domains( + &client, + GetAllDomainsVariables { + first: None, + after: None, + namespace: self.namespace, + }, + ) + .await?; + println!("{}", self.fmt.format.render(&domains)); + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/domain/mod.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/domain/mod.rs new file mode 100644 index 0000000..ec9de5d --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/domain/mod.rs @@ -0,0 +1,39 @@ +pub mod get; +pub mod list; +pub mod register; +pub mod zonefile; +use crate::commands::AsyncCliCommand; + +/// Manage DNS records +#[derive(clap::Subcommand, Debug)] +pub enum CmdDomain { + /// List domains + List(self::list::CmdDomainList), + + /// Get a domain + Get(self::get::CmdDomainGet), + + /// Get zone file for a domain + GetZoneFile(self::zonefile::CmdZoneFileGet), + + /// Sync local zone file with remotex + SyncZoneFile(self::zonefile::CmdZoneFileSync), + + /// Register new domain + Register(self::register::CmdDomainRegister), +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdDomain { + type Output = (); + + async fn run_async(self) -> Result<(), anyhow::Error> { + match self { + CmdDomain::List(cmd) => cmd.run_async().await, + CmdDomain::Get(cmd) => cmd.run_async().await, + CmdDomain::GetZoneFile(cmd) => cmd.run_async().await, + CmdDomain::SyncZoneFile(cmd) => cmd.run_async().await, + CmdDomain::Register(cmd) => cmd.run_async().await, + } + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/domain/register.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/domain/register.rs new file mode 100644 index 0000000..ef2d5f3 --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/domain/register.rs @@ -0,0 +1,45 @@ +use crate::{ + commands::AsyncCliCommand, + opts::{ApiOpts, ItemTableFormatOpts}, +}; + +/// Show a domain +#[derive(clap::Parser, Debug)] +pub struct CmdDomainRegister { + #[clap(flatten)] + fmt: ItemTableFormatOpts, + #[clap(flatten)] + api: ApiOpts, + + /// Name of the domain. + name: String, + + /// owner under which the domain will live. + #[clap(long, short)] + namespace: Option, + + /// auto update DNS records for this domain. + #[clap(long, short)] + import_records: bool, +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdDomainRegister { + type Output = (); + + async fn run_async(self) -> Result<(), anyhow::Error> { + let client = self.api.client()?; + let domain = wasmer_api::query::register_domain( + &client, + self.name, + self.namespace, + Some(self.import_records), + ) + .await?; + println!( + "{}: domain registered under owner `{}`", + domain.name, domain.owner.global_name + ); + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/domain/zonefile.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/domain/zonefile.rs new file mode 100644 index 0000000..692e4cb --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/domain/zonefile.rs @@ -0,0 +1,77 @@ +use crate::{ + commands::AsyncCliCommand, + opts::{ApiOpts, ItemFormatOpts}, +}; +use anyhow::Context; + +#[derive(clap::Parser, Debug)] +/// Show a zone file +pub struct CmdZoneFileGet { + #[clap(flatten)] + fmt: ItemFormatOpts, + + #[clap(flatten)] + api: ApiOpts, + + /// Name of the domain. + domain_name: String, + + /// output file name to store zone file + #[clap(short = 'o', long = "output", required = false)] + zone_file_path: Option, +} + +#[derive(clap::Parser, Debug)] +/// Show a zone file +pub struct CmdZoneFileSync { + #[clap(flatten)] + api: ApiOpts, + + /// filename of zone-file to sync + zone_file_path: String, + + /// Do not delete records that are not present in the zone file + #[clap(short = 'n', long = "no-delete-missing-records")] + no_delete_missing_records: bool, +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdZoneFileGet { + type Output = (); + + async fn run_async(self) -> Result<(), anyhow::Error> { + let client = self.api.client()?; + if let Some(domain) = + wasmer_api::query::get_domain_zone_file(&client, self.domain_name).await? + { + let zone_file_contents = domain.zone_file; + if let Some(zone_file_path) = self.zone_file_path { + std::fs::write(zone_file_path, zone_file_contents) + .context("Unable to write file")?; + } else { + println!("{}", zone_file_contents); + } + } else { + anyhow::bail!("Domain not found"); + } + Ok(()) + } +} + +#[async_trait::async_trait] +impl AsyncCliCommand for CmdZoneFileSync { + type Output = (); + + async fn run_async(self) -> Result<(), anyhow::Error> { + let data = std::fs::read(&self.zone_file_path).context("Unable to read file")?; + let zone_file_contents = String::from_utf8(data).context("Not a valid UTF-8 sequence")?; + let domain = wasmer_api::query::upsert_domain_from_zone_file( + &self.api.client()?, + zone_file_contents, + !self.no_delete_missing_records, + ) + .await?; + println!("Successfully synced domain: {}", domain.name); + Ok(()) + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/gen_c_header.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/gen_c_header.rs new file mode 100644 index 0000000..142c41a --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/gen_c_header.rs @@ -0,0 +1,143 @@ +use std::path::PathBuf; + +use anyhow::{Context, Error}; +use bytes::Bytes; +use clap::Parser; +use wasmer_compiler::Artifact; +use wasmer_types::{ + compilation::symbols::ModuleMetadataSymbolRegistry, CpuFeature, MetadataHeader, Triple, +}; +use webc::{compat::SharedBytes, Container, DetectError}; + +use crate::store::CompilerOptions; + +#[derive(Debug, Parser)] +/// The options for the `wasmer gen-c-header` subcommand +pub struct GenCHeader { + /// Input file + #[clap(name = "FILE")] + path: PathBuf, + + /// Prefix hash (default: SHA256 of input .wasm file) + #[clap(long)] + prefix: Option, + + /// For pirita files: optional atom name to compile + #[clap(long)] + atom: Option, + + /// Output file + #[clap(name = "OUTPUT PATH", short = 'o')] + output: PathBuf, + + /// Compilation Target triple + /// + /// Accepted target triple values must follow the + /// ['target_lexicon'](https://crates.io/crates/target-lexicon) crate format. + /// + /// The recommended targets we try to support are: + /// + /// - "x86_64-linux-gnu" + /// - "aarch64-linux-gnu" + /// - "x86_64-apple-darwin" + /// - "arm64-apple-darwin" + /// - "x86_64-windows-gnu" + #[clap(long = "target")] + target_triple: Option, + + #[clap(long, short = 'm', number_of_values = 1)] + cpu_features: Vec, +} + +impl GenCHeader { + /// Runs logic for the `gen-c-header` subcommand + pub fn execute(&self) -> Result<(), Error> { + let file: Bytes = std::fs::read(&self.path) + .with_context(|| format!("Unable to read \"{}\"", self.path.display()))? + .into(); + let prefix = match self.prefix.as_deref() { + Some(s) => s.to_string(), + None => crate::commands::PrefixMapCompilation::hash_for_bytes(&file), + }; + + let atom = match Container::from_bytes(file.clone()) { + Ok(webc) => self.get_atom(&webc)?, + Err(webc::compat::ContainerError::Detect(DetectError::InvalidMagic { .. })) => { + // we've probably got a WebAssembly file + file.into() + } + Err(other) => { + return Err(Error::new(other).context("Unable to parse the webc file")); + } + }; + + let target_triple = self.target_triple.clone().unwrap_or_else(Triple::host); + let target = crate::commands::create_exe::utils::target_triple_to_target( + &target_triple, + &self.cpu_features, + ); + let (engine, _) = CompilerOptions::default().get_engine_for_target(target.clone())?; + let engine_inner = engine.inner(); + let compiler = engine_inner.compiler()?; + let features = engine_inner.features(); + let tunables = engine.tunables(); + let (metadata, _, _) = Artifact::metadata( + compiler, + &atom, + Some(prefix.as_str()), + &target, + tunables, + features, + ) + .map_err(|e| anyhow::anyhow!("could not generate metadata: {e}"))?; + + let serialized_data = metadata + .serialize() + .map_err(|e| anyhow::anyhow!("failed to serialize: {e}"))?; + let mut metadata_binary = vec![]; + metadata_binary.extend(MetadataHeader::new(serialized_data.len()).into_bytes()); + metadata_binary.extend(serialized_data); + let metadata_length = metadata_binary.len(); + + let header_file_src = crate::c_gen::staticlib_header::generate_header_file( + &prefix, + &metadata.compile_info.module, + &ModuleMetadataSymbolRegistry { + prefix: prefix.clone(), + }, + metadata_length, + ); + + let output = crate::common::normalize_path(&self.output.display().to_string()); + + std::fs::write(&output, header_file_src) + .map_err(|e| anyhow::anyhow!("{e}")) + .with_context(|| anyhow::anyhow!("{output}"))?; + + Ok(()) + } + + fn get_atom(&self, pirita: &Container) -> Result { + let atoms = pirita.atoms(); + let atom_names: Vec<_> = atoms.keys().map(|s| s.as_str()).collect(); + + match *atom_names.as_slice() { + [] => Err(Error::msg("The file doesn't contain any atoms")), + [name] => Ok(atoms[name].clone()), + [..] => match &self.atom { + Some(name) => atoms + .get(name) + .cloned() + .with_context(|| format!("The file doesn't contain a \"{name}\" atom")) + .with_context(|| { + format!("-> note: available atoms are: {}", atom_names.join(", ")) + }), + None => { + let err = Error::msg("file has multiple atoms, please specify which atom to generate the header file for") + .context(format!("-> note: available atoms are: {}", atom_names.join(", "))); + Err(err) + } + }, + } + } +} diff --git a/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/init.rs b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/init.rs new file mode 100644 index 0000000..b81babc --- /dev/null +++ b/arbitrator/tools/module_roots/wasmer/lib/cli/src/commands/init.rs @@ -0,0 +1,555 @@ +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; + +use anyhow::Context; +use cargo_metadata::{CargoOpt, MetadataCommand}; +use clap::Parser; +use semver::VersionReq; +use wasmer_registry::wasmer_env::WasmerEnv; + +static NOTE: &str = "# See more keys and definitions at https://docs.wasmer.io/registry/manifest"; + +const NEWLINE: &str = if cfg!(windows) { "\r\n" } else { "\n" }; + +/// CLI args for the `wasmer init` command +#[derive(Debug, Parser)] +pub struct Init { + #[clap(flatten)] + env: WasmerEnv, + + /// Initialize wasmer.toml for a library package + #[clap(long, group = "crate-type")] + pub lib: bool, + /// Initialize wasmer.toml for a binary package + #[clap(long, group = "crate-type")] + pub bin: bool, + /// Initialize an empty wasmer.toml + #[clap(long, group = "crate-type")] + pub empty: bool, + /// Force overwriting the wasmer.toml, even if it already exists + #[clap(long)] + pub overwrite: bool, + /// Don't display debug output + #[clap(long)] + pub quiet: bool, + /// Namespace to init with, default = current logged in user or _ + #[clap(long)] + pub namespace: Option, + /// Package name to init with, default = Cargo.toml name or current directory name + #[clap(long)] + pub package_name: Option, + /// Version of the initialized package + #[clap(long)] + pub version: Option, + /// If the `manifest-path` is a Cargo.toml, use that file to initialize the wasmer.toml + #[clap(long)] + pub manifest_path: Option, + /// Add default dependencies for common packages + #[clap(long, value_enum)] + pub template: Option