diff --git a/Cargo.toml b/Cargo.toml index 4dddc77a3595..9129c2529602 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -172,6 +172,7 @@ members = [ ] exclude = [ 'docs/rust_wasi_markdown_parser', + 'crates/wizer', ] [workspace.package] diff --git a/crates/wizer/.github/actions/binary-compatible-builds/README.md b/crates/wizer/.github/actions/binary-compatible-builds/README.md new file mode 100644 index 000000000000..8368fd4f1a9e --- /dev/null +++ b/crates/wizer/.github/actions/binary-compatible-builds/README.md @@ -0,0 +1,9 @@ +# binary-compatible-builds + +A small (ish) action which is intended to be used and will configure builds of +Rust projects to be "more binary compatible". On Windows and macOS this +involves setting a few env vars, and on Linux this involves spinning up a CentOS +6 container which is running in the background. + +All subsequent build commands need to be wrapped in `$CENTOS` to optionally run +on `$CENTOS` on Linux to ensure builds happen inside the container. diff --git a/crates/wizer/.github/actions/binary-compatible-builds/action.yml b/crates/wizer/.github/actions/binary-compatible-builds/action.yml new file mode 100644 index 000000000000..8abc72d63742 --- /dev/null +++ b/crates/wizer/.github/actions/binary-compatible-builds/action.yml @@ -0,0 +1,10 @@ +name: 'Set up a CentOS 6 container to build releases in' +description: 'Set up a CentOS 6 container to build releases in' + +runs: + using: node20 + main: 'main.js' +inputs: + name: + required: true + description: "Name of the build" diff --git a/crates/wizer/.github/actions/binary-compatible-builds/main.js b/crates/wizer/.github/actions/binary-compatible-builds/main.js new file mode 100755 index 000000000000..6b72ebe2b4cc --- /dev/null +++ b/crates/wizer/.github/actions/binary-compatible-builds/main.js @@ -0,0 +1,62 @@ +#!/usr/bin/env node + +const child_process = require('child_process'); +const stdio = { stdio: 'inherit' }; +const fs = require('fs'); + +function set_env(name, val) { + fs.appendFileSync(process.env['GITHUB_ENV'], `${name}=${val}\n`) +} + +// On OSX all we need to do is configure our deployment target as old as +// possible. For now 10.9 is the limit. +if (process.platform == 'darwin') { + set_env("MACOSX_DEPLOYMENT_TARGET", "10.9"); + return; +} + +// On Windows we build against the static CRT to reduce dll dependencies +if (process.platform == 'win32') { + set_env("RUSTFLAGS", "-Ctarget-feature=+crt-static"); + return; +} + +// Android doesn't use a container as it's controlled by the installation of the +// SDK/NDK. +if (process.env.INPUT_NAME && process.env.INPUT_NAME.indexOf("android") >= 0) { + return; +} + +// ... and on Linux we do fancy things with containers. We'll spawn an old +// CentOS container in the background with a super old glibc, and then we'll run +// commands in there with the `$CENTOS` env var. + +if (process.env.CENTOS !== undefined) { + const args = ['exec', '-w', process.cwd(), '-i', 'build-container']; + for (const arg of process.argv.slice(2)) { + args.push(arg); + } + child_process.execFileSync('docker', args, stdio); + return; +} + +const name = process.env.INPUT_NAME.replace(/-min$/, ''); + +child_process.execFileSync('docker', [ + 'build', + '--tag', 'build-image', + `${process.cwd()}/ci/docker/${name}` +], stdio); + +child_process.execFileSync('docker', [ + 'run', + '--detach', + '--interactive', + '--name', 'build-container', + '-v', `${process.cwd()}:${process.cwd()}`, + '-v', `${child_process.execSync('rustc --print sysroot').toString().trim()}:/rust:ro`, + 'build-image', +], stdio); + +// Use ourselves to run future commands +set_env("CENTOS", __filename); diff --git a/crates/wizer/.github/actions/install-rust/README.md b/crates/wizer/.github/actions/install-rust/README.md new file mode 100644 index 000000000000..df8e94dcccbf --- /dev/null +++ b/crates/wizer/.github/actions/install-rust/README.md @@ -0,0 +1,18 @@ +# install-rust + +A small github action to install `rustup` and a Rust toolchain. This is +generally expressed inline, but it was repeated enough in this repository it +seemed worthwhile to extract. + +Some gotchas: + +* Can't `--self-update` on Windows due to permission errors (a bug in Github + Actions) +* `rustup` isn't installed on macOS (a bug in Github Actions) + +When the above are fixed we should delete this action and just use this inline: + +```yml +- run: rustup update $toolchain && rustup default $toolchain + shell: bash +``` diff --git a/crates/wizer/.github/actions/install-rust/action.yml b/crates/wizer/.github/actions/install-rust/action.yml new file mode 100644 index 000000000000..7a196591840d --- /dev/null +++ b/crates/wizer/.github/actions/install-rust/action.yml @@ -0,0 +1,12 @@ +name: 'Install Rust toolchain' +description: 'Install both `rustup` and a Rust toolchain' + +inputs: + toolchain: + description: 'Default toolchan to install' + required: false + default: 'stable' + +runs: + using: node12 + main: 'main.js' diff --git a/crates/wizer/.github/actions/install-rust/main.js b/crates/wizer/.github/actions/install-rust/main.js new file mode 100644 index 000000000000..b144d70aea43 --- /dev/null +++ b/crates/wizer/.github/actions/install-rust/main.js @@ -0,0 +1,36 @@ +const child_process = require('child_process'); +const toolchain = process.env.INPUT_TOOLCHAIN; +const fs = require('fs'); + +function set_env(name, val) { + fs.appendFileSync(process.env['GITHUB_ENV'], `${name}=${val}\n`) +} + +// Needed for now to get 1.24.2 which fixes a bug in 1.24.1 that causes issues +// on Windows. +if (process.platform === 'win32') { + child_process.execFileSync('rustup', ['self', 'update']); +} + +child_process.execFileSync('rustup', ['set', 'profile', 'minimal']); +child_process.execFileSync('rustup', ['update', toolchain, '--no-self-update']); +child_process.execFileSync('rustup', ['default', toolchain]); + +// Deny warnings on CI to keep our code warning-free as it lands in-tree. Don't +// do this on nightly though since there's a fair amount of warning churn there. +if (!toolchain.startsWith('nightly')) { + set_env("RUSTFLAGS", "-D warnings"); +} + +// Save disk space by avoiding incremental compilation, and also we don't use +// any caching so incremental wouldn't help anyway. +set_env("CARGO_INCREMENTAL", "0"); + +// Turn down debuginfo from 2 to 1 to help save disk space +set_env("CARGO_PROFILE_DEV_DEBUG", "1"); +set_env("CARGO_PROFILE_TEST_DEBUG", "1"); + +if (process.platform === 'darwin') { + set_env("CARGO_PROFILE_DEV_SPLIT_DEBUGINFO", "unpacked"); + set_env("CARGO_PROFILE_TEST_SPLIT_DEBUGINFO", "unpacked"); +} diff --git a/crates/wizer/.github/workflows/ci.yml b/crates/wizer/.github/workflows/ci.yml new file mode 100644 index 000000000000..0c4ac924d011 --- /dev/null +++ b/crates/wizer/.github/workflows/ci.yml @@ -0,0 +1,56 @@ +name: CI + +on: pull_request + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - build: x86_64-linux + os: ubuntu-latest + - build: x86_64-macos + os: macos-latest + target: x86_64-apple-darwin + - build: aarch64-macos + os: macos-latest + target: aarch64-apple-darwin + - build: x86_64-windows + os: windows-latest + - build: x86_64-mingw + os: windows-latest + target: x86_64-pc-windows-gnu + - build: aarch64-linux + os: ubuntu-latest + target: aarch64-unknown-linux-gnu + - build: s390x-linux + os: ubuntu-latest + target: s390x-unknown-linux-gnu + steps: + - uses: actions/checkout@v2 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose + - name: Checking benches + run : cargo check --benches + + check_fuzz: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: cargo install cargo-fuzz + - run: cargo fuzz build --dev -s none + + rustfmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: rustup update stable --no-self-update + - run: rustup default stable + - run: rustup component add rustfmt + - run: cargo fmt --all -- --check diff --git a/crates/wizer/.github/workflows/release.yml b/crates/wizer/.github/workflows/release.yml new file mode 100644 index 000000000000..648eae191961 --- /dev/null +++ b/crates/wizer/.github/workflows/release.yml @@ -0,0 +1,138 @@ +name: Release +on: + push: + branches: [main] + tags-ignore: [dev] + pull_request: +defaults: + run: + shell: bash + +permissions: write-all + +# Cancel any in-flight jobs for the same PR/branch so there's only one active +# at a time +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - build: x86_64-linux + os: ubuntu-latest + - build: x86_64-macos + os: macos-latest + target: x86_64-apple-darwin + - build: aarch64-macos + os: macos-latest + target: aarch64-apple-darwin + - build: x86_64-windows + os: windows-latest + - build: x86_64-mingw + os: windows-latest + target: x86_64-pc-windows-gnu + - build: aarch64-linux + os: ubuntu-latest + target: aarch64-unknown-linux-gnu + - build: s390x-linux + os: ubuntu-latest + target: s390x-unknown-linux-gnu + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - uses: ./.github/actions/install-rust + - uses: ./.github/actions/binary-compatible-builds + with: + name: ${{ matrix.build }} + - run: | + echo CARGO_BUILD_TARGET=${{ matrix.target }} >> $GITHUB_ENV + rustup target add ${{ matrix.target }} + if: matrix.target != '' + + # Build `wizer` and executables + - run: $CENTOS cargo build --release --locked --bin wizer --all-features + + # Assemble release artifats appropriate for this platform, then upload them + # unconditionally to this workflow's files so we have a copy of them. + - run: ./ci/build-tarballs.sh "${{ matrix.build }}" "${{ matrix.target }}" + - uses: actions/upload-artifact@v4 + with: + name: bins-${{ matrix.build }} + path: dist + publish: + needs: [build] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + # Download all the artifacts that we'll be publishing. Should keep an eye on + # the `download-artifact` repository to see if we can ever get something + # like "download all artifacts" or "download this list of artifacts" + - uses: actions/download-artifact@v4 + with: + name: bins-x86_64-macos + path: dist + - uses: actions/download-artifact@v4 + with: + name: bins-aarch64-macos + path: dist + - uses: actions/download-artifact@v4 + with: + name: bins-x86_64-windows + path: dist + - uses: actions/download-artifact@v4 + with: + name: bins-x86_64-mingw + path: dist + - uses: actions/download-artifact@v4 + with: + name: bins-x86_64-linux + path: dist + - uses: actions/download-artifact@v4 + with: + name: bins-aarch64-linux + path: dist + - uses: actions/download-artifact@v4 + with: + name: bins-s390x-linux + path: dist + + - name: Calculate tag name + run: | + name=dev + if [[ $GITHUB_REF == refs/tags/v* ]]; then + name=${GITHUB_REF:10} + fi + echo ::set-output name=val::$name + echo TAG=$name >> $GITHUB_ENV + id: tagname + + # ... and if this was an actual push (tag or `main`) then we publish a + # new release. This'll automatically publish a tag release or update `dev` + # with this `sha`. Note that `continue-on-error` is set here so if this hits + # a bug we can go back and fetch and upload the release ourselves. + - run: cd .github/actions/github-release && npm install --production + - name: Publish Release + uses: ./.github/actions/github-release + if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) + with: + files: "dist/*" + token: ${{ secrets.GITHUB_TOKEN }} + name: ${{ steps.tagname.outputs.val }} + continue-on-error: true + - name: Update npm packages to latest version + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + working-directory: ./npm/wizer + run: npm install && node update.js "${{ steps.tagname.outputs.val }}" + - name: Setup npm auth + run: sudo npm config --global set '//registry.npmjs.org/:_authToken'='${NODE_AUTH_TOKEN}' + - name: Publish npm packages + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + working-directory: ./npm + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: for dir in *; do (echo $dir && cd $dir && npm publish); done diff --git a/crates/wizer/.gitignore b/crates/wizer/.gitignore new file mode 100644 index 000000000000..196c957bbba6 --- /dev/null +++ b/crates/wizer/.gitignore @@ -0,0 +1,8 @@ +/target +node_modules +npm/wizer-darwin-arm64/ +npm/wizer-darwin-x64/ +npm/wizer-linux-x64/ +npm/wizer-win32-x64/ +npm/wizer-linux-s390x/ +npm/wizer-linux-arm64/ \ No newline at end of file diff --git a/crates/wizer/.gitmodules b/crates/wizer/.gitmodules new file mode 100644 index 000000000000..9d47e3e62af0 --- /dev/null +++ b/crates/wizer/.gitmodules @@ -0,0 +1,3 @@ +[submodule "benches/uap-bench/uap-core"] + path = benches/uap-bench/uap-core + url = https://github.com/ua-parser/uap-core.git diff --git a/crates/wizer/CODE_OF_CONDUCT.md b/crates/wizer/CODE_OF_CONDUCT.md new file mode 100644 index 000000000000..14db69404fe7 --- /dev/null +++ b/crates/wizer/CODE_OF_CONDUCT.md @@ -0,0 +1,49 @@ +# Contributor Covenant Code of Conduct + +*Note*: this Code of Conduct pertains to individuals' behavior. Please also see the [Organizational Code of Conduct][OCoC]. + +## 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, gender identity and expression, level of experience, 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 Bytecode Alliance CoC team at [report@bytecodealliance.org](mailto:report@bytecodealliance.org). The CoC team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The CoC 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 Bytecode Alliance's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[OCoC]: https://github.com/bytecodealliance/wasmtime/blob/main/ORG_CODE_OF_CONDUCT.md +[homepage]: https://www.contributor-covenant.org +[version]: https://www.contributor-covenant.org/version/1/4/ diff --git a/crates/wizer/CONTRIBUTING.md b/crates/wizer/CONTRIBUTING.md new file mode 100644 index 000000000000..17f77c3fdb1c --- /dev/null +++ b/crates/wizer/CONTRIBUTING.md @@ -0,0 +1,28 @@ +# Contributing to Wizer + +## Code of Conduct + +Wizer is a [Bytecode Alliance] project. It follows the Bytecode Alliance's [Code +of Conduct] and [Organizational Code of Conduct]. + +[Bytecode Alliance]: https://bytecodealliance.org/ +[Code of Conduct]: CODE_OF_CONDUCT.md +[Organizational Code of Conduct]: ORG_CODE_OF_CONDUCT.md + +## Building + +``` +$ cargo build +``` + +## Testing + +``` +$ cargo test +``` + +## Benchmarking + +``` +$ cargo bench +``` diff --git a/crates/wizer/Cargo.lock b/crates/wizer/Cargo.lock new file mode 100644 index 000000000000..5414c76b34a1 --- /dev/null +++ b/crates/wizer/Cargo.lock @@ -0,0 +1,3010 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[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 = "addr2line" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9acbfca36652500c911ddb767ed433e3ed99b032b5d935be73c6923662db1d43" +dependencies = [ + "gimli 0.32.2", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "ambient-authority" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" + +[[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 = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[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 = "anyhow" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + +[[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.98", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[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 0.21.0", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object 0.32.2", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[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 = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +dependencies = [ + "allocator-api2", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" + +[[package]] +name = "cap-fs-ext" +version = "3.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e41cc18551193fe8fa6f15c1e3c799bc5ec9e2cfbfaa8ed46f37013e3e6c173c" +dependencies = [ + "cap-primitives", + "cap-std", + "io-lifetimes", + "windows-sys 0.59.0", +] + +[[package]] +name = "cap-net-ext" +version = "3.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f83833816c66c986e913b22ac887cec216ea09301802054316fc5301809702c" +dependencies = [ + "cap-primitives", + "cap-std", + "rustix 1.0.8", + "smallvec", +] + +[[package]] +name = "cap-primitives" +version = "3.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a1e394ed14f39f8bc26f59d4c0c010dbe7f0a1b9bafff451b1f98b67c8af62a" +dependencies = [ + "ambient-authority", + "fs-set-times", + "io-extras", + "io-lifetimes", + "ipnet", + "maybe-owned", + "rustix 1.0.8", + "rustix-linux-procfs", + "windows-sys 0.59.0", + "winx", +] + +[[package]] +name = "cap-rand" +version = "3.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0acb89ccf798a28683f00089d0630dfaceec087234eae0d308c05ddeaa941b40" +dependencies = [ + "ambient-authority", + "rand", +] + +[[package]] +name = "cap-std" +version = "3.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c0355ca583dd58f176c3c12489d684163861ede3c9efa6fd8bba314c984189" +dependencies = [ + "cap-primitives", + "io-extras", + "io-lifetimes", + "rustix 1.0.8", +] + +[[package]] +name = "cap-time-ext" +version = "3.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "491af520b8770085daa0466978c75db90368c71896523f2464214e38359b1a5b" +dependencies = [ + "ambient-authority", + "cap-primitives", + "iana-time-zone", + "once_cell", + "rustix 1.0.8", + "winx", +] + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[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 = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[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", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim", + "textwrap", + "unicode-width 0.1.11", + "vec_map", +] + +[[package]] +name = "clap" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpp_demangle" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-assembler-x64" +version = "0.123.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0920ef6863433fa28ece7e53925be4cd39a913adba2dc3738f4edd182f76d168" +dependencies = [ + "cranelift-assembler-x64-meta", +] + +[[package]] +name = "cranelift-assembler-x64-meta" +version = "0.123.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8990a217e2529a378af1daf4f8afa889f928f07ebbde6ae2f058ae60e40e2c20" +dependencies = [ + "cranelift-srcgen", +] + +[[package]] +name = "cranelift-bforest" +version = "0.123.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62225596b687f69a42c038485a28369badc186cb7c74bd9436eeec9f539011b1" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-bitset" +version = "0.123.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c23914fc4062558650a6f0d8c1846c97b541215a291fdeabc85f68bdc9bbcca3" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "cranelift-codegen" +version = "0.123.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a238b2f7e7ec077eb170145fa15fd8b3d0f36cc83d8e354e29ca550f339ca7" +dependencies = [ + "bumpalo", + "cranelift-assembler-x64", + "cranelift-bforest", + "cranelift-bitset", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-isle", + "gimli 0.32.2", + "hashbrown", + "log", + "pulley-interpreter", + "regalloc2", + "rustc-hash", + "serde", + "smallvec", + "target-lexicon", + "wasmtime-internal-math", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.123.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9315ddcc2512513a9d66455ec89bb70ae5498cb472f5ed990230536f4cd5c011" +dependencies = [ + "cranelift-assembler-x64-meta", + "cranelift-codegen-shared", + "cranelift-srcgen", + "heck 0.5.0", + "pulley-interpreter", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.123.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6acea40ef860f28cb36eaad479e26556c1e538b0a66fc44598cf1b1689393d" + +[[package]] +name = "cranelift-control" +version = "0.123.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b2af895da90761cfda4a4445960554fcec971e637882eda5a87337d993fe1b9" +dependencies = [ + "arbitrary", +] + +[[package]] +name = "cranelift-entity" +version = "0.123.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e8c542c856feb50d504e4fc0526b3db3a514f882a9f68f956164531517828ab" +dependencies = [ + "cranelift-bitset", + "serde", + "serde_derive", +] + +[[package]] +name = "cranelift-frontend" +version = "0.123.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9996dd9c20929c03360fe0c4edf3594c0cbb94525bdbfa04b6bb639ec14573c7" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.123.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928b8dccad51b9e0ffe54accbd617da900239439b13d48f0f122ab61105ca6ad" + +[[package]] +name = "cranelift-native" +version = "0.123.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f75ef0a6a2efed3a2a14812318e28dc82c214eab5399c13d70878e2f88947b5" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-srcgen" +version = "0.123.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673bd6d1c83cb41d60afb140a1474ef6caf1a3e02f3820fc522aefbc93ac67d6" + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap 4.5.4", + "criterion-plot", + "is-terminal", + "itertools 0.10.5", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "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 = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[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 = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[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", +] + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fd-lock" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" +dependencies = [ + "cfg-if", + "rustix 0.38.44", + "windows-sys 0.52.0", +] + +[[package]] +name = "flagset" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb3aa5e95cf9aabc17f060cfa0ced7b83f042390760ca53bf09df9968acaa1" + +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + +[[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-set-times" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033b337d725b97690d86893f9de22b67b80dcc4e9ad815f348254c38119db8fb" +dependencies = [ + "io-lifetimes", + "rustix 0.38.44", + "windows-sys 0.52.0", +] + +[[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-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-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[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-core", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "fxprof-processed-profile" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" +dependencies = [ + "bitflags 2.5.0", + "debugid", + "fxhash", + "serde", + "serde_json", +] + +[[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 = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "gimli" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6298e594375a7fead9efd5568f0a46e6a154fb6a9bdcbe3c06946ffd81a5f6" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "half" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", + "serde", +] + +[[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.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[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 = "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 = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +dependencies = [ + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "io-extras" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2285ddfe3054097ef4b2fe909ef8c3bcd1ea52a8f0d274416caebeef39f04a65" +dependencies = [ + "io-lifetimes", + "windows-sys 0.59.0", +] + +[[package]] +name = "io-lifetimes" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a611371471e98973dbcab4e0ec66c31a10bc356eeb4d54a0e05eac8158fe38c" + +[[package]] +name = "io-uring" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "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 0.3.9", + "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.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "ittapi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b996fe614c41395cdaedf3cf408a9534851090959d90d54a535f675550b64b1" +dependencies = [ + "anyhow", + "ittapi-sys", + "log", +] + +[[package]] +name = "ittapi-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f5385394064fa2c886205dba02598013ce83d3e92d33dbdc0c52fe0e7bf4fc" +dependencies = [ + "cc", +] + +[[package]] +name = "jiff" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f33145a5cbea837164362c7bd596106eb7c5198f97d1ba6f6ebb3223952e488" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43ce13c40ec6956157a3635d97a1ee2df323b263f09ea14165131289cb0f5c19" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[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 = "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 = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[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 = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[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 = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + +[[package]] +name = "maybe-owned" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "memfd" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" +dependencies = [ + "rustix 0.38.44", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "crc32fast", + "hashbrown", + "indexmap", + "memchr", +] + +[[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 = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[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 = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "postcard" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" +dependencies = [ + "cobs", + "embedded-io", + "serde", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[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-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pulley-interpreter" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e2d31146038fd9e62bfa331db057aca325d5ca10451a9fe341356cead7da53" +dependencies = [ + "cranelift-bitset", + "log", + "pulley-macros", + "wasmtime-internal-math", +] + +[[package]] +name = "pulley-macros" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efb9fdafaca625f9ea8cfa793364ea1bdd32d306cff18f166b00ddaa61ecbb27" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[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", + "crossbeam-utils", +] + +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror 1.0.58", +] + +[[package]] +name = "regalloc2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5216b1837de2149f8bc8e6d5f88a9326b63b8c836ed58ce4a0a29ec736a59734" +dependencies = [ + "allocator-api2", + "bumpalo", + "hashbrown", + "log", + "rustc-hash", + "smallvec", +] + +[[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", + "regex-syntax", +] + +[[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", +] + +[[package]] +name = "regex-bench" +version = "0.1.0" +dependencies = [ + "regex", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "regex-test" +version = "0.1.0" +dependencies = [ + "regex", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix-linux-procfs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc84bf7e9aa16c4f2c758f27412dc9841341e16aa682d9c7ac308fe3ee12056" +dependencies = [ + "once_cell", + "rustix 1.0.8", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[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 = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[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_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap 2.34.0", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck 0.3.3", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[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.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-interface" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b858526d22750088a9b3cf2e3c2aacebd5377f13adeec02860c30d09113010a6" +dependencies = [ + "bitflags 2.5.0", + "cap-fs-ext", + "cap-std", + "fd-lock", + "io-lifetimes", + "rustix 0.38.44", + "windows-sys 0.52.0", + "winx", +] + +[[package]] +name = "target-lexicon" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width 0.1.11", +] + +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl 1.0.58", +] + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl 2.0.16", +] + +[[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.98", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[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.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +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 = "tokio" +version = "1.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" +dependencies = [ + "backtrace", + "bytes", + "io-uring", + "libc", + "mio", + "pin-project-lite", + "slab", + "socket2", + "windows-sys 0.52.0", +] + +[[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", +] + +[[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.22.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "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.98", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uap-bench" +version = "0.1.0" +dependencies = [ + "regex", + "serde", + "serde_yaml", +] + +[[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-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[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" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[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 = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "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", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.98", + "wasm-bindgen-shared", +] + +[[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.98", + "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-encoder" +version = "0.202.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfd106365a7f5f7aa3c1916a98cbb3ad477f5ff96ddb130285a91c6e7429e67a" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.236.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "724fccfd4f3c24b7e589d333fc0429c68042897a7e8a5f8694f31792471841e7" +dependencies = [ + "leb128fmt", + "wasmparser 0.236.1", +] + +[[package]] +name = "wasm-encoder" +version = "0.238.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50143b010bdc3adbd16275710f9085cc80d9c12cb869309a51a98ce2ff96558e" +dependencies = [ + "leb128fmt", + "wasmparser 0.238.0", +] + +[[package]] +name = "wasm-smith" +version = "0.202.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf58fe4c46def4c0cdab0818cf0e663db7a018473795349996c48335d5d1163" +dependencies = [ + "anyhow", + "arbitrary", + "flagset", + "indexmap", + "leb128", + "wasm-encoder 0.202.0", +] + +[[package]] +name = "wasmparser" +version = "0.236.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9b1e81f3eb254cf7404a82cee6926a4a3ccc5aad80cc3d43608a070c67aa1d7" +dependencies = [ + "bitflags 2.5.0", + "hashbrown", + "indexmap", + "semver", + "serde", +] + +[[package]] +name = "wasmparser" +version = "0.238.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ad4ca2ecb86b79ea410cd970985665de1d05774b7107b214bc5852b1bcbad7" +dependencies = [ + "bitflags 2.5.0", + "hashbrown", + "indexmap", + "semver", + "serde", +] + +[[package]] +name = "wasmprinter" +version = "0.236.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2df225df06a6df15b46e3f73ca066ff92c2e023670969f7d50ce7d5e695abbb1" +dependencies = [ + "anyhow", + "termcolor", + "wasmparser 0.236.1", +] + +[[package]] +name = "wasmprinter" +version = "0.238.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fec8a560f7288effd1a61fe8d7bfe9fc3efdc2173949d7a5ee38ea9e8eaa336" +dependencies = [ + "anyhow", + "termcolor", + "wasmparser 0.238.0", +] + +[[package]] +name = "wasmtime" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b3e1fab634681494213138ea3a18e958e5ea99da13a4a01a4b870d51a41680b" +dependencies = [ + "addr2line 0.25.0", + "anyhow", + "async-trait", + "bitflags 2.5.0", + "bumpalo", + "cc", + "cfg-if", + "encoding_rs", + "fxprof-processed-profile", + "gimli 0.32.2", + "hashbrown", + "indexmap", + "ittapi", + "libc", + "log", + "mach2", + "memfd", + "object 0.37.3", + "once_cell", + "postcard", + "pulley-interpreter", + "rayon", + "rustix 1.0.8", + "semver", + "serde", + "serde_derive", + "serde_json", + "smallvec", + "target-lexicon", + "wasm-encoder 0.236.1", + "wasmparser 0.236.1", + "wasmtime-environ", + "wasmtime-internal-asm-macros", + "wasmtime-internal-cache", + "wasmtime-internal-component-macro", + "wasmtime-internal-component-util", + "wasmtime-internal-cranelift", + "wasmtime-internal-fiber", + "wasmtime-internal-jit-debug", + "wasmtime-internal-jit-icache-coherence", + "wasmtime-internal-math", + "wasmtime-internal-slab", + "wasmtime-internal-unwinder", + "wasmtime-internal-versioned-export-macros", + "wasmtime-internal-winch", + "wat", + "windows-sys 0.60.2", +] + +[[package]] +name = "wasmtime-environ" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6750e519977953a018fe994aada7e02510aea4babb03310aa5f5b4145b6e6577" +dependencies = [ + "anyhow", + "cpp_demangle", + "cranelift-bitset", + "cranelift-entity", + "gimli 0.32.2", + "indexmap", + "log", + "object 0.37.3", + "postcard", + "rustc-demangle", + "semver", + "serde", + "serde_derive", + "smallvec", + "target-lexicon", + "wasm-encoder 0.236.1", + "wasmparser 0.236.1", + "wasmprinter 0.236.1", + "wasmtime-internal-component-util", +] + +[[package]] +name = "wasmtime-internal-asm-macros" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdbf38adac6e81d5c0326e8fd25f80450e3038f2fc103afd3c5cc8b83d5dd78b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-internal-cache" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0c9085d8c04cc294612d743e2f355382b39250de4bd20bf4b0b0b7c0ae7067a" +dependencies = [ + "anyhow", + "base64", + "directories-next", + "log", + "postcard", + "rustix 1.0.8", + "serde", + "serde_derive", + "sha2", + "toml", + "windows-sys 0.60.2", + "zstd", +] + +[[package]] +name = "wasmtime-internal-component-macro" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26a578a474e3b7ddce063cd169ced292b5185013341457522891b10e989aa42a" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn 2.0.98", + "wasmtime-internal-component-util", + "wasmtime-internal-wit-bindgen", + "wit-parser", +] + +[[package]] +name = "wasmtime-internal-component-util" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc23d46ec1b1cd42b6f73205eb80498ed94b47098ec53456c0b18299405b158" + +[[package]] +name = "wasmtime-internal-cranelift" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d85b8ba128525bff91b89ac8a97755136a4fb0fb59df5ffb7539dd646455d441" +dependencies = [ + "anyhow", + "cfg-if", + "cranelift-codegen", + "cranelift-control", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "gimli 0.32.2", + "itertools 0.14.0", + "log", + "object 0.37.3", + "pulley-interpreter", + "smallvec", + "target-lexicon", + "thiserror 2.0.16", + "wasmparser 0.236.1", + "wasmtime-environ", + "wasmtime-internal-math", + "wasmtime-internal-versioned-export-macros", +] + +[[package]] +name = "wasmtime-internal-fiber" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c566f5137de1f55339df8a236a5ec89698b466a3d33f9cc07823a58a3f85e16" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "libc", + "rustix 1.0.8", + "wasmtime-internal-asm-macros", + "wasmtime-internal-versioned-export-macros", + "windows-sys 0.60.2", +] + +[[package]] +name = "wasmtime-internal-jit-debug" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e03f0b11f8fe4d456feac11e7e9dc6f02ddb34d4f6a1912775dbc63c5bdd5670" +dependencies = [ + "cc", + "object 0.37.3", + "rustix 1.0.8", + "wasmtime-internal-versioned-export-macros", +] + +[[package]] +name = "wasmtime-internal-jit-icache-coherence" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71aeb74f9b3fd9225319c723e59832a77a674b0c899ba9795f9b2130a6d1b167" +dependencies = [ + "anyhow", + "cfg-if", + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "wasmtime-internal-math" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d5dad8a609c6cc47a5f265f13b52e347e893450a69641af082b8a276043fa7" +dependencies = [ + "libm", +] + +[[package]] +name = "wasmtime-internal-slab" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d152a7b875d62e395bfe0ae7d12e7b47cd332eb380353cce3eb831f9843731d" + +[[package]] +name = "wasmtime-internal-unwinder" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aaacc0fea00293f7af7e6c25cef74b7d213ebbe7560c86305eec15fc318fab8" +dependencies = [ + "anyhow", + "cfg-if", + "cranelift-codegen", + "log", + "object 0.37.3", +] + +[[package]] +name = "wasmtime-internal-versioned-export-macros" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61c7f75326434944cc5f3b75409a063fa37e537f6247f00f0f733679f0be406" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "wasmtime-internal-winch" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cfbaa87e1ac4972bb096c9cb1800fedc113e36332cc4bc2c96a2ef1d7c5e750" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli 0.32.2", + "object 0.37.3", + "target-lexicon", + "wasmparser 0.236.1", + "wasmtime-environ", + "wasmtime-internal-cranelift", + "winch-codegen", +] + +[[package]] +name = "wasmtime-internal-wit-bindgen" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "169042d58002f16da149ab7d608b71164411abd1fc5140f48f4c200b44bb5565" +dependencies = [ + "anyhow", + "bitflags 2.5.0", + "heck 0.5.0", + "indexmap", + "wit-parser", +] + +[[package]] +name = "wasmtime-wasi" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9049a5fedcd24fa0f665ba7c17c4445c1a547536a9560d960e15bee2d8428d0" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 2.5.0", + "bytes", + "cap-fs-ext", + "cap-net-ext", + "cap-rand", + "cap-std", + "cap-time-ext", + "fs-set-times", + "futures", + "io-extras", + "io-lifetimes", + "rustix 1.0.8", + "system-interface", + "thiserror 2.0.16", + "tokio", + "tracing", + "url", + "wasmtime", + "wasmtime-wasi-io", + "wiggle", + "windows-sys 0.60.2", +] + +[[package]] +name = "wasmtime-wasi-io" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d62156d8695d80df8e85baeb56379b3ba6b6bf5996671594724c24d40b67825f" +dependencies = [ + "anyhow", + "async-trait", + "bytes", + "futures", + "wasmtime", +] + +[[package]] +name = "wast" +version = "35.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" +dependencies = [ + "leb128", +] + +[[package]] +name = "wast" +version = "238.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c671ea796336ebaa49b963adb14cf13cb98de4e64d69ed4a16ace8c7b4db87b" +dependencies = [ + "bumpalo", + "leb128fmt", + "memchr", + "unicode-width 0.2.0", + "wasm-encoder 0.238.0", +] + +[[package]] +name = "wat" +version = "1.238.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de04a6a9c93aaae4de7bec6323bf11f810457b479f9f877e80d212fd77ffdbc" +dependencies = [ + "wast 238.0.0", +] + +[[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 = "wiggle" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e233166bc0ef02371ebe2c630aba51dd3f015bcaf616d32b4171efab84d09137" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 2.5.0", + "thiserror 2.0.16", + "tracing", + "wasmtime", + "wiggle-macro", +] + +[[package]] +name = "wiggle-generate" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93048543902e61c65b75d8a9ea0e78d5a8723e5db6e11ff93870165807c4463d" +dependencies = [ + "anyhow", + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.98", + "witx", +] + +[[package]] +name = "wiggle-macro" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd7e511edbcaa045079dea564486c4ff7946ae491002227c41d74ea62a59d329" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", + "wiggle-generate", +] + +[[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-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", +] + +[[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 = "winch-codegen" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e615fe205d7d4c9aa62217862f2e0969d00b9b0843af0b1b8181adaea3cfef3" +dependencies = [ + "anyhow", + "cranelift-assembler-x64", + "cranelift-codegen", + "gimli 0.32.2", + "regalloc2", + "smallvec", + "target-lexicon", + "thiserror 2.0.16", + "wasmparser 0.236.1", + "wasmtime-environ", + "wasmtime-internal-cranelift", + "wasmtime-internal-math", +] + +[[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.6", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +dependencies = [ + "memchr", +] + +[[package]] +name = "winx" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9643b83820c0cd246ecabe5fa454dd04ba4fa67996369466d0747472d337346" +dependencies = [ + "bitflags 2.5.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "wit-parser" +version = "0.236.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e4833a20cd6e85d6abfea0e63a399472d6f88c6262957c17f546879a80ba15" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.236.1", +] + +[[package]] +name = "witx" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" +dependencies = [ + "anyhow", + "log", + "thiserror 1.0.58", + "wast 35.0.2", +] + +[[package]] +name = "wizer" +version = "10.0.0" +dependencies = [ + "anyhow", + "cap-std", + "criterion", + "env_logger", + "log", + "rayon", + "structopt", + "wasm-encoder 0.238.0", + "wasmparser 0.238.0", + "wasmprinter 0.238.0", + "wasmtime", + "wasmtime-wasi", + "wat", +] + +[[package]] +name = "wizer-fuzz" +version = "0.0.0" +dependencies = [ + "env_logger", + "libfuzzer-sys", + "log", + "wasm-smith", + "wasmprinter 0.238.0", + "wasmtime", + "wizer", +] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "byteorder", + "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.98", +] + +[[package]] +name = "zstd" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.10+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/crates/wizer/Cargo.toml b/crates/wizer/Cargo.toml new file mode 100644 index 000000000000..2d0836b750b2 --- /dev/null +++ b/crates/wizer/Cargo.toml @@ -0,0 +1,70 @@ +[package] +authors = ["Nick Fitzgerald "] +categories = ["command-line-utilities", "development-tools", "wasm"] +description = "The WebAssembly Pre-Initializer" +documentation = "https://docs.rs/wizer" +edition = "2018" +exclude = ["**.wasm"] +homepage = "https://github.com/bytecodealliance/wizer" +license = "Apache-2.0 WITH LLVM-exception" +name = "wizer" +readme = "./README.md" +repository = "https://github.com/bytecodealliance/wizer" +version = "10.0.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[[bin]] +name = "wizer" +required-features = ["env_logger", "structopt"] + +[[bench]] +name = "regex" +harness = false + +[[bench]] +name = "uap" +harness = false + +[dependencies] +anyhow = "1.0.97" +cap-std = "3.4.3" +env_logger = { version = "0.11.8", optional = true } +log = "0.4.27" +rayon = "1.10.0" +structopt = { version = "0.3.26", optional = true } +wasm-encoder = "0.238.0" +wasmparser = "0.238.0" +wasmtime = { workspace = true } +wasmtime-wasi = { workspace = true, features = ["preview1"] } + +# Enable this dependency to get messages with WAT disassemblies when certain +# internal panics occur. +[dependencies.wasmprinter] +workspace = true +optional = true + +[workspace.dependencies] +wasmprinter = "0.238.0" +wasmtime = "36" +wasmtime-wasi = "36" + +[dev-dependencies] +criterion = "0.5.1" +env_logger = "0.11.8" +wasmprinter = { workspace = true } +wat = "1.238.0" + +[workspace] +members = [ + "benches/regex-bench", + "benches/uap-bench", + "fuzz", + "tests/regex-test", +] + +[profile.bench] +debug = true + +[lints.rust] +unexpected_cfgs = { level = "allow", check-cfg = ['cfg(fuzzing)'] } diff --git a/crates/wizer/LICENSE b/crates/wizer/LICENSE new file mode 100644 index 000000000000..f9d81955f4bc --- /dev/null +++ b/crates/wizer/LICENSE @@ -0,0 +1,220 @@ + + 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. + diff --git a/crates/wizer/ORG_CODE_OF_CONDUCT.md b/crates/wizer/ORG_CODE_OF_CONDUCT.md new file mode 100644 index 000000000000..6f4fb3f537d1 --- /dev/null +++ b/crates/wizer/ORG_CODE_OF_CONDUCT.md @@ -0,0 +1,143 @@ +# Bytecode Alliance Organizational Code of Conduct (OCoC) + +*Note*: this Code of Conduct pertains to organizations' behavior. Please also see the [Individual Code of Conduct](CODE_OF_CONDUCT.md). + +## Preamble + +The Bytecode Alliance (BA) welcomes involvement from organizations, +including commercial organizations. This document is an +*organizational* code of conduct, intended particularly to provide +guidance to commercial organizations. It is distinct from the +[Individual Code of Conduct (ICoC)](CODE_OF_CONDUCT.md), and does not +replace the ICoC. This OCoC applies to any group of people acting in +concert as a BA member or as a participant in BA activities, whether +or not that group is formally incorporated in some jurisdiction. + +The code of conduct described below is not a set of rigid rules, and +we did not write it to encompass every conceivable scenario that might +arise. For example, it is theoretically possible there would be times +when asserting patents is in the best interest of the BA community as +a whole. In such instances, consult with the BA, strive for +consensus, and interpret these rules with an intent that is generous +to the community the BA serves. + +While we may revise these guidelines from time to time based on +real-world experience, overall they are based on a simple principle: + +*Bytecode Alliance members should observe the distinction between + public community functions and private functions — especially + commercial ones — and should ensure that the latter support, or at + least do not harm, the former.* + +## Guidelines + + * **Do not cause confusion about Wasm standards or interoperability.** + + Having an interoperable WebAssembly core is a high priority for + the BA, and members should strive to preserve that core. It is fine + to develop additional non-standard features or APIs, but they + should always be clearly distinguished from the core interoperable + Wasm. + + Treat the WebAssembly name and any BA-associated names with + respect, and follow BA trademark and branding guidelines. If you + distribute a customized version of software originally produced by + the BA, or if you build a product or service using BA-derived + software, use names that clearly distinguish your work from the + original. (You should still provide proper attribution to the + original, of course, wherever such attribution would normally be + given.) + + Further, do not use the WebAssembly name or BA-associated names in + other public namespaces in ways that could cause confusion, e.g., + in company names, names of commercial service offerings, domain + names, publicly-visible social media accounts or online service + accounts, etc. It may sometimes be reasonable, however, to + register such a name in a new namespace and then immediately donate + control of that account to the BA, because that would help the project + maintain its identity. + + For further guidance, see the BA Trademark and Branding Policy + [TODO: create policy, then insert link]. + + * **Do not restrict contributors.** If your company requires + employees or contractors to sign non-compete agreements, those + agreements must not prevent people from participating in the BA or + contributing to related projects. + + This does not mean that all non-compete agreements are incompatible + with this code of conduct. For example, a company may restrict an + employee's ability to solicit the company's customers. However, an + agreement must not block any form of technical or social + participation in BA activities, including but not limited to the + implementation of particular features. + + The accumulation of experience and expertise in individual persons, + who are ultimately free to direct their energy and attention as + they decide, is one of the most important drivers of progress in + open source projects. A company that limits this freedom may hinder + the success of the BA's efforts. + + * **Do not use patents as offensive weapons.** If any BA participant + prevents the adoption or development of BA technologies by + asserting its patents, that undermines the purpose of the + coalition. The collaboration fostered by the BA cannot include + members who act to undermine its work. + + * **Practice responsible disclosure** for security vulnerabilities. + Use designated, non-public reporting channels to disclose technical + vulnerabilities, and give the project a reasonable period to + respond, remediate, and patch. [TODO: optionally include the + security vulnerability reporting URL here.] + + Vulnerability reporters may patch their company's own offerings, as + long as that patching does not significantly delay the reporting of + the vulnerability. Vulnerability information should never be used + for unilateral commercial advantage. Vendors may legitimately + compete on the speed and reliability with which they deploy + security fixes, but withholding vulnerability information damages + everyone in the long run by risking harm to the BA project's + reputation and to the security of all users. + + * **Respect the letter and spirit of open source practice.** While + there is not space to list here all possible aspects of standard + open source practice, some examples will help show what we mean: + + * Abide by all applicable open source license terms. Do not engage + in copyright violation or misattribution of any kind. + + * Do not claim others' ideas or designs as your own. + + * When others engage in publicly visible work (e.g., an upcoming + demo that is coordinated in a public issue tracker), do not + unilaterally announce early releases or early demonstrations of + that work ahead of their schedule in order to secure private + advantage (such as marketplace advantage) for yourself. + + The BA reserves the right to determine what constitutes good open + source practices and to take action as it deems appropriate to + encourage, and if necessary enforce, such practices. + +## Enforcement + +Instances of organizational behavior in violation of the OCoC may +be reported by contacting the Bytecode Alliance CoC team at +[report@bytecodealliance.org](mailto:report@bytecodealliance.org). The +CoC team will review and investigate all complaints, and will respond +in a way that it deems appropriate to the circumstances. The CoC team +is obligated to maintain confidentiality with regard to the reporter of +an incident. Further details of specific enforcement policies may be +posted separately. + +When the BA deems an organization in violation of this OCoC, the BA +will, at its sole discretion, determine what action to take. The BA +will decide what type, degree, and duration of corrective action is +needed, if any, before a violating organization can be considered for +membership (if it was not already a member) or can have its membership +reinstated (if it was a member and the BA canceled its membership due +to the violation). + +In practice, the BA's first approach will be to start a conversation, +with punitive enforcement used only as a last resort. Violations +often turn out to be unintentional and swiftly correctable with all +parties acting in good faith. diff --git a/crates/wizer/README.md b/crates/wizer/README.md new file mode 100644 index 000000000000..dfdcc7969e1f --- /dev/null +++ b/crates/wizer/README.md @@ -0,0 +1,177 @@ +
+

Wizer

+ +

+ The WebAssembly Pre-Initializer! +

+ + A Bytecode Alliance project + +

+ build status + zulip chat + Documentation Status +

+ +

+ API Docs + | + Contributing + | + Chat +

+
+ +* [About](#about) +* [Install](#install) +* [Example Usage](#example-usage) +* [Caveats](#caveats) +* [Using Wizer as a Library](#using-wizer-as-a-library) +* [How Does it Work?](#how-does-it-work) + +## About + +Don't wait for your Wasm module to initialize itself, pre-initialize it! Wizer +instantiates your WebAssembly module, executes its initialization function, and +then snapshots the initialized state out into a new WebAssembly module. Now you +can use this new, pre-initialized WebAssembly module to hit the ground running, +without making your users wait for that first-time set up code to complete. + +The improvements to start up latency you can expect will depend on how much +initialization work your WebAssembly module needs to do before it's ready. Some +initial benchmarking shows between 1.35 to 6.00 times faster instantiation and +initialization with Wizer, depending on the workload: + +| Program | Without Wizer | With Wizer | Speedup | +|------------------------|--------------:|-----------:|-----------------:| +| [`regex`][regex-bench] | 248.85 us | 183.99 us | **1.35x faster** | +| [UAP][uap-bench] | 98.297 ms | 16.385 ms | **6.00x faster** | + +[regex-bench]: https://github.com/bytecodealliance/wizer/tree/main/benches/regex-bench +[uap-bench]: https://github.com/bytecodealliance/wizer/tree/main/benches/uap-bench + +Not every program will see an improvement to instantiation and start up +latency. For example, Wizer will often increase the size of the Wasm module's +`Data` section, which could negatively impact network transfer times on the +Web. However, the best way to find out if your Wasm module will see an +improvement is to try it out! Adding an initialization function isn't too hard. + +Finally, you can likely see further improvements by running +[`wasm-opt`][binaryen] on the pre-initialized module. Beyond the usual benefits +that `wasm-opt` brings, the module likely has a bunch of initialization-only +code that is no longer needed now that the module is already initialized, and +which `wasm-opt` can remove. + +[binaryen]: https://github.com/WebAssembly/binaryen + +## Install + +Download the a pre-built release from the +[releases](https://github.com/bytecodealliance/wizer/releases) page. Unarchive +the binary and place it in your `$PATH`. + +Alternatively you can install via `cargo`: + +```shell-session +cargo install wizer --all-features +``` + +## Example Usage + +First, make sure your Wasm module exports an initialization function named +`wizer.initialize`. For example, in Rust you can export it like this: + +```rust +#[export_name = "wizer.initialize"] +pub extern "C" fn init() { + // Your initialization code goes here... +} +``` + +For a complete C++ example, see [this](https://github.com/bytecodealliance/wizer/tree/main/examples/cpp). + +Then, if your Wasm module is named `input.wasm`, run the `wizer` CLI: + +```shell-session +wizer input.wasm -o initialized.wasm +``` + +Now you have a pre-initialized version of your Wasm module at +`initialized.wasm`! + +More details, flags, and options can be found via `--help`: + +```shell-session +wizer --help +``` + +## Caveats + +* The initialization function may not call any imported functions. Doing so will + trigger a trap and `wizer` will exit. You can, however, allow WASI calls via + the `--allow-wasi` flag. + +* The Wasm module may not import globals, tables, or memories. + +* Reference types are not supported yet. It isn't 100% clear yet what the best + approach to snapshotting `externref` tables is. + +## Using Wizer as a Library + +Add a dependency in your `Cargo.toml`: + +```toml +# Cargo.toml + +[dependencies] +wizer = "9" +``` + +And then use the `wizer::Wizer` builder to configure and run Wizer: + +```rust +use wizer::Wizer; + +let input_wasm = get_input_wasm_bytes(); + +let initialized_wasm_bytes = Wizer::new() + .allow_wasi(true)? + .run(&input_wasm)?; +``` + +## Using Wizer with a custom Linker + +If you want your module to be able to import other modules during instantiation, you can +use the `.make_linker(...)` builder method to provide your own Linker, for example: + +```rust +use wizer::Wizer; + +let input_wasm = get_input_wasm_bytes(); +let initialized_wasm_bytes = Wizer::new() + .make_linker(Some(Rc::new(|e: &wasmtime::Engine| { + let mut linker = wasmtime::Linker::new(e); + linker.func_wrap("foo", "bar", |x: i32| x + 1)?; + Ok(linker) + }))) + .run(&input_wasm)?; +``` + +Note that `allow_wasi(true)` and a custom linker are currently mutually exclusive + +## How Does it Work? + +First we instantiate the input Wasm module with Wasmtime and run the +initialization function. Then we record the Wasm instance's state: + +* What are the values of its globals? +* What regions of memory are non-zero? + +Then we rewrite the Wasm binary by intializing its globals directly to their +recorded state, and removing the module's old data segments and replacing them +with data segments for each of the non-zero regions of memory we recorded. + +Want some more details? Check out the talk ["Hit the Ground Running: Wasm +Snapshots for Fast Start +Up"](https://fitzgeraldnick.com/2021/05/10/wasm-summit-2021.html) from the 2021 +WebAssembly Summit. diff --git a/crates/wizer/benches/regex-bench/Cargo.toml b/crates/wizer/benches/regex-bench/Cargo.toml new file mode 100644 index 000000000000..784d98f573c2 --- /dev/null +++ b/crates/wizer/benches/regex-bench/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "regex-bench" +version = "0.1.0" +authors = ["Nick Fitzgerald "] +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +regex = "1.10.4" + +[features] +wizer = [] diff --git a/crates/wizer/benches/regex-bench/README.md b/crates/wizer/benches/regex-bench/README.md new file mode 100644 index 000000000000..d6838d45079e --- /dev/null +++ b/crates/wizer/benches/regex-bench/README.md @@ -0,0 +1,10 @@ +Source code used to create `/wizer/benches/regex_bench.{control,wizer}.wasm`. + +Compiles a simple regular expression in the initialization function and the main +function checks whether the regex matches an input string. + +Rebuild via: + +``` +$ ./build.sh +``` diff --git a/crates/wizer/benches/regex-bench/build.sh b/crates/wizer/benches/regex-bench/build.sh new file mode 100755 index 000000000000..4e27374f057a --- /dev/null +++ b/crates/wizer/benches/regex-bench/build.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -ex +cd "$(dirname $0)" +cargo build --target wasm32-wasi --release +cp ../../target/wasm32-wasi/release/regex_bench.wasm ../regex_bench.control.wasm +cargo build --target wasm32-wasi --release --features wizer +cd ../.. +cargo run --all-features -- --allow-wasi target/wasm32-wasi/release/regex_bench.wasm -o benches/regex_bench.wizer.wasm diff --git a/crates/wizer/benches/regex-bench/src/lib.rs b/crates/wizer/benches/regex-bench/src/lib.rs new file mode 100644 index 000000000000..cef08b3bd1ca --- /dev/null +++ b/crates/wizer/benches/regex-bench/src/lib.rs @@ -0,0 +1,24 @@ +use regex::Regex; + +/// A regex that matches numbers that start with "1". +static mut REGEX: Option = None; + +#[export_name = "wizer.initialize"] +pub extern "C" fn init() { + unsafe { + REGEX = Some(Regex::new(r"^1\d*$").unwrap()); + } +} + +#[export_name = "run"] +pub extern "C" fn run(ptr: *mut u8, len: usize) -> i32 { + #[cfg(not(feature = "wizer"))] + init(); + + let s = unsafe { + let slice = std::slice::from_raw_parts(ptr, len); + std::str::from_utf8(slice).unwrap() + }; + let regex = unsafe { REGEX.as_ref().unwrap() }; + regex.is_match(&s) as u8 as i32 +} diff --git a/crates/wizer/benches/regex.rs b/crates/wizer/benches/regex.rs new file mode 100644 index 000000000000..79ceb8350bfe --- /dev/null +++ b/crates/wizer/benches/regex.rs @@ -0,0 +1,72 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use std::convert::TryFrom; +use wizer::StoreData; + +fn run_iter( + linker: &wasmtime::Linker, + module: &wasmtime::Module, + mut store: &mut wasmtime::Store, +) { + let instance = linker.instantiate(&mut store, module).unwrap(); + + let memory = instance.get_memory(&mut store, "memory").unwrap(); + let data = memory.data_mut(&mut store); + let ptr = data.len() - 5; + data[ptr..].copy_from_slice(b"hello"); + + let run = instance + .get_typed_func::<(i32, i32), i32>(&mut store, "run") + .unwrap(); + let result = run + .call(&mut store, (i32::try_from(ptr).unwrap(), 5)) + .unwrap(); + assert_eq!(result, 0); +} + +fn bench_regex(c: &mut Criterion) { + let mut group = c.benchmark_group("regex"); + group.bench_function("control", |b| { + let engine = wasmtime::Engine::default(); + let wasi = wasmtime_wasi::WasiCtxBuilder::new().build_p1(); + let mut store = wasmtime::Store::new( + &engine, + StoreData { + wasi_ctx: Some(wasi), + }, + ); + let module = + wasmtime::Module::new(store.engine(), &include_bytes!("regex_bench.control.wasm")) + .unwrap(); + let mut linker = wasmtime::Linker::new(&engine); + wasmtime_wasi::preview1::add_to_linker_sync(&mut linker, |s: &mut StoreData| { + s.wasi_ctx.as_mut().unwrap() + }) + .unwrap(); + + b.iter(|| run_iter(&linker, &module, &mut store)); + }); + group.bench_function("wizer", |b| { + let engine = wasmtime::Engine::default(); + let wasi = wasmtime_wasi::WasiCtxBuilder::new().build_p1(); + let mut store = wasmtime::Store::new( + &engine, + StoreData { + wasi_ctx: Some(wasi), + }, + ); + let module = + wasmtime::Module::new(store.engine(), &include_bytes!("regex_bench.control.wasm")) + .unwrap(); + let mut linker = wasmtime::Linker::new(&engine); + wasmtime_wasi::preview1::add_to_linker_sync(&mut linker, |s: &mut StoreData| { + s.wasi_ctx.as_mut().unwrap() + }) + .unwrap(); + + b.iter(|| run_iter(&linker, &module, &mut store)); + }); + group.finish(); +} + +criterion_group!(benches, bench_regex); +criterion_main!(benches); diff --git a/crates/wizer/benches/uap-bench/Cargo.toml b/crates/wizer/benches/uap-bench/Cargo.toml new file mode 100644 index 000000000000..b57373bf1a9f --- /dev/null +++ b/crates/wizer/benches/uap-bench/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "uap-bench" +version = "0.1.0" +authors = ["Nick Fitzgerald "] +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { version = "1.0.197", features = ["derive"] } +regex = "1.10.4" +serde_yaml = "0.9.34" + +[features] +wizer = [] diff --git a/crates/wizer/benches/uap-bench/README.md b/crates/wizer/benches/uap-bench/README.md new file mode 100644 index 000000000000..d5b0f25ef275 --- /dev/null +++ b/crates/wizer/benches/uap-bench/README.md @@ -0,0 +1,11 @@ +Source code used to create `/wizer/benches/uap_bench.{control,wizer}.wasm`. + +Creates a `RegexSet` from the user agent parsing regexes from the browserscope +project in the initialization function and then tests whether the input string +is a known user agent. + +Rebuild via: + +``` +$ ./build.sh +``` diff --git a/crates/wizer/benches/uap-bench/build.sh b/crates/wizer/benches/uap-bench/build.sh new file mode 100755 index 000000000000..0327f62ce014 --- /dev/null +++ b/crates/wizer/benches/uap-bench/build.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -ex +cd "$(dirname $0)" +cargo build --target wasm32-wasi --release +cp ../../target/wasm32-wasi/release/uap_bench.wasm ../uap_bench.control.wasm +cargo build --target wasm32-wasi --release --features wizer +cd ../.. +cargo run --all-features -- --allow-wasi target/wasm32-wasi/release/uap_bench.wasm -o benches/uap_bench.wizer.wasm diff --git a/crates/wizer/benches/uap-bench/src/lib.rs b/crates/wizer/benches/uap-bench/src/lib.rs new file mode 100644 index 000000000000..00bed5d37e7e --- /dev/null +++ b/crates/wizer/benches/uap-bench/src/lib.rs @@ -0,0 +1,67 @@ +use regex::RegexSet; +use serde::Deserialize; + +static mut UA_REGEX_SET: Option = None; + +#[derive(Deserialize)] +struct UserAgentParsers { + user_agent_parsers: Vec, +} + +#[derive(Deserialize)] +struct UserAgentParserEntry { + regex: String, + // family_replacement: Option, + // brand_replacement: Option, + // model_replacement: Option, + // os_replacement: Option, + // v1_replacement: Option, + // v2_replacement: Option, + // os_v1_replacement: Option, + // os_v2_replacement: Option, + // os_v3_replacement: Option, +} + +#[export_name = "wizer.initialize"] +pub extern "C" fn init() { + let uap_yaml = include_str!("../uap-core/regexes.yaml"); + let parsers: UserAgentParsers = serde_yaml::from_str(uap_yaml).unwrap(); + let regex_set = RegexSet::new( + parsers + .user_agent_parsers + .iter() + .map(|e| e.regex.replace("\\/", "/").replace("\\!", "!")), + ) + .unwrap(); + unsafe { + assert!(UA_REGEX_SET.is_none()); + UA_REGEX_SET = Some(regex_set); + } +} + +#[export_name = "run"] +pub extern "C" fn run(ptr: *mut u8, len: usize) -> i32 { + #[cfg(not(feature = "wizer"))] + init(); + + let s = unsafe { + let slice = std::slice::from_raw_parts(ptr, len); + std::str::from_utf8(slice).unwrap() + }; + let regex_set = unsafe { UA_REGEX_SET.as_ref().unwrap() }; + regex_set.is_match(&s) as u8 as i32 +} + +#[export_name = "alloc"] +pub extern "C" fn alloc(size: usize, align: usize) -> *mut u8 { + let layout = std::alloc::Layout::from_size_align(size, align).unwrap(); + unsafe { std::alloc::alloc(layout) } +} + +#[export_name = "dealloc"] +pub extern "C" fn dealloc(ptr: *mut u8, size: usize, align: usize) { + let layout = std::alloc::Layout::from_size_align(size, align).unwrap(); + unsafe { + std::alloc::dealloc(ptr, layout); + } +} diff --git a/crates/wizer/benches/uap.rs b/crates/wizer/benches/uap.rs new file mode 100644 index 000000000000..beafc0d91eec --- /dev/null +++ b/crates/wizer/benches/uap.rs @@ -0,0 +1,84 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use std::convert::TryFrom; +use wizer::StoreData; + +fn run_iter( + linker: &wasmtime::Linker, + module: &wasmtime::Module, + mut store: &mut wasmtime::Store, +) { + let instance = linker.instantiate(&mut store, module).unwrap(); + + let ua = "Mozilla/5.0 (X11; Linux x86_64; rv:85.0) Gecko/20100101 Firefox/85.0"; + + let alloc = instance + .get_typed_func::<(u32, u32), u32>(&mut store, "alloc") + .unwrap(); + let ptr = alloc.call(&mut store, (ua.len() as u32, 1)).unwrap() as usize; + + let memory = instance.get_memory(&mut store, "memory").unwrap(); + let data = memory.data_mut(&mut store); + data[ptr..ptr + ua.len()].copy_from_slice(ua.as_bytes()); + + let run = instance + .get_typed_func::<(i32, i32), i32>(&mut store, "run") + .unwrap(); + let result = run + .call(&mut store, (i32::try_from(ptr).unwrap(), 5)) + .unwrap(); + assert_eq!(result, 0); + + let dealloc = instance + .get_typed_func::<(u32, u32, u32), ()>(&mut store, "dealloc") + .unwrap(); + dealloc + .call(&mut store, (ptr as u32, ua.len() as u32, 1)) + .unwrap(); +} + +fn bench_uap(c: &mut Criterion) { + let mut group = c.benchmark_group("uap"); + group.bench_function("control", |b| { + let engine = wasmtime::Engine::default(); + let wasi = wasmtime_wasi::WasiCtxBuilder::new().build_p1(); + let mut store = wasmtime::Store::new( + &engine, + StoreData { + wasi_ctx: Some(wasi), + }, + ); + let module = + wasmtime::Module::new(store.engine(), &include_bytes!("uap_bench.control.wasm")) + .unwrap(); + let mut linker = wasmtime::Linker::new(&engine); + wasmtime_wasi::preview1::add_to_linker_sync(&mut linker, |s: &mut StoreData| { + s.wasi_ctx.as_mut().unwrap() + }) + .unwrap(); + + b.iter(|| run_iter(&linker, &module, &mut store)); + }); + group.bench_function("wizer", |b| { + let engine = wasmtime::Engine::default(); + let wasi = wasmtime_wasi::WasiCtxBuilder::new().build_p1(); + let mut store = wasmtime::Store::new( + &engine, + StoreData { + wasi_ctx: Some(wasi), + }, + ); + let module = + wasmtime::Module::new(store.engine(), &include_bytes!("uap_bench.wizer.wasm")).unwrap(); + let mut linker = wasmtime::Linker::new(&engine); + wasmtime_wasi::preview1::add_to_linker_sync(&mut linker, |s: &mut StoreData| { + s.wasi_ctx.as_mut().unwrap() + }) + .unwrap(); + + b.iter(|| run_iter(&linker, &module, &mut store)); + }); + group.finish(); +} + +criterion_group!(benches, bench_uap); +criterion_main!(benches); diff --git a/crates/wizer/ci/build-tarballs.sh b/crates/wizer/ci/build-tarballs.sh new file mode 100755 index 000000000000..4e316407f4c8 --- /dev/null +++ b/crates/wizer/ci/build-tarballs.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# A small script used for assembling release tarballs for the `wizer` +# binary. This is executed with two arguments, mostly coming from +# the CI matrix. +# +# The first argument is the name of the platform, used to name the release +# The second argument is the "target", if present, currently only for +# cross-compiles +# +# This expects the build to already be done and will assemble release artifacts +# in `dist/` + +set -ex + +platform=$1 +target=$2 + +rm -rf tmp +mkdir tmp +mkdir -p dist + +tag=dev +if [[ $GITHUB_REF == refs/tags/v* ]]; then + tag=${GITHUB_REF:10} +fi + +bin_pkgname=wizer-$tag-$platform + +mkdir tmp/$bin_pkgname +cp LICENSE README.md tmp/$bin_pkgname + +fmt=tar +if [ "$platform" = "x86_64-windows" ]; then + cp target/release/wizer.exe tmp/$bin_pkgname + fmt=zip +elif [ "$platform" = "x86_64-mingw" ]; then + cp target/x86_64-pc-windows-gnu/release/wizer.exe tmp/$bin_pkgname + fmt=zip +elif [ "$target" = "" ]; then + cp target/release/wizer tmp/$bin_pkgname +else + cp target/$target/release/wizer tmp/$bin_pkgname +fi + + +mktarball() { + dir=$1 + if [ "$fmt" = "tar" ]; then + # this is a bit wonky, but the goal is to use `xz` with threaded compression + # to ideally get better performance with the `-T0` flag. + tar -cvf - -C tmp $dir | xz -9 -T0 > dist/$dir.tar.xz + else + # Note that this runs on Windows, and it looks like GitHub Actions doesn't + # have a `zip` tool there, so we use something else + (cd tmp && 7z a ../dist/$dir.zip $dir/) + fi +} + +mktarball $bin_pkgname \ No newline at end of file diff --git a/crates/wizer/ci/docker/aarch64-linux/Dockerfile b/crates/wizer/ci/docker/aarch64-linux/Dockerfile new file mode 100644 index 000000000000..738688bedcdb --- /dev/null +++ b/crates/wizer/ci/docker/aarch64-linux/Dockerfile @@ -0,0 +1,7 @@ +FROM ubuntu:16.04 + +RUN apt-get update -y && apt-get install -y gcc gcc-aarch64-linux-gnu ca-certificates + +ENV PATH=$PATH:/rust/bin +ENV CARGO_BUILD_TARGET=aarch64-unknown-linux-gnu +ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \ No newline at end of file diff --git a/crates/wizer/ci/docker/s390x-linux/Dockerfile b/crates/wizer/ci/docker/s390x-linux/Dockerfile new file mode 100644 index 000000000000..f25df1b5d6bb --- /dev/null +++ b/crates/wizer/ci/docker/s390x-linux/Dockerfile @@ -0,0 +1,7 @@ +FROM ubuntu:16.04 + +RUN apt-get update -y && apt-get install -y gcc gcc-s390x-linux-gnu ca-certificates + +ENV PATH=$PATH:/rust/bin +ENV CARGO_BUILD_TARGET=s390x-unknown-linux-gnu +ENV CARGO_TARGET_S390X_UNKNOWN_LINUX_GNU_LINKER=s390x-linux-gnu-gcc \ No newline at end of file diff --git a/crates/wizer/ci/docker/x86_64-linux/Dockerfile b/crates/wizer/ci/docker/x86_64-linux/Dockerfile new file mode 100644 index 000000000000..b88812c79602 --- /dev/null +++ b/crates/wizer/ci/docker/x86_64-linux/Dockerfile @@ -0,0 +1,5 @@ +FROM almalinux:8 + +RUN dnf install -y git gcc make + +ENV PATH=$PATH:/rust/bin diff --git a/crates/wizer/examples/.gitignore b/crates/wizer/examples/.gitignore new file mode 100644 index 000000000000..19e1bced9ad8 --- /dev/null +++ b/crates/wizer/examples/.gitignore @@ -0,0 +1 @@ +*.wasm diff --git a/crates/wizer/examples/cpp/Makefile b/crates/wizer/examples/cpp/Makefile new file mode 100644 index 000000000000..41f5df1a9d10 --- /dev/null +++ b/crates/wizer/examples/cpp/Makefile @@ -0,0 +1,21 @@ +CXX := /opt/wasi-sdk/bin/clang++ +CXXFLAGS := -O2 -I ../../include/ +WIZER := ../../target/release/wizer +WASMTIME ?= wasmtime + +.PHONY: all +all: main_initialized.wasm + +main.wasm: main.cpp + $(CXX) $(CXXFLAGS) -o $@ $^ + +main_initialized.wasm: main.wasm + $(WIZER) --allow-wasi --wasm-bulk-memory=true -r _start=wizer.resume -o $@ $^ + +.PHONY: test +test: main_initialized.wasm + $(WASMTIME) run $^ + +.PHONY: clean +clean: + rm -f *.wasm diff --git a/crates/wizer/examples/cpp/main.cpp b/crates/wizer/examples/cpp/main.cpp new file mode 100644 index 000000000000..86d1c00072e3 --- /dev/null +++ b/crates/wizer/examples/cpp/main.cpp @@ -0,0 +1,32 @@ +#include +#include + +class Test { +public: + Test() : value(1) { + printf("global constructor (should be the first printed line)\n"); + } + ~Test() { printf("global destructor (should be the last printed line)\n"); } + int value; +}; + +bool initialized = false; +int orig_value = 0; +Test t; + +static void init_func() { + // This should run after the ctor for `t`, and before `main`. + orig_value = t.value; + t.value = 2; + initialized = true; +} + +WIZER_INIT(init_func); + +int main(int argc, char **argv) { + if (!initialized) + init_func(); + printf("argc (should not be baked into snapshot): %d\n", argc); + printf("orig_value (should be 1): %d\n", orig_value); + printf("t.value (should be 2): %d\n", t.value); +} diff --git a/crates/wizer/fuzz/.gitignore b/crates/wizer/fuzz/.gitignore new file mode 100644 index 000000000000..62f1add3f688 --- /dev/null +++ b/crates/wizer/fuzz/.gitignore @@ -0,0 +1,5 @@ +test.wasm +test.wat +target +corpus +artifacts diff --git a/crates/wizer/fuzz/Cargo.toml b/crates/wizer/fuzz/Cargo.toml new file mode 100644 index 000000000000..537f7428cd6e --- /dev/null +++ b/crates/wizer/fuzz/Cargo.toml @@ -0,0 +1,27 @@ + +[package] +name = "wizer-fuzz" +version = "0.0.0" +authors = ["Automatically generated"] +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +env_logger = "0.11.3" +libfuzzer-sys = "0.4" +log = "0.4.21" +wasm-smith = "0.202.0" +wasmprinter = { workspace = true } +wasmtime = { workspace = true } + +[dependencies.wizer] +path = ".." + +[[bin]] +name = "same_result" +path = "fuzz_targets/same_result.rs" +test = false +doc = false diff --git a/crates/wizer/fuzz/fuzz_targets/same_result.rs b/crates/wizer/fuzz/fuzz_targets/same_result.rs new file mode 100644 index 000000000000..f4b9bb09f3e6 --- /dev/null +++ b/crates/wizer/fuzz/fuzz_targets/same_result.rs @@ -0,0 +1,230 @@ +//! Check that we get the same result whether we +//! +//! 1. Call the initialization function +//! 2. Call the main function +//! +//! or +//! +//! 1. Call the initialization function +//! 2. Snapshot with Wizer +//! 3. Instantiate the snapshot +//! 4. Call the instantiated snapshot's main function +//! +//! When checking that we get the same result, we don't just consider the main +//! function's results: we also consider memories and globals. + +#![no_main] + +use libfuzzer_sys::{ + arbitrary::{Arbitrary, Unstructured}, + fuzz_target, +}; +use wasm_smith::MemoryOffsetChoices; +use wasmtime::*; + +const FUEL: u32 = 1_000; + +fuzz_target!(|data: &[u8]| { + let _ = env_logger::try_init(); + + let mut u = Unstructured::new(data); + + let mut config = wasm_smith::Config::arbitrary(&mut u).unwrap(); + config.max_memories = 10; + + // We want small memories that are quick to compare, but we also want to + // allow memories to grow so we can shake out any memory-growth-related + // bugs, so we choose `2` instead of `1`. + config.max_memory32_pages = 2; + config.max_memory64_pages = 2; + + // Always generate at least one function that we can hopefully use as an + // initialization function. + config.min_funcs = 1; + + config.max_funcs = 10; + + // Always at least one export, hopefully a function we can use as an + // initialization routine. + config.min_exports = 1; + + config.max_exports = 10; + + // Always use an offset immediate that is within the memory's minimum + // size. This should make trapping on loads/stores a little less + // frequent. + config.memory_offset_choices = MemoryOffsetChoices(1, 0, 0); + + config.reference_types_enabled = false; + config.bulk_memory_enabled = false; + + let Ok(mut module) = wasm_smith::Module::new(config, &mut u) else { + return; + }; + module.ensure_termination(FUEL).unwrap(); + let wasm = module.to_bytes(); + + if log::log_enabled!(log::Level::Debug) { + log::debug!("Writing test case to `test.wasm`"); + std::fs::write("test.wasm", &wasm).unwrap(); + if let Ok(wat) = wasmprinter::print_bytes(&wasm) { + log::debug!("Writing disassembly to `test.wat`"); + std::fs::write("test.wat", wat).unwrap(); + } + } + + let mut config = Config::new(); + wasmtime::Cache::from_file(None) + .map(|cache| config.cache(Some(cache))) + .unwrap(); + config.wasm_multi_memory(true); + config.wasm_multi_value(true); + + let engine = Engine::new(&config).unwrap(); + let module = Module::new(&engine, &wasm).unwrap(); + if module.imports().len() > 0 { + // Not using the `WasmConfig` for this because we want to encourage + // imports/exports between modules within the bundle, just not at the + // top level. + return; + } + + let mut main_funcs = vec![]; + let mut init_funcs = vec![]; + for exp in module.exports() { + if let ExternType::Func(ty) = exp.ty() { + main_funcs.push(exp.name()); + if ty.params().len() == 0 && ty.results().len() == 0 { + init_funcs.push(exp.name()); + } + } + } + + 'init_loop: for init_func in init_funcs { + log::debug!("Using initialization function: {:?}", init_func); + + // Create a wizened snapshot of the given Wasm using `init_func` as the + // initialization routine. + let mut wizer = wizer::Wizer::new(); + wizer + .wasm_multi_memory(true) + .wasm_multi_value(true) + .init_func(init_func); + let snapshot_wasm = match wizer.run(&wasm) { + Err(_) => continue 'init_loop, + Ok(s) => s, + }; + let snapshot_module = + Module::new(&engine, &snapshot_wasm).expect("snapshot should be valid wasm"); + + // Now check that each "main" function behaves the same whether we call + // it on an instantiated snapshot or if we instantiate the original + // Wasm, call the initialization routine, and then call the "main" + // function. + 'main_loop: for main_func in &main_funcs { + if *main_func == init_func { + // Wizer un-exports the initialization function, so we can't use + // it as a main function. + continue 'main_loop; + } + log::debug!("Using main function: {:?}", main_func); + + let mut store = Store::new(&engine, ()); + + // Instantiate the snapshot and call the main function. + let snapshot_instance = Instance::new(&mut store, &snapshot_module, &[]).unwrap(); + let snapshot_main_func = snapshot_instance.get_func(&mut store, main_func).unwrap(); + let main_args = + wizer::dummy::dummy_values(snapshot_main_func.ty(&store).params()).unwrap(); + let mut snapshot_result = + vec![wasmtime::Val::I32(0); snapshot_main_func.ty(&store).results().len()]; + let snapshot_call_result = + snapshot_main_func.call(&mut store, &main_args, &mut snapshot_result); + + // Instantiate the original Wasm and then call the initialization + // and main functions back to back. + let instance = Instance::new(&mut store, &module, &[]).unwrap(); + let init_func = instance + .get_typed_func::<(), ()>(&mut store, init_func) + .unwrap(); + init_func.call(&mut store, ()).unwrap(); + let main_func = instance.get_func(&mut store, main_func).unwrap(); + let mut result = vec![wasmtime::Val::I32(0); main_func.ty(&store).results().len()]; + let call_result = main_func.call(&mut store, &main_args, &mut result); + + // Check that the function return values / traps are the same. + match (snapshot_call_result, call_result) { + // Both did not trap. + (Ok(()), Ok(())) => { + assert_eq!(snapshot_result.len(), result.len()); + for (s, r) in snapshot_result.iter().zip(result.iter()) { + assert_val_eq(s, r); + } + } + + // Both trapped. + (Err(_), Err(_)) => {} + + // Divergence. + (s, r) => { + panic!( + "divergence between whether the main function traps or not!\n\n\ + no snapshotting result = {:?}\n\n\ + snapshotted result = {:?}", + r, s, + ); + } + } + + // Assert that all other exports have the same state as well. + let exports = snapshot_instance + .exports(&mut store) + .map(|export| export.name().to_string()) + .collect::>(); + for name in exports.iter() { + let export = snapshot_instance.get_export(&mut store, &name).unwrap(); + match export { + Extern::Global(snapshot_global) => { + let global = instance.get_global(&mut store, &name).unwrap(); + assert_val_eq(&snapshot_global.get(&mut store), &global.get(&mut store)); + } + Extern::Memory(snapshot_memory) => { + let memory = instance.get_memory(&mut store, &name).unwrap(); + let snapshot_memory = snapshot_memory.data(&store); + let memory = memory.data(&store); + assert_eq!(snapshot_memory.len(), memory.len()); + // NB: Don't use `assert_eq` here so that we don't + // try to print the full memories' debug + // representations on failure. + if snapshot_memory != memory { + panic!("divergence between snapshot and non-snapshot memories"); + } + } + Extern::SharedMemory(_) + | Extern::Func(_) + | Extern::Table(_) + | Extern::Tag(_) => continue, + } + } + } + } +}); + +fn assert_val_eq(a: &Val, b: &Val) { + match (a, b) { + (Val::I32(a), Val::I32(b)) => assert_eq!(a, b), + (Val::I64(a), Val::I64(b)) => assert_eq!(a, b), + (Val::F32(a), Val::F32(b)) => assert!({ + let a = f32::from_bits(*a); + let b = f32::from_bits(*b); + a == b || (a.is_nan() && b.is_nan()) + }), + (Val::F64(a), Val::F64(b)) => assert!({ + let a = f64::from_bits(*a); + let b = f64::from_bits(*b); + a == b || (a.is_nan() && b.is_nan()) + }), + (Val::V128(a), Val::V128(b)) => assert_eq!(a, b), + _ => panic!("{:?} != {:?}", a, b), + } +} diff --git a/crates/wizer/include/wizer.h b/crates/wizer/include/wizer.h new file mode 100644 index 000000000000..b138a5e27b4c --- /dev/null +++ b/crates/wizer/include/wizer.h @@ -0,0 +1,133 @@ +/* + * Wizer interface for Wasm module to be initialized. + * + * This header provides several macros that allow a Wasm module written in C/C++ + * to declare an initializer function, and ensure that global constructors (in + * C++'s case) are run at initialization time rather than on startup of the + * pre-initialized module. + */ +#ifndef _WIZER_H_ +#define _WIZER_H_ + +#ifdef __cplusplus +#define __WIZER_EXTERN_C extern "C" +#else +#define __WIZER_EXTERN_C extern +#endif + +#ifdef __clang_major__ +// wasi-sdk-16 was the first wasi-sdk version that shipped with a version of +// wasi-libc that did not include __original_main. However, wasi-sdk-15 shipped +// with clang-14.0.0. To correctly identify the boundary where __original_main +// no longer exists, we check for either clang-15+ or specifically clang-14.0.4. +// +// wasi-sdk-17 ships with clang-15.0.6 +// wasi-sdk-16 ships with clang-14.0.4 +#if __clang_major__ >= 15 || \ + (__clang_major__ == 14 && __clang_patchlevel__ == 4) +#define WIZER_MAIN_VOID __main_void +#else +#define WIZER_MAIN_VOID __original_main +#endif +#endif + +// We default to assuming that the compiler is new enough to provide +// __main_void. +#ifndef WIZER_MAIN_VOID +#define WIZER_MAIN_VOID __main_void +#endif + +/* + * This macro inserts the exported functions necessary to allow Wizer to + * pre-initialize a Wasm module. + * + * To use, simply invoke the macro in exactly one compilation unit (C/C++ file) + * that is compiled into the Wasm module to be pre-initialized: + * + * static void my_init_function() { ... } + * + * WIZER_INIT(my_init_function); + * + * (The macro refers to the provided init function, so it must have been defined + * or must have a forward declaration at the point the macro is used.) + * + * The resulting module should be processed by Wizer as follows: + * + * $ wizer -r _start=wizer.resume -o out.wasm in.wasm + * + * The result of this will be the following behavior: + * + * - If the `in.wasm` (the direct compilation output of a program including this + * macro invocation) is run directly according to the WASI ABI (i.e., by + * invoking `_start`), then nothing changes: global constructors are run, + * `main()` is invoked, then global destructors are run. The initialization + * function is *not* run in this case. + * + * - During pre-initialization (i.e., during this `wizer` invocation), global + * constructors will run, and then the provided initialization function will + * run. The module's memory and global-variable state is then snapshotted and + * saved into `out.wasm`. + * + * All other Wizer restrictions apply (see Wizer documentation for details): + * for example, WASI hostcalls may be blocked, depending on options, and + * invoking any other imported function will result in an immediate trap + * and failure of the Wizer run. + * + * - If the resulting `out.wasm` is then run using the WASI ABI, the program's + * global constructors are *not* re-run. Instead, execution starts directly at + * `main()`, using the heap and global-variable state left by the global + * constructor and initialization function execution during the Wizer + * invocation. + * + * If no initialization function is needed (i.e., only C++ global constructors + * should be run), use `WIZER_DEFAULT_INIT()` instead. + */ +#define WIZER_INIT(init_func) \ + __WIZER_EXTERN_C void __wasm_call_ctors(); \ + __WIZER_EXTERN_C void __wasm_call_dtors(); \ + __WIZER_EXTERN_C void __wasi_proc_exit(int); \ + __WIZER_EXTERN_C int WIZER_MAIN_VOID(); \ + /* This function's export name `wizer.initialize` is specially */ \ + /* recognized by Wizer. It is the direct entry point for pre-init. */ \ + __attribute__((export_name("wizer.initialize"))) void __wizer_initialize() { \ + /* `__wasm_call_ctors()` is generated by `wasm-ld` and invokes all */ \ + /* of the global constructors. It is safe (and in fact necessary) */ \ + /* to manually invoke it here because `wizer.initialize` is the */ \ + /* direct entry point, and no libc startup (crt1.o or equivalent) */ \ + /* is executed before this code does. */ \ + __wasm_call_ctors(); \ + /* We now invoke the provided init function before returning. */ \ + init_func(); \ + } \ + /* This function replaces `_start` (the WASI-specified entry point) in */ \ + /* the pre-initialized Wasm module. */ \ + __attribute__((export_name("wizer.resume"))) void __wizer_resume() { \ + /* `__main_void()` is defined by the WASI SDK toolchain due to */ \ + /* special semantics in C/C++ for the `main()` function, i.e., ito */ \ + /* can either take argc/argv or not. It collects arguments using */ \ + /* the appropriate WASI calls and then invokes the user program's */ \ + /* `main()`. This may change in the future; when it does, we will */ \ + /* coordinate with the WASI-SDK toolchain to implement this entry */ \ + /* point in an alternate way. */ \ + int r = WIZER_MAIN_VOID(); \ + /* Because we are replacing `_start()`, we need to manually invoke */ \ + /* destructors as well. */ \ + __wasm_call_dtors(); \ + /* If main returned non-zero code, call `__wasi_proc_exit`. */ \ + if (r != 0) { \ + __wasi_proc_exit(r); \ + } \ + } + +/* + * This macro is like `WIZER_INIT()`, but takes no initialization function. + * Instead, the pre-initialization phase only executes C++ global constructors + * before snapshotting the module state. + * + * See documentation for `WIZER_INIT()` for more details and usage instructions. + */ +#define WIZER_DEFAULT_INIT() \ + static void __empty_init() {} \ + WIZER_INIT(__empty_init) + +#endif // _WIZER_H_ diff --git a/crates/wizer/npm/wizer/LICENSE.md b/crates/wizer/npm/wizer/LICENSE.md new file mode 100644 index 000000000000..f9d81955f4bc --- /dev/null +++ b/crates/wizer/npm/wizer/LICENSE.md @@ -0,0 +1,220 @@ + + 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. + diff --git a/crates/wizer/npm/wizer/README.md b/crates/wizer/npm/wizer/README.md new file mode 100644 index 000000000000..1a501256ab08 --- /dev/null +++ b/crates/wizer/npm/wizer/README.md @@ -0,0 +1,18 @@ +# wizer + +> Prebuilt wizer binaries available via npm + +## API + +``` +$ npm install --save @bytecodealliance/wizer +``` + +```js +const execFile = require('child_process').execFile; +const wizer = require('@bytecodealliance/wizer'); + +execFile(wizer, ['input.wasm', '-o', 'initialized.wasm'], (err, stdout) => { + console.log(stdout); +}); +``` diff --git a/crates/wizer/npm/wizer/index.js b/crates/wizer/npm/wizer/index.js new file mode 100644 index 000000000000..c7c3e9065116 --- /dev/null +++ b/crates/wizer/npm/wizer/index.js @@ -0,0 +1,17 @@ +import { pkgForCurrentPlatform } from "./package-helpers.js"; + +const pkg = pkgForCurrentPlatform(); + +let location; +try { + // Check for the binary package from our "optionalDependencies". This + // package should have been installed alongside this package at install time. + location = (await import(pkg)).default; +} catch (e) { + throw new Error(`The package "${pkg}" could not be found, and is needed by @bytecodealliance/wizer. +If you are installing @bytecodealliance/wizer with npm, make sure that you don't specify the +"--no-optional" flag. The "optionalDependencies" package.json feature is used +by @bytecodealliance/wizer to install the correct binary executable for your current platform.`); +} + +export default location; \ No newline at end of file diff --git a/crates/wizer/npm/wizer/package-helpers.js b/crates/wizer/npm/wizer/package-helpers.js new file mode 100644 index 000000000000..72d85aa605b0 --- /dev/null +++ b/crates/wizer/npm/wizer/package-helpers.js @@ -0,0 +1,19 @@ +import { endianness } from "node:os"; +import { platform, arch } from "node:process"; + +const knownPackages = { + "win32 x64 LE": "@bytecodealliance/wizer-win32-x64", + "darwin arm64 LE": "@bytecodealliance/wizer-darwin-arm64", + "darwin x64 LE": "@bytecodealliance/wizer-darwin-x64", + "linux arm64 LE": "@bytecodealliance/wizer-linux-arm64", + "linux s390x BE": "@bytecodealliance/wizer-linux-s390x", + "linux x64 LE": "@bytecodealliance/wizer-linux-x64", +}; + +export function pkgForCurrentPlatform() { + let platformKey = `${platform} ${arch} ${endianness()}`; + if (platformKey in knownPackages) { + return knownPackages[platformKey]; + } + throw new Error(`Unsupported platform: "${platformKey}". "@bytecodealliance/wizer does not have a precompiled binary for the platform/architecture you are using. You can open an issue on https://github.com/bytecodealliance/wizer/issues to request for your platform/architecture to be included."`); +} diff --git a/crates/wizer/npm/wizer/package-lock.json b/crates/wizer/npm/wizer/package-lock.json new file mode 100644 index 000000000000..b8ec28e08c21 --- /dev/null +++ b/crates/wizer/npm/wizer/package-lock.json @@ -0,0 +1,989 @@ +{ + "name": "@bytecodealliance/wizer", + "version": "3.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@bytecodealliance/wizer", + "version": "3.0.1", + "license": "Apache-2.0", + "bin": { + "wizer": "wizer.js" + }, + "devDependencies": { + "decompress": "^4.2.1", + "decompress-tar": "^4.1.1", + "decompress-unzip": "^4.0.1", + "plzmasdk": "^1.2.5" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "@bytecodealliance/wizer-darwin-arm64": "3.0.1", + "@bytecodealliance/wizer-darwin-x64": "3.0.1", + "@bytecodealliance/wizer-linux-arm64": "3.0.1", + "@bytecodealliance/wizer-linux-s390x": "3.0.1", + "@bytecodealliance/wizer-linux-x64": "3.0.1", + "@bytecodealliance/wizer-win32-x64": "3.0.1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "dev": true, + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", + "dev": true + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/decompress": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", + "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", + "dev": true, + "dependencies": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "dev": true, + "dependencies": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "dev": true, + "dependencies": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tarbz2/node_modules/file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "dev": true, + "dependencies": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==", + "dev": true, + "dependencies": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-unzip/node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "node_modules/get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==", + "dev": true, + "dependencies": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==", + "dev": true + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/make-dir/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/plzmasdk": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/plzmasdk/-/plzmasdk-1.2.5.tgz", + "integrity": "sha512-Z0SzRoroOI6yIKhBz3Qvoo6ExYLxOA/+2bj67u8RkNTEuMS1ZDgH0Uj7aZoN2+8+tJlVwhHBvtTBi0mxfhq5Cw==", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=13.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/seek-bzip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", + "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", + "dev": true, + "dependencies": { + "commander": "^2.8.1" + }, + "bin": { + "seek-bunzip": "bin/seek-bunzip", + "seek-table": "bin/seek-bzip-table" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "dev": true, + "dependencies": { + "is-natural-number": "^4.0.1" + } + }, + "node_modules/tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "dev": true, + "dependencies": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "dev": true + }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + }, + "dependencies": { + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "dev": true, + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "decompress": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", + "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", + "dev": true, + "requires": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + } + }, + "decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "dev": true, + "requires": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + } + }, + "decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "dev": true, + "requires": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "dependencies": { + "file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", + "dev": true + } + } + }, + "decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "dev": true, + "requires": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + } + }, + "decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==", + "dev": true, + "requires": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "dependencies": { + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "dev": true + } + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==", + "dev": true + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true + } + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "plzmasdk": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/plzmasdk/-/plzmasdk-1.2.5.tgz", + "integrity": "sha512-Z0SzRoroOI6yIKhBz3Qvoo6ExYLxOA/+2bj67u8RkNTEuMS1ZDgH0Uj7aZoN2+8+tJlVwhHBvtTBi0mxfhq5Cw==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "seek-bzip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", + "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", + "dev": true, + "requires": { + "commander": "^2.8.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "dev": true, + "requires": { + "is-natural-number": "^4.0.1" + } + }, + "tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "dev": true, + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "dev": true + }, + "unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/crates/wizer/npm/wizer/package.json b/crates/wizer/npm/wizer/package.json new file mode 100644 index 000000000000..408684a0fd1b --- /dev/null +++ b/crates/wizer/npm/wizer/package.json @@ -0,0 +1,37 @@ +{ + "name": "@bytecodealliance/wizer", + "version": "VERSION", + "description": "The WebAssembly Pre-Initializer", + "private": true, + "type": "module", + "devDependencies": { + "decompress": "^4.2.1", + "decompress-unzip": "^4.0.1", + "decompress-tar": "^4.1.1", + "plzmasdk": "^1.2.5" + }, + "main": "index.js", + "engines": { + "node": ">=16" + }, + "bin": { + "wizer": "./wizer.js" + }, + "optionalDependencies": { + "@bytecodealliance/wizer-darwin-arm64": "VERSION", + "@bytecodealliance/wizer-darwin-x64": "VERSION", + "@bytecodealliance/wizer-linux-x64": "VERSION", + "@bytecodealliance/wizer-linux-arm64": "VERSION", + "@bytecodealliance/wizer-linux-s390x": "VERSION", + "@bytecodealliance/wizer-win32-x64": "VERSION" + }, + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "git+https://github.com/bytecodealliance/wizer.git" + }, + "bugs": { + "url": "https://github.com/bytecodealliance/wizer/issues" + }, + "homepage": "https://github.com/bytecodealliance/wizer#readme" +} \ No newline at end of file diff --git a/crates/wizer/npm/wizer/update.js b/crates/wizer/npm/wizer/update.js new file mode 100644 index 000000000000..288251452c71 --- /dev/null +++ b/crates/wizer/npm/wizer/update.js @@ -0,0 +1,155 @@ +#!/usr/bin/env node + +import { fileURLToPath } from 'node:url'; +import { dirname, join, parse } from 'node:path'; +import { mkdir, writeFile, readFile } from "node:fs/promises"; +import decompress from 'decompress'; +import decompressUnzip from 'decompress-unzip'; +import decompressTar from 'decompress-tar'; +import plzma from 'plzmasdk'; +const __dirname = dirname(fileURLToPath(import.meta.url)); +const tag = process.argv.slice(2).at(0).trim() || 'dev'; +const version = tag.startsWith('v') ? tag.slice(1) : tag; + +const pjson = JSON.parse(await readFile('package.json')); +pjson.version = version; +delete pjson.private; +for (const dep of Object.keys(pjson.optionalDependencies)) { + pjson.optionalDependencies[dep] = version; +} +await writeFile('package.json', JSON.stringify(pjson, null, 2)); + +let packages = { + 'wizer-darwin-arm64': { + releaseAsset: `wizer-${tag}-aarch64-macos.tar.xz`, + binaryAsset: 'wizer', + description: 'The macOS 64-bit binary for Wizer, the WebAssembly Pre-Initializer', + os: 'darwin', + cpu: 'arm64', + }, + 'wizer-darwin-x64': { + releaseAsset: `wizer-${tag}-x86_64-macos.tar.xz`, + binaryAsset: 'wizer', + description: 'The macOS 64-bit binary for Wizer, the WebAssembly Pre-Initializer', + os: 'darwin', + cpu: 'x64', + }, + 'wizer-linux-x64': { + releaseAsset: `wizer-${tag}-x86_64-linux.tar.xz`, + binaryAsset: 'wizer', + description: 'The Linux 64-bit binary for Wizer, the WebAssembly Pre-Initializer', + os: 'linux', + cpu: 'x64', + }, + 'wizer-linux-arm64': { + releaseAsset: `wizer-${tag}-aarch64-linux.tar.xz`, + binaryAsset: 'wizer', + description : 'The Linux 64-bit binary for Wizer, the WebAssembly Pre-Initializer', + os: 'linux', + cpu: 'arm64', + }, + 'wizer-linux-s390x': { + releaseAsset: `wizer-${tag}-s390x-linux.tar.xz`, + binaryAsset: 'wizer', + description: 'The Linux S390X binary for Wizer, the WebAssembly Pre-Initializer', + os: 'linux', + cpu: 's390x', + }, + 'wizer-win32-x64': { + releaseAsset: `wizer-${tag}-x86_64-windows.zip`, + binaryAsset: 'wizer.exe', + description: 'The Windows 64-bit binary for Wizer, the WebAssembly Pre-Initializer', + os: 'win32', + cpu: 'x64', + }, +} + +let response = await fetch(`https://api.github.com/repos/bytecodealliance/wizer/releases/tags/${tag}`) +if (!response.ok) { + console.error(`Response from https://api.github.com/repos/bytecodealliance/wizer/releases/tags/${tag} was not ok`, response) + console.error(await response.text()) + process.exit(1) +} +response = await response.json() +const id = response.id +let assets = await fetch(`https://api.github.com/repos/bytecodealliance/wizer/releases/${id}/assets`) +if (!assets.ok) { + console.error(`Response from https://api.github.com/repos/bytecodealliance/wizer/releases/${id}/assets was not ok`, assets) + console.error(await response.text()) + process.exit(1) +} +assets = await assets.json() + +for (const [packageName, info] of Object.entries(packages)) { + const asset = assets.find(asset => asset.name === info.releaseAsset) + if (!asset) { + console.error(`Can't find an asset named ${info.releaseAsset} for the release https://github.com/bytecodealliance/wizer/releases/tag/${tag}`) + process.exit(1) + } + const packageDirectory = join(__dirname, '../', packageName.split('/').pop()) + await mkdir(packageDirectory, { recursive: true }) + await writeFile(join(packageDirectory, 'package.json'), packageJson(packageName, tag, info.description, info.os, info.cpu)) + await writeFile(join(packageDirectory, 'index.js'), indexJs(info.binaryAsset)) + const browser_download_url = asset.browser_download_url; + const archive = await fetch(browser_download_url) + if (!archive.ok) { + console.error(`Response from ${browser_download_url} was not ok`, archive) + console.error(await response.text()) + process.exit(1) + } + let buf = await archive.arrayBuffer() + + // Need to decompress into the original tarball format for later use in the `decompress` function + if (info.releaseAsset.endsWith('.xz')) { + const archiveDataInStream = new plzma.InStream(buf); + const decoder = new plzma.Decoder(archiveDataInStream, plzma.FileType.xz); + decoder.open(); + + // We know the xz archive only contains 1 file, the tarball + // We extract the tarball in-memory, for later use in the `decompress` function + const selectedItemsToStreams = new Map(); + selectedItemsToStreams.set(decoder.itemAt(0), plzma.OutStream()); + + decoder.extract(selectedItemsToStreams); + for (const value of selectedItemsToStreams.values()) { + buf = value.copyContent() + } + } + await decompress(Buffer.from(buf), packageDirectory, { + // Remove the leading directory from the extracted file. + strip: 1, + plugins: [ + decompressUnzip(), + decompressTar() + ], + // Only extract the binary file and nothing else + filter: file => parse(file.path).base === info.binaryAsset + }) +} + +function indexJs(binaryAsset) { + return ` +import { fileURLToPath } from 'node:url' +import { dirname, join } from 'node:path' +const __dirname = dirname(fileURLToPath(import.meta.url)) +let location = join(__dirname, '${binaryAsset}') +export default location +` +} +function packageJson(name, version, description, os, cpu) { + version = version.startsWith('v') ? version.replace('v','') : version + return JSON.stringify({ + name: `@bytecodealliance/${name}`, + bin: { + [name]: "wizer" + }, + type: "module", + version, + main: "index.js", + description, + license: "Apache-2.0", + preferUnplugged: false, + os: [os], + cpu: [cpu], + }, null, 4); +} diff --git a/crates/wizer/npm/wizer/wizer.js b/crates/wizer/npm/wizer/wizer.js new file mode 100755 index 000000000000..72cbb0479650 --- /dev/null +++ b/crates/wizer/npm/wizer/wizer.js @@ -0,0 +1,20 @@ +#!/usr/bin/env node +import { execFileSync } from "node:child_process"; + +import { pkgForCurrentPlatform } from "./package-helpers.js"; + +const pkg = pkgForCurrentPlatform(); + +let location; +try { + // Check for the binary package from our "optionalDependencies". This + // package should have been installed alongside this package at install time. + location = (await import(pkg)).default; +} catch (e) { + throw new Error(`The package "${pkg}" could not be found, and is needed by @bytecodealliance/wizer. +If you are installing @bytecodealliance/wizer with npm, make sure that you don't specify the +"--no-optional" flag. The "optionalDependencies" package.json feature is used +by @bytecodealliance/wizer to install the correct binary executable for your current platform.`); +} + +execFileSync(location, process.argv.slice(2), { stdio: "inherit" }); diff --git a/crates/wizer/src/bin/wizer.rs b/crates/wizer/src/bin/wizer.rs new file mode 100644 index 000000000000..d5cb411af776 --- /dev/null +++ b/crates/wizer/src/bin/wizer.rs @@ -0,0 +1,59 @@ +use anyhow::Context; +use std::fs; +use std::io::{self, BufRead, Write}; +use std::path::PathBuf; +use structopt::StructOpt; +use wizer::Wizer; + +#[derive(StructOpt)] +pub struct Options { + /// The input Wasm module's file path. + /// + /// If not specified, then `stdin` is used. + #[structopt(parse(from_os_str))] + input: Option, + + /// The file path to write the output Wasm module to. + /// + /// If not specified, then `stdout` is used. + #[structopt(short = "o", parse(from_os_str))] + output: Option, + + #[structopt(flatten)] + wizer: Wizer, +} + +fn main() -> anyhow::Result<()> { + env_logger::init(); + let options = Options::from_args(); + + let stdin = io::stdin(); + let mut input: Box = if let Some(input) = options.input.as_ref() { + Box::new(io::BufReader::new( + fs::File::open(input).context("failed to open input file")?, + )) + } else { + Box::new(stdin.lock()) + }; + + let mut output: Box = if let Some(output) = options.output.as_ref() { + Box::new(io::BufWriter::new( + fs::File::create(output).context("failed to create output file")?, + )) + } else { + Box::new(io::stdout()) + }; + + let mut input_wasm = vec![]; + input + .read_to_end(&mut input_wasm) + .context("failed to read input Wasm module")?; + + let output_wasm = options.wizer.run(&input_wasm)?; + + output + .write_all(&output_wasm) + .context("failed to write to output")?; + + Ok(()) +} diff --git a/crates/wizer/src/dummy.rs b/crates/wizer/src/dummy.rs new file mode 100644 index 000000000000..949447a5c481 --- /dev/null +++ b/crates/wizer/src/dummy.rs @@ -0,0 +1,93 @@ +//! Dummy implementations of things that a Wasm module can import. +//! +//! Forked from `wasmtime/crates/fuzzing/src/oracles/dummy.rs`. + +use anyhow::{anyhow, Result}; +use wasmtime::*; + +/// Create dummy imports for instantiating the module. +pub fn dummy_imports( + store: &mut crate::Store, + module: &wasmtime::Module, + linker: &mut crate::Linker, +) -> Result<()> { + log::debug!("Creating dummy imports"); + + for imp in module.imports() { + let name = imp.name(); + if linker.get(&mut *store, imp.module(), name).is_some() { + // Already defined, must be part of WASI. + continue; + } + let val = dummy_extern( + &mut *store, + imp.ty(), + &format!("'{}' '{}'", imp.module(), name), + )?; + linker.define(&mut *store, imp.module(), name, val).unwrap(); + } + + Ok(()) +} + +/// Construct a dummy `Extern` from its type signature +pub fn dummy_extern(store: &mut crate::Store, ty: ExternType, name: &str) -> Result { + Ok(match ty { + ExternType::Func(func_ty) => Extern::Func(dummy_func(store, func_ty, name)), + ExternType::Global(_) => { + anyhow::bail!("Error: attempted to import unknown global: {}", name) + } + ExternType::Table(_) => anyhow::bail!("Error: attempted to import unknown table: {}", name), + ExternType::Memory(_) => { + anyhow::bail!("Error: attempted to import unknown memory: {}", name) + } + ExternType::Tag(_) => { + anyhow::bail!("Error: attempted to import unknown tag: {}", name) + } + }) +} + +/// Construct a dummy function for the given function type +pub fn dummy_func(store: &mut crate::Store, ty: FuncType, name: &str) -> Func { + let name = name.to_string(); + Func::new(store, ty.clone(), move |_caller, _params, _results| { + Err(anyhow!( + "Error: attempted to call an unknown imported function: {}\n\ + \n\ + You cannot call arbitrary imported functions during Wizer initialization.", + name, + )) + }) +} + +/// Construct a dummy value for the given value type. +#[cfg(fuzzing)] +pub fn dummy_value(val_ty: ValType) -> Result { + Ok(match val_ty { + ValType::I32 => Val::I32(0), + ValType::I64 => Val::I64(0), + ValType::F32 => Val::F32(0), + ValType::F64 => Val::F64(0), + ValType::V128 => Val::V128(0.into()), + ValType::Ref(ref_type) => { + if !ref_type.is_nullable() { + anyhow::bail!("cannot create a dummy value for a non-nullable reference type"); + } + if ref_type.matches(&RefType::EXTERNREF) { + Val::ExternRef(None) + } else if ref_type.matches(&RefType::FUNCREF) { + Val::FuncRef(None) + } else if ref_type.matches(&RefType::NULLFUNCREF) { + Val::FuncRef(None) + } else { + panic!("Unknown RefType {:?}", ref_type); + } + } + }) +} + +/// Construct a sequence of dummy values for the given types. +#[cfg(fuzzing)] +pub fn dummy_values(val_tys: impl IntoIterator) -> Result> { + val_tys.into_iter().map(|ty| dummy_value(ty)).collect() +} diff --git a/crates/wizer/src/info.rs b/crates/wizer/src/info.rs new file mode 100644 index 000000000000..82acbbbde5ec --- /dev/null +++ b/crates/wizer/src/info.rs @@ -0,0 +1,303 @@ +use std::convert::TryFrom; +use std::ops::Range; +use types_interner::{EntityType, TypeId, TypesInterner}; +use wasm_encoder::SectionId; + +pub mod types_interner; + +/// A collection of info about modules within a module linking bundle. +pub(crate) struct ModuleContext<'a> { + arena: Vec>, + types: TypesInterner, +} + +impl<'a> ModuleContext<'a> { + /// Construct a new `ModuleContext`, pre-populated with an empty root + /// module. + pub fn new() -> Self { + Self { + arena: vec![ModuleInfo::Defined(DefinedModuleInfo::default())], + types: TypesInterner::default(), + } + } + + /// Get the root module. + pub fn root(&self) -> Module { + Module { id: 0 } + } + + /// Get the interned types set for this module context. + pub fn types(&self) -> &TypesInterner { + &self.types + } + + /// Get a shared reference to the `DefinedModuleInfo` for this module, + /// following through aliases. + fn defined(&self, module: Module) -> &DefinedModuleInfo<'a> { + match &self.arena[module.id] { + ModuleInfo::Defined(d) => return d, + } + } + + /// Get an exclusive reference to the `DefinedModuleInfo` for this module. + /// + /// Does not resolve through aliases, because you shouldn't ever mutate + /// aliased modules. + fn defined_mut(&mut self, module: Module) -> &mut DefinedModuleInfo<'a> { + match &mut self.arena[module.id] { + ModuleInfo::Defined(d) => d, + } + } +} + +enum ModuleInfo<'a> { + Defined(DefinedModuleInfo<'a>), +} + +/// Info that we keep track of on a per module-within-a-module-linking-bundle +/// basis. +/// +/// These are created during during our `parse` pass and then used throughout +/// our later passes. +#[derive(Default)] +struct DefinedModuleInfo<'a> { + /// The raw sections from the original Wasm input. + raw_sections: Vec>, + + /// Types available in this module. + /// + /// We keep track of these for determining how many things we need to + /// re-export for new instantiations and for inner module's aliases. + types: Vec, + + /// Imports made by this module. + imports: Vec>, + + /// A map from global indices to each global's type for all defined, + /// imported, and aliased globals. + globals: Vec, + + /// The index within the global index space where defined globals (as + /// opposed to imported or aliased) begin. + /// + /// If this is `None`, then there are no locally defined globals. + defined_globals_index: Option, + + /// This module's exports. + /// + /// This is used later on, in the rewrite phase, when we are inserting state + /// instance imports. + /// + /// Note that this does *not* include the `__wizer_thing_N` exports that + /// this instrumentation pass adds. + exports: Vec>, + + /// Maps from function index to the function's type index for all functions + /// defined, imported, and aliased in this module. + functions: Vec, + + /// Maps from table index to the table's type for all tables defined, + /// imported, and aliased in this module. + tables: Vec, + + /// Maps from memory index to the memory's type for all memories defined, + /// imported, and aliased in this module. + memories: Vec, + + /// The index within the memory index space where defined memories (as + /// opposed to imported or aliased) begin. + /// + /// If this is `None`, then there are no locally defined memories. + defined_memories_index: Option, +} + +/// A module inside a module linking bundle. +/// +/// This is a small, copy-able type that essentially just indexes into a +/// `ModuleContext`. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub(crate) struct Module { + /// This module's id, aka its pre-order traversal index, aka its index in + /// `Modules::arena`. + id: usize, +} + +impl Module { + /// Translate the given `wasmparser` entity type into its interned + /// representation using this module's types space. + pub fn entity_type(self, cx: &ModuleContext<'_>, ty: wasmparser::TypeRef) -> EntityType { + cx.types().entity_type(ty, &cx.defined(self).types) + } + + /// Add a new raw section to this module info during parsing. + pub fn add_raw_section<'a>( + self, + cx: &mut ModuleContext<'a>, + id: SectionId, + range: Range, + full_wasm: &'a [u8], + ) { + cx.defined_mut(self) + .raw_sections + .push(wasm_encoder::RawSection { + id: id as u8, + data: &full_wasm[range.start..range.end], + }) + } + + /// Push a new type into this module's types space. + pub fn push_type<'a>(self, cx: &mut ModuleContext<'a>, ty: wasmparser::CompositeType) { + let types_space = match &cx.arena[self.id] { + ModuleInfo::Defined(d) => &d.types, + }; + let ty = cx.types.insert_wasmparser(ty, types_space); + cx.defined_mut(self).types.push(ty); + } + + /// Push a new imported memory into this module's memory index space. + pub fn push_imported_memory(self, cx: &mut ModuleContext, memory_type: wasmparser::MemoryType) { + let info = cx.defined_mut(self); + assert!(info.defined_memories_index.is_none()); + info.memories.push(memory_type); + } + + /// Push a new defined memory into this module's memory index space. + pub fn push_defined_memory(self, cx: &mut ModuleContext, memory_type: wasmparser::MemoryType) { + let info = cx.defined_mut(self); + if info.defined_memories_index.is_none() { + info.defined_memories_index = Some(u32::try_from(info.memories.len()).unwrap()); + } + info.memories.push(memory_type); + } + + /// Push a new imported global into this module's global index space. + pub fn push_imported_global(self, cx: &mut ModuleContext, global_type: wasmparser::GlobalType) { + let info = cx.defined_mut(self); + assert!(info.defined_globals_index.is_none()); + info.globals.push(global_type); + } + + /// Push a new defined global into this module's global index space. + pub fn push_defined_global(self, cx: &mut ModuleContext, global_type: wasmparser::GlobalType) { + let info = cx.defined_mut(self); + if info.defined_globals_index.is_none() { + info.defined_globals_index = Some(u32::try_from(info.globals.len()).unwrap()); + } + info.globals.push(global_type); + } + + /// Push a new function into this module's function index space. + pub fn push_function(self, cx: &mut ModuleContext, func_type: TypeId) { + assert!(cx.types.get(func_type).is_func()); + cx.defined_mut(self).functions.push(func_type); + } + + /// Push a new table into this module's table index space. + pub fn push_table(self, cx: &mut ModuleContext, table_type: wasmparser::TableType) { + cx.defined_mut(self).tables.push(table_type); + } + + /// Push a new import into this module. + pub fn push_import<'a>(self, cx: &mut ModuleContext<'a>, import: wasmparser::Import<'a>) { + cx.defined_mut(self).imports.push(import); + + // Add the import to the appropriate index space for our current module. + match import.ty { + wasmparser::TypeRef::Memory(ty) => { + self.push_imported_memory(cx, ty); + } + wasmparser::TypeRef::Global(ty) => { + self.push_imported_global(cx, ty); + } + wasmparser::TypeRef::Func(ty_idx) => { + let ty = self.type_id_at(cx, ty_idx); + self.push_function(cx, ty); + } + wasmparser::TypeRef::Table(ty) => { + self.push_table(cx, ty); + } + wasmparser::TypeRef::Tag(_) => { + unreachable!("exceptions are unsupported; checked in validation") + } + } + } + + /// Push an export into this module. + pub fn push_export<'a>(self, cx: &mut ModuleContext<'a>, export: wasmparser::Export<'a>) { + cx.defined_mut(self).exports.push(export); + } + + /// Is this the root of the module linking bundle? + pub fn is_root(self) -> bool { + self.id == 0 + } + + /// The number of defined memories in this module. + pub fn defined_memories_len(self, cx: &ModuleContext) -> usize { + let info = cx.defined(self); + info.defined_memories_index.map_or(0, |n| { + let n = usize::try_from(n).unwrap(); + assert!(info.memories.len() > n); + info.memories.len() - n + }) + } + + /// Iterate over the defined memories in this module. + pub fn defined_memories<'b>( + self, + cx: &'b ModuleContext<'_>, + ) -> impl Iterator + 'b { + let info = cx.defined(self); + info.memories + .iter() + .copied() + .enumerate() + .skip( + info.defined_memories_index + .map_or(info.memories.len(), |i| usize::try_from(i).unwrap()), + ) + .map(|(i, m)| (u32::try_from(i).unwrap(), m)) + } + + /// Iterate over the defined globals in this module. + pub fn defined_globals<'b>( + self, + cx: &'b ModuleContext<'_>, + ) -> impl Iterator + 'b { + let info = cx.defined(self); + info.globals + .iter() + .copied() + .enumerate() + .skip( + info.defined_globals_index + .map_or(info.globals.len(), |i| usize::try_from(i).unwrap()), + ) + .map(|(i, g)| (u32::try_from(i).unwrap(), g)) + } + + /// Get a slice of this module's original raw sections. + pub fn raw_sections<'a, 'b>( + self, + cx: &'b ModuleContext<'a>, + ) -> &'b [wasm_encoder::RawSection<'a>] { + &cx.defined(self).raw_sections + } + + /// Get a slice of this module's exports. + pub fn exports<'a, 'b>(self, cx: &'b ModuleContext<'a>) -> &'b [wasmparser::Export<'a>] { + &cx.defined(self).exports + } + + /// Get the full types index space for this module. + pub fn types<'a, 'b>(self, cx: &'b ModuleContext<'a>) -> &'b [TypeId] { + &cx.defined(self).types + } + + /// Get the type at the given index. + /// + /// Panics if the types index space does not contain the given index. + pub fn type_id_at(self, cx: &ModuleContext<'_>, type_index: u32) -> TypeId { + cx.defined(self).types[usize::try_from(type_index).unwrap()] + } +} diff --git a/crates/wizer/src/info/types_interner.rs b/crates/wizer/src/info/types_interner.rs new file mode 100644 index 000000000000..748a835bddc5 --- /dev/null +++ b/crates/wizer/src/info/types_interner.rs @@ -0,0 +1,107 @@ +use std::{collections::HashMap, convert::TryFrom, rc::Rc}; + +/// A de-duplicated set of type definitions. +/// +/// We insert new entries via hash consing, to de-duplicate entries. +/// +/// This is shared across all modules in a module linking bundle. +/// +/// We assign and track a unique index for each type we insert. These end up +/// being the indices of each type in the root Wasm module. All nested modules +/// refer to these types, and pull them into their nested types index space, via +/// outer type aliases. +#[derive(Default)] +pub struct TypesInterner { + /// The interned types. + types: Vec>, + + /// An map from a type to its index in `self.types`. + type_to_index: HashMap, u32>, +} + +/// An interned Wasm type definition. +#[derive(PartialEq, Eq, Hash)] +pub enum Type { + Func(wasmparser::FuncType), +} + +impl Type { + pub fn is_func(&self) -> bool { + matches!(self, Type::Func(_)) + } +} + +/// An interned type for some kind of Wasm entity. +#[derive(PartialEq, Eq, Hash)] +pub enum EntityType { + Function(TypeId), + Table(wasmparser::TableType), + Memory(wasmparser::MemoryType), + Global(wasmparser::GlobalType), +} + +/// An id of a type in a `TypesInterner` type set. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct TypeId { + index: u32, +} + +impl TypesInterner { + /// Get a type by id. + pub fn get(&self, id: TypeId) -> &Type { + &*self.types[usize::try_from(id.index).unwrap()] + } + + /// Intern a `wasmparser` type into this type set and get its id. + /// + /// The provided `types_space` must be a slice of the defining module's + /// types index space. + /// + /// If the type has already been inserted and assigned an id before, then + /// that entry and its id are reused. + pub fn insert_wasmparser( + &mut self, + ty: wasmparser::CompositeType, + _types_space: &[TypeId], + ) -> TypeId { + match ty.inner { + wasmparser::CompositeInnerType::Func(func_ty) => self.insert(Type::Func(func_ty)), + wasmparser::CompositeInnerType::Array(_) => todo!(), + wasmparser::CompositeInnerType::Struct(_) => todo!(), + wasmparser::CompositeInnerType::Cont(_) => todo!(), + } + } + + /// Insert a new type into this type set and get its id. + /// + /// If the type has already been inserted and assigned an id before, then + /// that entry and its id are reused. + pub fn insert(&mut self, ty: Type) -> TypeId { + if let Some(index) = self.type_to_index.get(&ty).copied() { + return TypeId { index }; + } + + let index = u32::try_from(self.types.len()).unwrap(); + let ty = Rc::new(ty); + self.type_to_index.insert(ty.clone(), index); + self.types.push(ty); + TypeId { index } + } + + /// Convert a `wasmparser::EntityType` into an interned + /// `EntityType`. + /// + /// The provided `types_space` must be a slice of the defining module's + /// types index space. + pub fn entity_type(&self, ty: wasmparser::TypeRef, types_space: &[TypeId]) -> EntityType { + match ty { + wasmparser::TypeRef::Func(idx) => { + EntityType::Function(types_space[usize::try_from(idx).unwrap()]) + } + wasmparser::TypeRef::Table(ty) => EntityType::Table(ty), + wasmparser::TypeRef::Memory(ty) => EntityType::Memory(ty), + wasmparser::TypeRef::Global(ty) => EntityType::Global(ty), + wasmparser::TypeRef::Tag(_) => unreachable!(), + } + } +} diff --git a/crates/wizer/src/instrument.rs b/crates/wizer/src/instrument.rs new file mode 100644 index 000000000000..f6fb9c7e57e2 --- /dev/null +++ b/crates/wizer/src/instrument.rs @@ -0,0 +1,144 @@ +//! The initial instrumentation pass. + +use crate::info::{Module, ModuleContext}; +use crate::stack_ext::StackExt; +use wasm_encoder::SectionId; + +/// Instrument the input Wasm so that it exports its memories and globals, +/// allowing us to inspect their state after the module is instantiated and +/// initialized. +/// +/// For example, given this input module: +/// +/// ```wat +/// (module $A +/// (module $B +/// (memory $B_mem) +/// (global $B_glob (mut i32)) +/// ) +/// +/// (instance $x (instantiate $B)) +/// (instance $y (instantiate $B)) +/// +/// (memory $A_mem) +/// (global $A_glob (mut i32)) +/// ) +/// ``` +/// +/// this pass will produce the following instrumented module: +/// +/// ```wat +/// (module $A +/// (module $B +/// (memory $B_mem) +/// (global $B_glob (mut i32)) +/// +/// ;; Export all state. +/// (export "__wizer_memory_0" (memory $B_mem)) +/// (export "__wizer_global_0" (global $B_glob)) +/// ) +/// +/// (instance $x (instantiate $B)) +/// (instance $y (instantiate $B)) +/// +/// (memory $A_mem) +/// (global $A_glob (mut i32)) +/// +/// ;; Export of all state (including transitively re-exporting nested +/// ;; instantiations' state). +/// (export "__wizer_memory_0" (memory $A_mem)) +/// (export "__wizer_global_0" (global $A_glob)) +/// (export "__wizer_instance_0" (instance $x)) +/// (export "__wizer_instance_1" (instance $y)) +/// ) +/// ``` +/// +/// NB: we re-export nested instantiations as a whole instance export because we +/// can do this without disturbing existing instances' indices. If we were to +/// export their memories and globals individually, that would disturb the +/// modules locally defined memoryies' and globals' indices, which would require +/// rewriting the code section, which would break debug info offsets. +pub(crate) fn instrument(cx: &ModuleContext<'_>) -> Vec { + log::debug!("Instrumenting the input Wasm"); + + struct StackEntry<'a> { + /// This entry's module. + module: Module, + + /// The work-in-progress encoding of the new, instrumented module. + encoder: wasm_encoder::Module, + + /// Sections in this module info that we are iterating over. + sections: std::slice::Iter<'a, wasm_encoder::RawSection<'a>>, + } + + let root = cx.root(); + let mut stack = vec![StackEntry { + module: root, + encoder: wasm_encoder::Module::new(), + sections: root.raw_sections(cx).iter(), + }]; + + loop { + assert!(!stack.is_empty()); + + match stack.top_mut().sections.next() { + // For the exports section, we need to transitively export internal + // state so that we can read the initialized state after we call the + // initialization function. + Some(section) if section.id == u8::from(SectionId::Export) => { + let entry = stack.top_mut(); + let mut exports = wasm_encoder::ExportSection::new(); + + // First, copy over all the original exports. + for export in entry.module.exports(cx) { + exports.export( + export.name, + match export.kind { + wasmparser::ExternalKind::Func => wasm_encoder::ExportKind::Func, + wasmparser::ExternalKind::Table => wasm_encoder::ExportKind::Table, + wasmparser::ExternalKind::Memory => wasm_encoder::ExportKind::Memory, + wasmparser::ExternalKind::Global => wasm_encoder::ExportKind::Global, + wasmparser::ExternalKind::Tag => { + unreachable!("should have been rejected in validation/parsing") + } + }, + export.index, + ); + } + + // Now export all of this module's defined globals, memories, + // and instantiations under well-known names so we can inspect + // them after initialization. + for (i, (j, _)) in entry.module.defined_globals(cx).enumerate() { + let name = format!("__wizer_global_{}", i); + exports.export(&name, wasm_encoder::ExportKind::Global, j); + } + for (i, (j, _)) in entry.module.defined_memories(cx).enumerate() { + let name = format!("__wizer_memory_{}", i); + exports.export(&name, wasm_encoder::ExportKind::Memory, j); + } + + entry.encoder.section(&exports); + } + + // End of the current module: if this is the root, return the + // instrumented module, otherwise add it as an entry in its parent's + // module section. + None => { + let entry = stack.pop().unwrap(); + + if entry.module.is_root() { + assert!(stack.is_empty()); + return entry.encoder.finish(); + } + } + + // All other sections don't need instrumentation and can be copied + // over directly. + Some(section) => { + stack.top_mut().encoder.section(section); + } + } + } +} diff --git a/crates/wizer/src/lib.rs b/crates/wizer/src/lib.rs new file mode 100644 index 000000000000..61d4d814ae2d --- /dev/null +++ b/crates/wizer/src/lib.rs @@ -0,0 +1,936 @@ +//! Wizer: the WebAssembly pre-initializer! +//! +//! See the [`Wizer`] struct for details. + +#![deny(missing_docs)] + +#[cfg(fuzzing)] +pub mod dummy; +#[cfg(not(fuzzing))] +mod dummy; + +mod info; +mod instrument; +mod parse; +mod rewrite; +mod snapshot; +mod stack_ext; +mod translate; + +use wasmparser::WasmFeatures; +/// Re-export wasmtime so users can align with our version. This is +/// especially useful when providing a custom Linker. +pub use wasmtime; + +use anyhow::Context; +use dummy::dummy_imports; +use std::collections::{HashMap, HashSet}; +use std::fmt::Display; +use std::path::PathBuf; +use std::rc::Rc; +#[cfg(feature = "structopt")] +use structopt::StructOpt; +use wasmtime::{Engine, Extern}; +use wasmtime_wasi::{ + preview1::{self, WasiP1Ctx}, + WasiCtxBuilder, +}; + +const DEFAULT_INHERIT_STDIO: bool = true; +const DEFAULT_INHERIT_ENV: bool = false; +const DEFAULT_KEEP_INIT_FUNC: bool = false; +const DEFAULT_WASM_MULTI_VALUE: bool = true; +const DEFAULT_WASM_MULTI_MEMORY: bool = true; +const DEFAULT_WASM_BULK_MEMORY: bool = true; +const DEFAULT_WASM_SIMD: bool = true; +const DEFAULT_WASM_REFERENCE_TYPES: bool = true; + +/// The type of data that is stored in the `wasmtime::Store` during +/// initialization. +pub struct StoreData { + /// The WASI context that is optionally used during initialization. + pub wasi_ctx: Option, +} + +/// We only ever use `Store` with a fixed `T` that is our optional WASI +/// context. +pub(crate) type Store = wasmtime::Store; + +/// The type of linker that Wizer uses when evaluating the initialization function. +pub type Linker = wasmtime::Linker; + +#[cfg(feature = "structopt")] +fn parse_map_dirs(s: &str) -> anyhow::Result<(String, PathBuf)> { + let parts: Vec<&str> = s.split("::").collect(); + if parts.len() != 2 { + anyhow::bail!("must contain exactly one double colon ('::')"); + } + Ok((parts[0].into(), parts[1].into())) +} + +/// Wizer: the WebAssembly pre-initializer! +/// +/// Don't wait for your Wasm module to initialize itself, pre-initialize it! +/// Wizer instantiates your WebAssembly module, executes its initialization +/// function, and then serializes the instance's initialized state out into a +/// new WebAssembly module. Now you can use this new, pre-initialized +/// WebAssembly module to hit the ground running, without making your users wait +/// for that first-time set up code to complete. +/// +/// ## Caveats +/// +/// * The initialization function may not call any imported functions. Doing so +/// will trigger a trap and `wizer` will exit. +/// +/// * The Wasm module may not import globals, tables, or memories. +/// +/// * Reference types are not supported yet. This is tricky because it would +/// allow the Wasm module to mutate tables, and we would need to be able to +/// snapshot the new table state, but funcrefs and externrefs don't have +/// identity and aren't comparable in the Wasm spec, which makes snapshotting +/// difficult. +#[cfg_attr(feature = "structopt", derive(StructOpt))] +#[derive(Clone)] +pub struct Wizer { + /// The Wasm export name of the function that should be executed to + /// initialize the Wasm module. + #[cfg_attr( + feature = "structopt", + structopt(short = "f", long = "init-func", default_value = "wizer.initialize") + )] + init_func: String, + + /// Any function renamings to perform. + /// + /// A renaming specification `dst=src` renames a function export `src` to + /// `dst`, overwriting any previous `dst` export. + /// + /// Multiple renamings can be specified. It is an error to specify more than + /// one source to rename to a destination name, or to specify more than one + /// renaming destination for one source. + /// + /// This option can be used, for example, to replace a `_start` entry point + /// in an initialized module with an alternate entry point. + /// + /// When module linking is enabled, these renames are only applied to the + /// outermost module. + #[cfg_attr( + feature = "structopt", + structopt( + short = "r", + long = "rename-func", + alias = "func-rename", + value_name = "dst=src" + ) + )] + func_renames: Vec, + + /// Allow WASI imports to be called during initialization. + /// + /// This can introduce diverging semantics because the initialization can + /// observe nondeterminism that might have gone a different way at runtime + /// than it did at initialization time. + /// + /// If your Wasm module uses WASI's `get_random` to add randomness to + /// something as a security mitigation (e.g. something akin to ASLR or the + /// way Rust's hash maps incorporate a random nonce) then note that, if the + /// randomization is added during initialization time and you don't ever + /// re-randomize at runtime, then that randomization will become per-module + /// rather than per-instance. + #[cfg_attr(feature = "structopt", structopt(long = "allow-wasi"))] + allow_wasi: bool, + + /// Provide an additional preloaded module that is available to the + /// main module. + /// + /// This allows running a module that depends on imports from + /// another module. Note that the additional module's state is *not* + /// snapshotted, nor is its code included in the Wasm snapshot; + /// rather, it is assumed that the resulting snapshot Wasm will also + /// be executed with the same imports available. + /// + /// The main purpose of this option is to allow "stubs" for certain + /// intrinsics to be included, when these will be provided with + /// different implementations when running or further processing the + /// snapshot. + /// + /// The format of this option is `name=file.{wasm,wat}`; e.g., + /// `intrinsics=stubs.wat`. Multiple instances of the option may + /// appear. + #[cfg_attr(feature = "structopt", structopt(long = "preload"))] + preload: Vec, + + /// Like `preload` above, but with the module contents provided, + /// rather than a filename. This is more useful for programmatic + /// use-cases where the embedding tool may also embed a Wasm module. + #[cfg_attr(feature = "structopt", structopt(skip))] + preload_bytes: Vec<(String, Vec)>, + + #[cfg_attr(feature = "structopt", structopt(skip))] + make_linker: Option anyhow::Result>>, + + /// When using WASI during initialization, should `stdin`, `stderr`, and + /// `stdout` be inherited? + /// + /// This is true by default. + #[cfg_attr( + feature = "structopt", + structopt(long = "inherit-stdio", value_name = "true|false") + )] + inherit_stdio: Option, + + /// When using WASI during initialization, should environment variables be + /// inherited? + /// + /// This is false by default. + #[cfg_attr( + feature = "structopt", + structopt(long = "inherit-env", value_name = "true|false") + )] + inherit_env: Option, + + /// After initialization, should the Wasm module still export the + /// initialization function? + /// + /// This is `false` by default, meaning that the initialization function is + /// no longer exported from the Wasm module. + #[cfg_attr( + feature = "structopt", + structopt(long = "keep-init-func", value_name = "true|false") + )] + keep_init_func: Option, + + /// When using WASI during initialization, which file system directories + /// should be made available? + /// + /// None are available by default. + #[cfg_attr( + feature = "structopt", + structopt(long = "dir", parse(from_os_str), value_name = "directory") + )] + dirs: Vec, + + /// When using WASI during initialization, which guest directories should be + /// mapped to a host directory? + /// + /// The `--mapdir` option differs from `--dir` in that it allows giving a + /// custom guest name to the directory that is different from its name in + /// the host. + /// + /// None are mapped by default. + #[cfg_attr( + feature = "structopt", + structopt(long = "mapdir", value_name = "GUEST_DIR::HOST_DIR", parse(try_from_str = parse_map_dirs)) + )] + map_dirs: Vec<(String, PathBuf)>, + + /// Enable or disable Wasm multi-memory proposal. + /// + /// Enabled by default. + #[cfg_attr(feature = "structopt", structopt(long, value_name = "true|false"))] + wasm_multi_memory: Option, + + /// Enable or disable the Wasm multi-value proposal. + /// + /// Enabled by default. + #[cfg_attr(feature = "structopt", structopt(long, value_name = "true|false"))] + wasm_multi_value: Option, + + /// Enable or disable Wasm bulk memory operations. + /// + /// Note that only `memory.copy`, `memory.fill`, and `memory.init` operations + /// are currently supported. Modules which use other instructions, such as + /// `table.copy` will be rejected. + /// + /// Enabled by default. + #[cfg_attr(feature = "structopt", structopt(long, value_name = "true|false"))] + wasm_bulk_memory: Option, + + /// Enable or disable the Wasm SIMD128 proposal. + /// + /// Enabled by default. + #[cfg_attr(feature = "structopt", structopt(long, value_name = "true|false"))] + wasm_simd: Option, + + /// Enable or disable the Wasm reference-types proposal. + /// + /// Currently does not implement snapshotting or the use of references, + /// but enables initializing Wasm modules that use encodings introduced + /// in the reference-types proposal. + /// + /// Enabled by default. + #[cfg_attr(feature = "structopt", structopt(long, value_name = "true|false"))] + wasm_reference_types: Option, +} + +impl std::fmt::Debug for Wizer { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let Wizer { + init_func, + func_renames, + allow_wasi, + preload, + preload_bytes, + make_linker: _, + inherit_stdio, + inherit_env, + keep_init_func, + dirs, + map_dirs, + wasm_multi_memory, + wasm_multi_value, + wasm_bulk_memory, + wasm_simd, + wasm_reference_types, + } = self; + f.debug_struct("Wizer") + .field("init_func", &init_func) + .field("func_renames", &func_renames) + .field("allow_wasi", &allow_wasi) + .field("preload", &preload) + .field("preload_bytes", &preload_bytes) + .field("make_linker", &"..") + .field("inherit_stdio", &inherit_stdio) + .field("inherit_env", &inherit_env) + .field("keep_init_func", &keep_init_func) + .field("dirs", &dirs) + .field("map_dirs", &map_dirs) + .field("wasm_multi_memory", &wasm_multi_memory) + .field("wasm_multi_value", &wasm_multi_value) + .field("wasm_bulk_memory", &wasm_bulk_memory) + .field("wasm_simd", &wasm_simd) + .field("wasm_reference_types", &wasm_reference_types) + .finish() + } +} + +struct FuncRenames { + /// For a given export name that we encounter in the original module, a map + /// to a new name, if any, to emit in the output module. + rename_src_to_dst: HashMap, + /// A set of export names that we ignore in the original module (because + /// they are overwritten by renamings). + rename_dsts: HashSet, +} + +impl FuncRenames { + fn parse(renames: &Vec) -> anyhow::Result { + let mut ret = FuncRenames { + rename_src_to_dst: HashMap::new(), + rename_dsts: HashSet::new(), + }; + if renames.is_empty() { + return Ok(ret); + } + + for rename_spec in renames { + let equal = rename_spec + .trim() + .find('=') + .ok_or_else(|| anyhow::anyhow!("Invalid function rename part: {}", rename_spec))?; + // TODO: use .split_off() when the API is stabilized. + let dst = rename_spec[..equal].to_owned(); + let src = rename_spec[equal + 1..].to_owned(); + if ret.rename_dsts.contains(&dst) { + anyhow::bail!("Duplicated function rename dst {}", dst); + } + if ret.rename_src_to_dst.contains_key(&src) { + anyhow::bail!("Duplicated function rename src {}", src); + } + ret.rename_dsts.insert(dst.clone()); + ret.rename_src_to_dst.insert(src, dst); + } + + Ok(ret) + } +} + +impl Wizer { + /// Construct a new `Wizer` builder. + pub fn new() -> Self { + Wizer { + init_func: "wizer.initialize".into(), + func_renames: vec![], + allow_wasi: false, + preload: vec![], + preload_bytes: vec![], + make_linker: None, + inherit_stdio: None, + inherit_env: None, + keep_init_func: None, + dirs: vec![], + map_dirs: vec![], + wasm_multi_memory: None, + wasm_multi_value: None, + wasm_bulk_memory: None, + wasm_simd: None, + wasm_reference_types: None, + } + } + + /// The export name of the initializer function. + /// + /// Defaults to `"wizer.initialize"`. + pub fn init_func(&mut self, init_func: impl Into) -> &mut Self { + self.init_func = init_func.into(); + self + } + + /// Add a function rename to perform. + pub fn func_rename(&mut self, new_name: impl Display, old_name: impl Display) -> &mut Self { + self.func_renames.push(format!("{}={}", new_name, old_name)); + self + } + + /// Allow WASI imports to be called during initialization? + /// + /// This can introduce diverging semantics because the initialization can + /// observe nondeterminism that might have gone a different way at runtime + /// than it did at initialization time. + /// + /// If your Wasm module uses WASI's `get_random` to add randomness to + /// something as a security mitigation (e.g. something akin to ASLR or the + /// way Rust's hash maps incorporate a random nonce) then note that, if the + /// randomization is added during initialization time and you don't ever + /// re-randomize at runtime, then that randomization will become per-module + /// rather than per-instance. + /// + /// Defaults to `false`. + pub fn allow_wasi(&mut self, allow: bool) -> anyhow::Result<&mut Self> { + anyhow::ensure!( + self.make_linker.is_none(), + "Cannot use 'allow_wasi' with a custom linker" + ); + self.allow_wasi = allow; + Ok(self) + } + + /// Provide an additional preloaded module that is available to the + /// main module. + /// + /// This allows running a module that depends on imports from + /// another module. Note that the additional module's state is *not* + /// snapshotted, nor is its code included in the Wasm snapshot; + /// rather, it is assumed that the resulting snapshot Wasm will also + /// be executed with the same imports available. + /// + /// The main purpose of this option is to allow "stubs" for certain + /// intrinsics to be included, when these will be provided with + /// different implementations when running or further processing the + /// snapshot. + pub fn preload(&mut self, name: &str, filename: &str) -> anyhow::Result<&mut Self> { + anyhow::ensure!( + self.make_linker.is_none(), + "Cannot use 'preload' with a custom linker" + ); + anyhow::ensure!( + !name.contains("="), + "Module name cannot contain an `=` character" + ); + self.preload.push(format!("{}={}", name, filename)); + Ok(self) + } + + /// Provide an additional preloaded module that is available to the + /// main module. Unlike `preload()`, this method takes an owned + /// vector of bytes as the module's actual content, rather than a + /// filename. As with `preload()`, the module may be in Wasm binary + /// format or in WAT text format. + /// + /// This allows running a module that depends on imports from + /// another module. Note that the additional module's state is *not* + /// snapshotted, nor is its code included in the Wasm snapshot; + /// rather, it is assumed that the resulting snapshot Wasm will also + /// be executed with the same imports available. + /// + /// The main purpose of this option is to allow "stubs" for certain + /// intrinsics to be included, when these will be provided with + /// different implementations when running or further processing the + /// snapshot. + pub fn preload_bytes( + &mut self, + name: &str, + module_bytes: Vec, + ) -> anyhow::Result<&mut Self> { + anyhow::ensure!( + self.make_linker.is_none(), + "Cannot use 'preload_bytes' with a custom linker" + ); + self.preload_bytes.push((name.to_owned(), module_bytes)); + Ok(self) + } + + /// The linker to use during initialization rather than the default + /// `wasmtime::Linker`. + /// + /// If you want your Wasm module to be able to import non-WASI functionality + /// (or a subset of WASI) during initialization, you can provide a closure + /// that returns a `Linker` result. Note, this has the same non-determinism + /// concerns that `.allow_wasi(true)` does: if the allowed imports interact + /// with the world in some way, the outcome of that interaction will be + /// snapshotted by Wizer during initialization and will yield the same result + /// in every instance of the wizened module. + pub fn make_linker( + &mut self, + make_linker: Option anyhow::Result>>, + ) -> anyhow::Result<&mut Self> { + anyhow::ensure!( + !self.allow_wasi, + "Cannot use 'allow_wasi' with a custom linker" + ); + self.make_linker = make_linker; + Ok(self) + } + + /// When using WASI during initialization, should `stdin`, `stdout`, and + /// `stderr` be inherited? + /// + /// Defaults to `true`. + pub fn inherit_stdio(&mut self, inherit: bool) -> &mut Self { + self.inherit_stdio = Some(inherit); + self + } + + /// When using WASI during initialization, should the environment variables + /// be inherited? + /// + /// Defaults to `false`. + pub fn inherit_env(&mut self, inherit: bool) -> &mut Self { + self.inherit_env = Some(inherit); + self + } + + /// After initialization, should the Wasm module still export the + /// initialization function? + /// + /// This is `false` by default, meaning that the initialization function is + /// no longer exported from the Wasm module. + pub fn keep_init_func(&mut self, keep: bool) -> &mut Self { + self.keep_init_func = Some(keep); + self + } + + /// When using WASI during initialization, which file system directories + /// should be made available? + /// + /// None are available by default. + pub fn dir(&mut self, directory: impl Into) -> &mut Self { + self.dirs.push(directory.into()); + self + } + + /// When using WASI during initialization, which guest directories should be + /// mapped to a host directory? + /// + /// The `map_dir` method differs from `dir` in that it allows giving a custom + /// guest name to the directory that is different from its name in the host. + /// + /// None are mapped by default. + pub fn map_dir( + &mut self, + guest_dir: impl Into, + host_dir: impl Into, + ) -> &mut Self { + self.map_dirs.push((guest_dir.into(), host_dir.into())); + self + } + + /// Enable or disable the Wasm multi-memory proposal. + /// + /// Defaults to `true`. + pub fn wasm_multi_memory(&mut self, enable: bool) -> &mut Self { + self.wasm_multi_memory = Some(enable); + self + } + + /// Enable or disable the Wasm multi-value proposal. + /// + /// Defaults to `true`. + pub fn wasm_multi_value(&mut self, enable: bool) -> &mut Self { + self.wasm_multi_value = Some(enable); + self + } + + /// Enable or disable Wasm bulk memory operations. + /// + /// Note that only `memory.copy`, `memory.fill`, and `memory.init` + /// operations are currently supported. Modules which use other + /// instructions, such as `table.copy` will be rejected. + /// + /// Defaults to `true`. + pub fn wasm_bulk_memory(&mut self, enable: bool) -> &mut Self { + self.wasm_bulk_memory = Some(enable); + self + } + + /// Enable or disable the Wasm SIMD128 proposal. + /// + /// Defaults to `true`. + pub fn wasm_simd(&mut self, enable: bool) -> &mut Self { + self.wasm_simd = Some(enable); + self + } + + /// Enable or disable the Wasm reference-types proposal. + /// + /// Currently does not implement snapshotting or the use of references, + /// but enables initializing Wasm modules that use encodings introduced + /// in the reference-types proposal. + /// + /// Defaults to `true`. + pub fn wasm_reference_types(&mut self, enable: bool) -> &mut Self { + self.wasm_reference_types = Some(enable); + self + } + + /// Initialize the given Wasm, snapshot it, and return the serialized + /// snapshot as a new, pre-initialized Wasm module. + pub fn run(&self, wasm: &[u8]) -> anyhow::Result> { + // Parse rename spec. + let renames = FuncRenames::parse(&self.func_renames)?; + + // Make sure we're given valid Wasm from the get go. + self.wasm_validate(&wasm)?; + + let mut cx = parse::parse(wasm)?; + let instrumented_wasm = instrument::instrument(&cx); + + if cfg!(debug_assertions) { + if let Err(error) = self.wasm_validate(&instrumented_wasm) { + #[cfg(feature = "wasmprinter")] + let wat = wasmprinter::print_bytes(&wasm) + .unwrap_or_else(|e| format!("Disassembling to WAT failed: {}", e)); + #[cfg(not(feature = "wasmprinter"))] + let wat = "`wasmprinter` cargo feature is not enabled".to_string(); + panic!( + "instrumented Wasm is not valid: {:?}\n\nWAT:\n{}", + error, wat + ); + } + } + + let config = self.wasmtime_config()?; + let engine = wasmtime::Engine::new(&config)?; + let wasi_ctx = self.wasi_context()?; + let mut store = wasmtime::Store::new(&engine, StoreData { wasi_ctx }); + let module = wasmtime::Module::new(&engine, &instrumented_wasm) + .context("failed to compile the Wasm module")?; + self.validate_init_func(&module)?; + + let (instance, has_wasi_initialize) = self.initialize(&engine, &mut store, &module)?; + let snapshot = snapshot::snapshot(&mut store, &instance); + let rewritten_wasm = self.rewrite( + &mut cx, + &mut store, + &snapshot, + &renames, + has_wasi_initialize, + ); + + if cfg!(debug_assertions) { + if let Err(error) = self.wasm_validate(&rewritten_wasm) { + #[cfg(feature = "wasmprinter")] + let wat = wasmprinter::print_bytes(&wasm) + .unwrap_or_else(|e| format!("Disassembling to WAT failed: {}", e)); + #[cfg(not(feature = "wasmprinter"))] + let wat = "`wasmprinter` cargo feature is not enabled".to_string(); + panic!("rewritten Wasm is not valid: {:?}\n\nWAT:\n{}", error, wat); + } + } + + Ok(rewritten_wasm) + } + + // NB: keep this in sync with the wasmparser features. + fn wasmtime_config(&self) -> anyhow::Result { + let mut config = wasmtime::Config::new(); + + // Enable Wasmtime's code cache. This makes it so that repeated + // wizenings of the same Wasm module (e.g. with different WASI inputs) + // doesn't require re-compiling the Wasm to native code every time. + wasmtime::Cache::from_file(None).map(|cache| config.cache(Some(cache)))?; + + // Proposals we support. + config.wasm_multi_memory(self.wasm_multi_memory.unwrap_or(DEFAULT_WASM_MULTI_MEMORY)); + config.wasm_multi_value(self.wasm_multi_value.unwrap_or(DEFAULT_WASM_MULTI_VALUE)); + // Note that we only support `memory.copy`, `memory.fill`, and + // `memory.init` for the time being: + config.wasm_bulk_memory(self.wasm_bulk_memory.unwrap_or(DEFAULT_WASM_BULK_MEMORY)); + + config.wasm_simd(self.wasm_simd.unwrap_or(DEFAULT_WASM_SIMD)); + + // Note that reference_types are not actually supported, + // but many compilers now enable them by default + config.wasm_reference_types( + self.wasm_reference_types + .unwrap_or(DEFAULT_WASM_REFERENCE_TYPES), + ); + + config.wasm_tail_call(true); + config.wasm_extended_const(true); + + // Proposals that we should add support for. + config.wasm_threads(false); + + Ok(config) + } + + // NB: keep this in sync with the Wasmtime config. + fn wasm_features(&self) -> wasmparser::WasmFeatures { + let mut features = WasmFeatures::WASM2; + + features.set( + WasmFeatures::MULTI_MEMORY, + self.wasm_multi_memory.unwrap_or(DEFAULT_WASM_MULTI_MEMORY), + ); + features.set( + WasmFeatures::MULTI_VALUE, + self.wasm_multi_value.unwrap_or(DEFAULT_WASM_MULTI_VALUE), + ); + features.set( + WasmFeatures::BULK_MEMORY, + self.wasm_bulk_memory.unwrap_or(DEFAULT_WASM_BULK_MEMORY), + ); + features.set( + WasmFeatures::SIMD, + self.wasm_simd.unwrap_or(DEFAULT_WASM_SIMD), + ); + features.set( + WasmFeatures::REFERENCE_TYPES, + self.wasm_reference_types + .unwrap_or(DEFAULT_WASM_REFERENCE_TYPES), + ); + + features.set(WasmFeatures::TAIL_CALL, true); + features.set(WasmFeatures::EXTENDED_CONST, true); + + return features; + } + + fn wasm_validate(&self, wasm: &[u8]) -> anyhow::Result<()> { + log::debug!("Validating input Wasm"); + + let mut validator = wasmparser::Validator::new_with_features(self.wasm_features()); + validator.validate_all(wasm)?; + + // Reject bulk memory stuff that manipulates state we don't + // snapshot. See the comment inside `wasm_features`. + let mut wasm = wasm; + let mut parsers = vec![wasmparser::Parser::new(0)]; + while !parsers.is_empty() { + let payload = match parsers.last_mut().unwrap().parse(wasm, true).unwrap() { + wasmparser::Chunk::NeedMoreData(_) => unreachable!(), + wasmparser::Chunk::Parsed { consumed, payload } => { + wasm = &wasm[consumed..]; + payload + } + }; + match payload { + wasmparser::Payload::CodeSectionEntry(code) => { + let mut ops = code.get_operators_reader().unwrap(); + while !ops.eof() { + match ops.read().unwrap() { + wasmparser::Operator::TableCopy { .. } => { + anyhow::bail!("unsupported `table.copy` instruction") + } + wasmparser::Operator::TableInit { .. } => { + anyhow::bail!("unsupported `table.init` instruction") + } + wasmparser::Operator::ElemDrop { .. } => { + anyhow::bail!("unsupported `elem.drop` instruction") + } + wasmparser::Operator::DataDrop { .. } => { + anyhow::bail!("unsupported `data.drop` instruction") + } + wasmparser::Operator::TableSet { .. } => { + anyhow::bail!("unsupported `table.set` instruction") + } + wasmparser::Operator::TableGet { .. } => { + anyhow::bail!("unsupported `table.get` instruction") + } + wasmparser::Operator::RefNull { .. } => { + anyhow::bail!("unsupported `ref.null` instruction") + } + wasmparser::Operator::RefIsNull => { + anyhow::bail!("unsupported `ref.is_null` instruction") + } + wasmparser::Operator::TypedSelect { .. } => { + anyhow::bail!("unsupported typed `select` instruction") + } + wasmparser::Operator::RefFunc { .. } => { + anyhow::bail!("unsupported `ref.func` instruction") + } + wasmparser::Operator::TableSize { .. } => { + anyhow::bail!("unsupported `table.size` instruction") + } + wasmparser::Operator::TableGrow { .. } => { + anyhow::bail!("unsupported `table.grow` instruction") + } + wasmparser::Operator::TableFill { .. } => { + anyhow::bail!("unsupported `table.fill` instruction") + } + _ => continue, + } + } + } + wasmparser::Payload::End(_) => { + parsers.pop(); + } + _ => continue, + } + } + + Ok(()) + } + + /// Check that the module exports an initialization function, and that the + /// function has the correct type. + fn validate_init_func(&self, module: &wasmtime::Module) -> anyhow::Result<()> { + log::debug!("Validating the exported initialization function"); + match module.get_export(&self.init_func) { + Some(wasmtime::ExternType::Func(func_ty)) => { + if func_ty.params().len() != 0 || func_ty.results().len() != 0 { + anyhow::bail!( + "the Wasm module's `{}` function export does not have type `[] -> []`", + &self.init_func + ); + } + } + Some(_) => anyhow::bail!( + "the Wasm module's `{}` export is not a function", + &self.init_func + ), + None => anyhow::bail!( + "the Wasm module does not have a `{}` export", + &self.init_func + ), + } + Ok(()) + } + + fn wasi_context(&self) -> anyhow::Result> { + if !self.allow_wasi { + return Ok(None); + } + + let mut ctx = WasiCtxBuilder::new(); + if self.inherit_stdio.unwrap_or(DEFAULT_INHERIT_STDIO) { + ctx.inherit_stdio(); + } + if self.inherit_env.unwrap_or(DEFAULT_INHERIT_ENV) { + ctx.inherit_env(); + } + for dir in &self.dirs { + log::debug!("Preopening directory: {}", dir.display()); + let guest = dir.display().to_string(); + ctx.preopened_dir( + dir, + guest, + wasmtime_wasi::DirPerms::all(), + wasmtime_wasi::FilePerms::all(), + ) + .with_context(|| format!("failed to open directory: {}", dir.display()))?; + } + for (guest_dir, host_dir) in &self.map_dirs { + log::debug!("Preopening directory: {guest_dir}::{}", host_dir.display()); + ctx.preopened_dir( + host_dir, + guest_dir, + wasmtime_wasi::DirPerms::all(), + wasmtime_wasi::FilePerms::all(), + ) + .with_context(|| format!("failed to open directory: {}", host_dir.display()))?; + } + Ok(Some(ctx.build_p1())) + } + + /// Preload a module. + fn do_preload( + &self, + engine: &Engine, + store: &mut Store, + linker: &mut Linker, + name: &str, + content: &[u8], + ) -> anyhow::Result<()> { + let module = + wasmtime::Module::new(engine, content).context("failed to parse preload module")?; + let instance = wasmtime::Instance::new(&mut *store, &module, &[]) + .context("failed to instantiate preload module")?; + linker + .instance(&mut *store, name, instance) + .context("failed to add preload's exports to linker")?; + Ok(()) + } + + /// Instantiate the module and call its initialization function. + fn initialize( + &self, + engine: &Engine, + store: &mut Store, + module: &wasmtime::Module, + ) -> anyhow::Result<(wasmtime::Instance, bool)> { + log::debug!("Calling the initialization function"); + + let mut linker = if let Some(make_linker) = self.make_linker.as_deref() { + make_linker(store.engine()).context("failed to make custom linker")? + } else { + wasmtime::Linker::new(store.engine()) + }; + + if self.allow_wasi { + preview1::add_to_linker_sync(&mut linker, |ctx: &mut StoreData| { + ctx.wasi_ctx.as_mut().unwrap() + })?; + } + + for preload in &self.preload { + if let Some((name, value)) = preload.split_once('=') { + let content = std::fs::read(value).context("failed to read preload module")?; + self.do_preload(engine, &mut *store, &mut linker, &name[..], &content[..])?; + } else { + anyhow::bail!( + "Bad preload option: {} (must be of form `name=file`)", + preload + ); + } + } + for (name, bytes) in &self.preload_bytes { + self.do_preload(engine, &mut *store, &mut linker, &name[..], &bytes[..])?; + } + + dummy_imports(&mut *store, &module, &mut linker)?; + + let instance = linker + .instantiate(&mut *store, module) + .context("failed to instantiate the Wasm module")?; + + let mut has_wasi_initialize = false; + + if let Some(export) = instance.get_export(&mut *store, "_initialize") { + if let Extern::Func(func) = export { + func.typed::<(), ()>(&store) + .and_then(|f| { + has_wasi_initialize = true; + f.call(&mut *store, ()).map_err(Into::into) + }) + .context("calling the Reactor initialization function")?; + + if self.init_func == "_initialize" && has_wasi_initialize { + // Don't run `_initialize` twice if the it was explicitly + // requested as the init function. + return Ok((instance, has_wasi_initialize)); + } + } + } + + let init_func = instance + .get_typed_func::<(), ()>(&mut *store, &self.init_func) + .expect("checked by `validate_init_func`"); + init_func + .call(&mut *store, ()) + .with_context(|| format!("the `{}` function trapped", self.init_func))?; + + Ok((instance, has_wasi_initialize)) + } +} diff --git a/crates/wizer/src/parse.rs b/crates/wizer/src/parse.rs new file mode 100644 index 000000000000..2ef58c82e694 --- /dev/null +++ b/crates/wizer/src/parse.rs @@ -0,0 +1,308 @@ +use crate::info::{ + types_interner::{EntityType, TypeId}, + Module, ModuleContext, +}; +use crate::stack_ext::StackExt; +use anyhow::{Context, Result}; +use std::convert::TryFrom; +use wasm_encoder::SectionId; + +struct StackEntry { + parser: wasmparser::Parser, + module: Module, +} + +/// Parse the given Wasm bytes into a `ModuleInfo` tree. +pub(crate) fn parse<'a>(full_wasm: &'a [u8]) -> anyhow::Result> { + log::debug!("Parsing the input Wasm"); + + let mut cx = ModuleContext::new(); + + // The wasm we are currently parsing. This is advanced as the parser + // consumes input. + let mut wasm = full_wasm; + + let mut stack = vec![StackEntry { + parser: wasmparser::Parser::new(0), + module: cx.root(), + }]; + + loop { + let (payload, consumed) = match stack + .top_mut() + .parser + .parse(wasm, true) + .context("failed to parse Wasm")? + { + wasmparser::Chunk::NeedMoreData(_) => unreachable!(), + wasmparser::Chunk::Parsed { payload, consumed } => (payload, consumed), + }; + wasm = &wasm[consumed..]; + + use wasmparser::Payload::*; + match payload { + Version { .. } => {} + TypeSection(types) => type_section(&mut cx, &mut stack, full_wasm, types)?, + ImportSection(imports) => import_section(&mut cx, &mut stack, full_wasm, imports)?, + FunctionSection(funcs) => function_section(&mut cx, &mut stack, full_wasm, funcs)?, + TableSection(tables) => table_section(&mut cx, &mut stack, full_wasm, tables)?, + MemorySection(mems) => memory_section(&mut cx, &mut stack, full_wasm, mems)?, + GlobalSection(globals) => global_section(&mut cx, &mut stack, full_wasm, globals)?, + ExportSection(exports) => export_section(&mut cx, &mut stack, full_wasm, exports)?, + StartSection { func: _, range } => { + stack + .top_mut() + .module + .add_raw_section(&mut cx, SectionId::Start, range, full_wasm) + } + ElementSection(elems) => stack.top_mut().module.add_raw_section( + &mut cx, + SectionId::Element, + elems.range(), + full_wasm, + ), + DataCountSection { range, .. } => stack.top_mut().module.add_raw_section( + &mut cx, + SectionId::DataCount, + range, + full_wasm, + ), + DataSection(data) => stack.top_mut().module.add_raw_section( + &mut cx, + SectionId::Data, + data.range(), + full_wasm, + ), + CustomSection(c) => stack.top_mut().module.add_raw_section( + &mut cx, + SectionId::Custom, + c.range(), + full_wasm, + ), + CodeSectionStart { + range, + count: _, + size, + } => { + wasm = &wasm[usize::try_from(size).unwrap()..]; + let entry = stack.top_mut(); + entry.parser.skip_section(); + entry + .module + .add_raw_section(&mut cx, SectionId::Code, range, full_wasm) + } + CodeSectionEntry(_) => unreachable!(), + UnknownSection { .. } => anyhow::bail!("unknown section"), + TagSection(_) => anyhow::bail!("exceptions are not supported yet"), + End(_) => { + let entry = stack.pop().unwrap(); + + // If we finished parsing the root Wasm module, then we're done. + assert!(entry.module.is_root()); + assert!(stack.is_empty()); + return Ok(cx); + } + ComponentTypeSection(_) + | ComponentImportSection(_) + | ComponentExportSection(_) + | ComponentStartSection { .. } + | ComponentAliasSection(_) + | CoreTypeSection(_) + | InstanceSection(_) + | ComponentInstanceSection(_) + | ComponentCanonicalSection(_) + | ModuleSection { .. } + | ComponentSection { .. } => { + unreachable!() + } + _ => anyhow::bail!("unsupported wasmparser payload"), + } + } +} + +fn type_section<'a>( + cx: &mut ModuleContext<'a>, + stack: &mut Vec, + full_wasm: &'a [u8], + types: wasmparser::TypeSectionReader<'a>, +) -> anyhow::Result<()> { + let module = stack.top().module; + module.add_raw_section(cx, SectionId::Type, types.range(), full_wasm); + + // Parse out types, as we will need them later when processing + // instance imports. + for group in types { + for ty in group?.into_types() { + match ty.composite_type.inner { + wasmparser::CompositeInnerType::Func(_) => { + module.push_type(cx, ty.composite_type); + } + wasmparser::CompositeInnerType::Array(_) => todo!(), + wasmparser::CompositeInnerType::Struct(_) => todo!(), + wasmparser::CompositeInnerType::Cont(_) => todo!(), + } + } + } + + Ok(()) +} + +fn import_section<'a>( + cx: &mut ModuleContext<'a>, + stack: &mut Vec, + full_wasm: &'a [u8], + imports: wasmparser::ImportSectionReader<'a>, +) -> anyhow::Result<()> { + let module = stack.top().module; + stack + .top_mut() + .module + .add_raw_section(cx, SectionId::Import, imports.range(), full_wasm); + + // Check that we can properly handle all imports. + for imp in imports { + let imp = imp?; + + if imp.module.starts_with("__wizer_") || imp.name.starts_with("__wizer_") { + anyhow::bail!( + "input Wasm module already imports entities named with the `__wizer_*` prefix" + ); + } + + check_import_type( + cx, + stack.top().module.types(cx), + stack.top().module.is_root(), + &module.entity_type(cx, imp.ty), + )?; + module.push_import(cx, imp); + } + Ok(()) +} + +fn check_import_type( + _cx: &ModuleContext, + _types: &[TypeId], + is_root: bool, + ty: &EntityType, +) -> Result<()> { + match ty { + EntityType::Function(_) => Ok(()), + EntityType::Memory(mem_ty) => { + anyhow::ensure!( + !mem_ty.shared, + "shared memories are not supported by Wizer yet" + ); + anyhow::ensure!( + !mem_ty.memory64, + "the memory64 proposal is not supported by Wizer yet" + ); + anyhow::ensure!( + !is_root, + "memory imports are not allowed in the root Wasm module" + ); + Ok(()) + } + EntityType::Table(_) | EntityType::Global(_) => { + anyhow::ensure!( + !is_root, + "table and global imports are not allowed in the root Wasm module" + ); + Ok(()) + } + } +} + +fn function_section<'a>( + cx: &mut ModuleContext<'a>, + stack: &mut Vec, + full_wasm: &'a [u8], + funcs: wasmparser::FunctionSectionReader<'a>, +) -> anyhow::Result<()> { + let module = stack.top().module; + module.add_raw_section(cx, SectionId::Function, funcs.range(), full_wasm); + + for ty_idx in funcs { + let ty = module.type_id_at(cx, ty_idx?); + module.push_function(cx, ty); + } + Ok(()) +} + +fn table_section<'a>( + cx: &mut ModuleContext<'a>, + stack: &mut Vec, + full_wasm: &'a [u8], + tables: wasmparser::TableSectionReader<'a>, +) -> anyhow::Result<()> { + let module = stack.top().module; + module.add_raw_section(cx, SectionId::Table, tables.range(), full_wasm); + + for table in tables { + module.push_table(cx, table?.ty); + } + Ok(()) +} + +fn memory_section<'a>( + cx: &mut ModuleContext<'a>, + stack: &mut Vec, + full_wasm: &'a [u8], + mems: wasmparser::MemorySectionReader<'a>, +) -> anyhow::Result<()> { + let module = stack.top().module; + module.add_raw_section(cx, SectionId::Memory, mems.range(), full_wasm); + + for m in mems { + module.push_defined_memory(cx, m?); + } + Ok(()) +} + +fn global_section<'a>( + cx: &mut ModuleContext<'a>, + stack: &mut Vec, + full_wasm: &'a [u8], + globals: wasmparser::GlobalSectionReader<'a>, +) -> anyhow::Result<()> { + let module = stack.top().module; + module.add_raw_section(cx, SectionId::Global, globals.range(), full_wasm); + + for g in globals { + module.push_defined_global(cx, g?.ty); + } + Ok(()) +} + +fn export_section<'a>( + cx: &mut ModuleContext<'a>, + stack: &mut Vec, + full_wasm: &'a [u8], + exports: wasmparser::ExportSectionReader<'a>, +) -> anyhow::Result<()> { + let module = stack.top().module; + module.add_raw_section(cx, SectionId::Export, exports.range(), full_wasm); + + for export in exports { + let export = export?; + + if export.name.starts_with("__wizer_") { + anyhow::bail!( + "input Wasm module already exports entities named with the `__wizer_*` prefix" + ); + } + + match export.kind { + wasmparser::ExternalKind::Tag => { + unreachable!("checked in validation") + } + wasmparser::ExternalKind::Func + | wasmparser::ExternalKind::Table + | wasmparser::ExternalKind::Memory + | wasmparser::ExternalKind::Global => { + module.push_export(cx, export); + } + } + } + Ok(()) +} diff --git a/crates/wizer/src/rewrite.rs b/crates/wizer/src/rewrite.rs new file mode 100644 index 000000000000..fbf39d7d7888 --- /dev/null +++ b/crates/wizer/src/rewrite.rs @@ -0,0 +1,176 @@ +//! Final rewrite pass. + +use crate::{ + info::ModuleContext, snapshot::Snapshot, translate, FuncRenames, Wizer, DEFAULT_KEEP_INIT_FUNC, +}; +use std::convert::TryFrom; +use wasm_encoder::{ConstExpr, SectionId}; + +impl Wizer { + /// Given the initialized snapshot, rewrite the Wasm so that it is already + /// initialized. + /// + pub(crate) fn rewrite( + &self, + cx: &mut ModuleContext<'_>, + store: &crate::Store, + snapshot: &Snapshot, + renames: &FuncRenames, + has_wasi_initialize: bool, + ) -> Vec { + log::debug!("Rewriting input Wasm to pre-initialized state"); + + let mut encoder = wasm_encoder::Module::new(); + let module = cx.root(); + + // Encode the initialized data segments from the snapshot rather + // than the original, uninitialized data segments. + let mut data_section = if snapshot.data_segments.is_empty() { + None + } else { + let mut data_section = wasm_encoder::DataSection::new(); + for seg in &snapshot.data_segments { + data_section.active( + seg.memory_index, + &ConstExpr::i32_const(seg.offset as i32), + seg.data(store).iter().copied(), + ); + } + Some(data_section) + }; + + // There are multiple places were we potentially need to check whether + // we've added the data section already and if we haven't yet, then do + // so. For example, the original Wasm might not have a data section at + // all, and so we have to potentially add it at the end of iterating + // over the original sections. This closure encapsulates all that + // add-it-if-we-haven't-already logic in one place. + let mut add_data_section = |module: &mut wasm_encoder::Module| { + if let Some(data_section) = data_section.take() { + module.section(&data_section); + } + }; + + for section in module.raw_sections(cx) { + match section { + // Some tools expect the name custom section to come last, even + // though custom sections are allowed in any order. Therefore, + // make sure we've added our data section by now. + s if is_name_section(s) => { + add_data_section(&mut encoder); + encoder.section(s); + } + + // For the memory section, we update the minimum size of each + // defined memory to the snapshot's initialized size for that + // memory. + s if s.id == u8::from(SectionId::Memory) => { + let mut memories = wasm_encoder::MemorySection::new(); + assert_eq!(module.defined_memories_len(cx), snapshot.memory_mins.len()); + for ((_, mem), new_min) in module + .defined_memories(cx) + .zip(snapshot.memory_mins.iter().copied()) + { + let mut mem = translate::memory_type(mem); + mem.minimum = new_min; + memories.memory(mem); + } + encoder.section(&memories); + } + + // Encode the initialized global values from the snapshot, + // rather than the original values. + s if s.id == u8::from(SectionId::Global) => { + let mut globals = wasm_encoder::GlobalSection::new(); + for ((_, glob_ty), val) in + module.defined_globals(cx).zip(snapshot.globals.iter()) + { + let glob_ty = translate::global_type(glob_ty); + globals.global( + glob_ty, + &match val { + wasmtime::Val::I32(x) => ConstExpr::i32_const(*x), + wasmtime::Val::I64(x) => ConstExpr::i64_const(*x), + wasmtime::Val::F32(x) => { + ConstExpr::f32_const(wasm_encoder::Ieee32::new(*x)) + } + wasmtime::Val::F64(x) => { + ConstExpr::f64_const(wasm_encoder::Ieee64::new(*x)) + } + wasmtime::Val::V128(x) => { + ConstExpr::v128_const(x.as_u128() as i128) + } + _ => unreachable!(), + }, + ); + } + encoder.section(&globals); + } + + // Remove exports for the wizer initialization + // function and WASI reactor _initialize function, + // then perform any requested renames. + s if s.id == u8::from(SectionId::Export) => { + let mut exports = wasm_encoder::ExportSection::new(); + for export in module.exports(cx) { + if (export.name == self.init_func + && !self.keep_init_func.unwrap_or(DEFAULT_KEEP_INIT_FUNC)) + || (has_wasi_initialize && export.name == "_initialize") + { + continue; + } + + if !renames.rename_src_to_dst.contains_key(export.name) + && renames.rename_dsts.contains(export.name) + { + // A rename overwrites this export, and it is not + // renamed to another export, so skip it. + continue; + } + + let field = renames + .rename_src_to_dst + .get(export.name) + .map_or(export.name, |f| f.as_str()); + + let kind = translate::export(export.kind); + exports.export(field, kind, export.index); + } + encoder.section(&exports); + } + + // Skip the `start` function -- it's already been run! + s if s.id == u8::from(SectionId::Start) => { + continue; + } + + s if s.id == u8::from(SectionId::DataCount) => { + encoder.section(&wasm_encoder::DataCountSection { + count: u32::try_from(snapshot.data_segments.len()).unwrap(), + }); + } + + s if s.id == u8::from(SectionId::Data) => { + // TODO: supporting bulk memory will require copying over + // any passive and declared segments. + add_data_section(&mut encoder); + } + + s => { + encoder.section(s); + } + } + } + + // Make sure that we've added our data section to the module. + add_data_section(&mut encoder); + encoder.finish() + } +} + +fn is_name_section(s: &wasm_encoder::RawSection) -> bool { + s.id == u8::from(SectionId::Custom) && { + let mut reader = wasmparser::BinaryReader::new(s.data, 0); + matches!(reader.read_string(), Ok("name")) + } +} diff --git a/crates/wizer/src/snapshot.rs b/crates/wizer/src/snapshot.rs new file mode 100644 index 000000000000..d3c5fcd36ea0 --- /dev/null +++ b/crates/wizer/src/snapshot.rs @@ -0,0 +1,270 @@ +use rayon::iter::{IntoParallelIterator, ParallelExtend, ParallelIterator}; +use std::convert::TryFrom; +use wasmtime::{AsContext, AsContextMut}; + +const WASM_PAGE_SIZE: u64 = 65_536; + +/// The maximum number of data segments that we will emit. Most +/// engines support more than this, but we want to leave some +/// headroom. +const MAX_DATA_SEGMENTS: usize = 10_000; + +/// A "snapshot" of Wasm state from its default value after having been initialized. +pub struct Snapshot { + /// Maps global index to its initialized value. + pub globals: Vec, + + /// A new minimum size for each memory (in units of pages). + pub memory_mins: Vec, + + /// Segments of non-zero memory. + pub data_segments: Vec, +} + +/// A data segment initializer for a memory. +#[derive(Clone, Copy)] +pub struct DataSegment { + /// The index of this data segment's memory. + pub memory_index: u32, + + /// This data segment's initialized memory that it originated from. + pub memory: wasmtime::Memory, + + /// The offset within the memory that `data` should be copied to. + pub offset: u32, + + /// This segment's length. + pub len: u32, +} + +impl DataSegment { + pub fn data<'a>(&self, ctx: &'a impl AsContext) -> &'a [u8] { + let start = usize::try_from(self.offset).unwrap(); + let end = start + usize::try_from(self.len).unwrap(); + &self.memory.data(ctx)[start..end] + } +} + +impl DataSegment { + /// What is the gap between two consecutive data segments? + /// + /// `self` must be in front of `other` and they must not overlap with each + /// other. + fn gap(&self, other: &Self) -> u32 { + debug_assert_eq!(self.memory_index, other.memory_index); + debug_assert!(self.offset + self.len <= other.offset); + other.offset - (self.offset + self.len) + } + + /// Merge two consecutive data segments. + /// + /// `self` must be in front of `other` and they must not overlap with each + /// other. + fn merge(&self, other: &Self) -> DataSegment { + let gap = self.gap(other); + + DataSegment { + offset: self.offset, + len: self.len + gap + other.len, + ..*self + } + } +} + +/// Snapshot the given instance's globals, memories, and instances from the Wasm +/// defaults. +// +// TODO: when we support reference types, we will have to snapshot tables. +pub fn snapshot(ctx: &mut impl AsContextMut, instance: &wasmtime::Instance) -> Snapshot { + log::debug!("Snapshotting the initialized state"); + + let globals = snapshot_globals(&mut *ctx, instance); + let (memory_mins, data_segments) = snapshot_memories(&mut *ctx, instance); + + Snapshot { + globals, + memory_mins, + data_segments, + } +} + +/// Get the initialized values of all globals. +fn snapshot_globals( + ctx: &mut impl AsContextMut, + instance: &wasmtime::Instance, +) -> Vec { + log::debug!("Snapshotting global values"); + let mut globals = vec![]; + let mut index = 0; + loop { + let name = format!("__wizer_global_{}", index); + match instance.get_global(&mut *ctx, &name) { + None => break, + Some(global) => { + globals.push(global.get(&mut *ctx)); + index += 1; + } + } + } + globals +} + +/// Find the initialized minimum page size of each memory, as well as all +/// regions of non-zero memory. +fn snapshot_memories( + ctx: &mut impl AsContextMut, + instance: &wasmtime::Instance, +) -> (Vec, Vec) { + log::debug!("Snapshotting memories"); + + // Find and record non-zero regions of memory (in parallel). + let mut memory_mins = vec![]; + let mut data_segments = vec![]; + let mut memory_index = 0; + loop { + let name = format!("__wizer_memory_{}", memory_index); + let memory = match instance.get_memory(&mut *ctx, &name) { + None => break, + Some(memory) => memory, + }; + memory_mins.push(memory.size(&*ctx)); + + let num_wasm_pages = memory.size(&*ctx); + + let memory_data = memory.data(&*ctx); + + // Consider each Wasm page in parallel. Create data segments for each + // region of non-zero memory. + data_segments.par_extend((0..num_wasm_pages).into_par_iter().flat_map(|i| { + let page_end = ((i + 1) * WASM_PAGE_SIZE) as usize; + let mut start = (i * WASM_PAGE_SIZE) as usize; + let mut segments = vec![]; + while start < page_end { + let nonzero = match memory_data[start..page_end] + .iter() + .position(|byte| *byte != 0) + { + None => break, + Some(i) => i, + }; + start += nonzero; + let end = memory_data[start..page_end] + .iter() + .position(|byte| *byte == 0) + .map_or(page_end, |zero| start + zero); + segments.push(DataSegment { + memory_index, + memory, + offset: u32::try_from(start).unwrap(), + len: u32::try_from(end - start).unwrap(), + }); + start = end; + } + segments + })); + + memory_index += 1; + } + + if data_segments.is_empty() { + return (memory_mins, data_segments); + } + + // Sort data segments to enforce determinism in the face of the + // parallelism above. + data_segments.sort_by_key(|s| (s.memory_index, s.offset)); + + // Merge any contiguous segments (caused by spanning a Wasm page boundary, + // and therefore created in separate logical threads above) or pages that + // are within four bytes of each other. Four because this is the minimum + // overhead of defining a new active data segment: one for the memory index + // LEB, two for the memory offset init expression (one for the `i32.const` + // opcode and another for the constant immediate LEB), and finally one for + // the data length LEB). + const MIN_ACTIVE_SEGMENT_OVERHEAD: u32 = 4; + let mut merged_data_segments = Vec::with_capacity(data_segments.len()); + merged_data_segments.push(data_segments[0]); + for b in &data_segments[1..] { + let a = merged_data_segments.last_mut().unwrap(); + + // Only merge segments for the same memory. + if a.memory_index != b.memory_index { + merged_data_segments.push(*b); + continue; + } + + // Only merge segments if they are contiguous or if it is definitely + // more size efficient than leaving them apart. + let gap = a.gap(b); + if gap > MIN_ACTIVE_SEGMENT_OVERHEAD { + merged_data_segments.push(*b); + continue; + } + + // Okay, merge them together into `a` (so that the next iteration can + // merge it with its predecessor) and then omit `b`! + let merged = a.merge(b); + *a = merged; + } + + remove_excess_segments(&mut merged_data_segments); + + (memory_mins, merged_data_segments) +} + +/// Engines apply a limit on how many segments a module may contain, and Wizer +/// can run afoul of it. When that happens, we need to merge data segments +/// together until our number of data segments fits within the limit. +fn remove_excess_segments(merged_data_segments: &mut Vec) { + if merged_data_segments.len() < MAX_DATA_SEGMENTS { + return; + } + + // We need to remove `excess` number of data segments. + let excess = merged_data_segments.len() - MAX_DATA_SEGMENTS; + + #[derive(Clone, Copy, PartialEq, Eq)] + struct GapIndex { + gap: u32, + // Use a `u32` instead of `usize` to fit `GapIndex` within a word on + // 64-bit systems, using less memory. + index: u32, + } + + // Find the gaps between the start of one segment and the next (if they are + // both in the same memory). We will merge the `excess` segments with the + // smallest gaps together. Because they are the smallest gaps, this will + // bloat the size of our data segment the least. + let mut smallest_gaps = Vec::with_capacity(merged_data_segments.len() - 1); + for (index, w) in merged_data_segments.windows(2).enumerate() { + if w[0].memory_index != w[1].memory_index { + continue; + } + let gap = w[0].gap(&w[1]); + let index = u32::try_from(index).unwrap(); + smallest_gaps.push(GapIndex { gap, index }); + } + smallest_gaps.sort_unstable_by_key(|g| g.gap); + smallest_gaps.truncate(excess); + + // Now merge the chosen segments together in reverse index order so that + // merging two segments doesn't mess up the index of the next segments we + // will to merge. + smallest_gaps.sort_unstable_by(|a, b| a.index.cmp(&b.index).reverse()); + for GapIndex { index, .. } in smallest_gaps { + let index = usize::try_from(index).unwrap(); + let merged = merged_data_segments[index].merge(&merged_data_segments[index + 1]); + merged_data_segments[index] = merged; + + // Okay to use `swap_remove` here because, even though it makes + // `merged_data_segments` unsorted, the segments are still sorted within + // the range `0..index` and future iterations will only operate within + // that subregion because we are iterating over largest to smallest + // indices. + merged_data_segments.swap_remove(index + 1); + } + + // Finally, sort the data segments again so that our output is + // deterministic. + merged_data_segments.sort_by_key(|s| (s.memory_index, s.offset)); +} diff --git a/crates/wizer/src/stack_ext.rs b/crates/wizer/src/stack_ext.rs new file mode 100644 index 000000000000..e5ec5a0382ac --- /dev/null +++ b/crates/wizer/src/stack_ext.rs @@ -0,0 +1,16 @@ +/// An extension trait for getting the top element on a stack with an implicit +/// unwrap for if the stack is empty. +pub(crate) trait StackExt { + fn top(&self) -> &T; + fn top_mut(&mut self) -> &mut T; +} + +impl StackExt for Vec { + fn top(&self) -> &T { + self.last().unwrap() + } + + fn top_mut(&mut self) -> &mut T { + self.last_mut().unwrap() + } +} diff --git a/crates/wizer/src/translate.rs b/crates/wizer/src/translate.rs new file mode 100644 index 000000000000..6b0addc101c3 --- /dev/null +++ b/crates/wizer/src/translate.rs @@ -0,0 +1,43 @@ +//! Type translator functions from `wasmparser` to `wasm_encoder`. + +pub(crate) fn val_type(ty: wasmparser::ValType) -> wasm_encoder::ValType { + use wasm_encoder::ValType; + use wasmparser::ValType::*; + match ty { + I32 => ValType::I32, + I64 => ValType::I64, + F32 => ValType::F32, + F64 => ValType::F64, + V128 => ValType::V128, + wasmparser::ValType::FUNCREF => ValType::FUNCREF, + Ref(_) => panic!("not supported"), + } +} + +pub(crate) fn global_type(ty: wasmparser::GlobalType) -> wasm_encoder::GlobalType { + wasm_encoder::GlobalType { + val_type: val_type(ty.content_type), + mutable: ty.mutable, + shared: ty.shared, + } +} + +pub(crate) fn memory_type(ty: wasmparser::MemoryType) -> wasm_encoder::MemoryType { + wasm_encoder::MemoryType { + minimum: ty.initial.into(), + maximum: ty.maximum.map(|val| val.into()), + memory64: ty.memory64, + shared: ty.shared, + page_size_log2: None, + } +} + +pub(crate) fn export(kind: wasmparser::ExternalKind) -> wasm_encoder::ExportKind { + match kind { + wasmparser::ExternalKind::Func => wasm_encoder::ExportKind::Func, + wasmparser::ExternalKind::Global => wasm_encoder::ExportKind::Global, + wasmparser::ExternalKind::Table => wasm_encoder::ExportKind::Table, + wasmparser::ExternalKind::Memory => wasm_encoder::ExportKind::Memory, + wasmparser::ExternalKind::Tag => unreachable!(), + } +} diff --git a/crates/wizer/tests/make_linker.rs b/crates/wizer/tests/make_linker.rs new file mode 100644 index 000000000000..8147692a065b --- /dev/null +++ b/crates/wizer/tests/make_linker.rs @@ -0,0 +1,124 @@ +use anyhow::{anyhow, Context, Result}; +use std::rc::Rc; +use wasmtime_wasi::WasiCtxBuilder; +use wat::parse_str as wat_to_wasm; +use wizer::Wizer; + +fn get_wizer() -> Wizer { + let mut wizer = Wizer::new(); + wizer + .make_linker(Some(Rc::new(|e: &wasmtime::Engine| { + let mut linker = wasmtime::Linker::new(e); + linker.func_wrap("foo", "bar", |x: i32| x + 1)?; + Ok(linker) + }))) + .unwrap(); + wizer +} + +fn run_wasm(args: &[wasmtime::Val], expected: i32, wasm: &[u8]) -> Result<()> { + let _ = env_logger::try_init(); + + let wasm = get_wizer().run(&wasm)?; + log::debug!( + "=== Wizened Wasm ==========================================================\n\ + {}\n\ + ===========================================================================", + wasmprinter::print_bytes(&wasm).unwrap() + ); + if log::log_enabled!(log::Level::Debug) { + std::fs::write("test.wasm", &wasm).unwrap(); + } + + let mut config = wasmtime::Config::new(); + wasmtime::Cache::from_file(None) + .map(|cache| config.cache(Some(cache))) + .unwrap(); + config.wasm_multi_memory(true); + config.wasm_multi_value(true); + + let engine = wasmtime::Engine::new(&config)?; + let wasi_ctx = WasiCtxBuilder::new().build_p1(); + let mut store = wasmtime::Store::new(&engine, wasi_ctx); + let module = + wasmtime::Module::new(store.engine(), wasm).context("Wasm test case failed to compile")?; + + let mut linker = wasmtime::Linker::new(&engine); + linker.func_wrap("foo", "bar", |_: i32| -> Result { + Err(anyhow!("shouldn't be called")) + })?; + + let instance = linker.instantiate(&mut store, &module)?; + + let run = instance + .get_func(&mut store, "run") + .ok_or_else(|| anyhow::anyhow!("the test Wasm module does not export a `run` function"))?; + + let mut actual = vec![wasmtime::Val::I32(0)]; + run.call(&mut store, args, &mut actual)?; + anyhow::ensure!(actual.len() == 1, "expected one result"); + let actual = match actual[0] { + wasmtime::Val::I32(x) => x, + _ => anyhow::bail!("expected an i32 result"), + }; + anyhow::ensure!( + expected == actual, + "expected `{}`, found `{}`", + expected, + actual, + ); + + Ok(()) +} + +fn run_wat(args: &[wasmtime::Val], expected: i32, wat: &str) -> Result<()> { + let _ = env_logger::try_init(); + let wasm = wat_to_wasm(wat)?; + run_wasm(args, expected, &wasm) +} + +#[test] +fn custom_linker() -> Result<()> { + run_wat( + &[], + 1, + r#" +(module + (type (func (param i32) (result i32))) + (import "foo" "bar" (func (type 0))) + (global $g (mut i32) (i32.const 0)) + (func (export "wizer.initialize") + global.get $g + call 0 + global.set $g + ) + (func (export "run") (result i32) + (global.get $g) + ) +)"#, + ) +} + +#[test] +#[should_panic] +fn linker_and_wasi() { + Wizer::new() + .make_linker(Some(Rc::new(|e: &wasmtime::Engine| { + Ok(wasmtime::Linker::new(e)) + }))) + .unwrap() + .allow_wasi(true) + .unwrap(); +} + +#[test] +#[should_panic] +fn wasi_and_linker() { + Wizer::new() + .allow_wasi(true) + .unwrap() + .make_linker(Some(Rc::new(|e: &wasmtime::Engine| { + Ok(wasmtime::Linker::new(e)) + }))) + .unwrap(); +} diff --git a/crates/wizer/tests/preloads.rs b/crates/wizer/tests/preloads.rs new file mode 100644 index 000000000000..74d5deff3d3b --- /dev/null +++ b/crates/wizer/tests/preloads.rs @@ -0,0 +1,80 @@ +use anyhow::Result; +use wat::parse_str as wat_to_wasm; +use wizer::Wizer; + +const PRELOAD1: &'static str = r#" +(module + (func (export "f") (param i32) (result i32) + local.get 0 + i32.const 1 + i32.add)) + "#; + +const PRELOAD2: &'static str = r#" +(module + (func (export "f") (param i32) (result i32) + local.get 0 + i32.const 2 + i32.add)) + "#; + +fn run_with_preloads(args: &[wasmtime::Val], wat: &str) -> Result { + let wasm = wat_to_wasm(wat)?; + let mut w = Wizer::new(); + w.preload_bytes("mod1", PRELOAD1.as_bytes().to_vec())?; + w.preload_bytes("mod2", PRELOAD2.as_bytes().to_vec())?; + let processed = w.run(&wasm)?; + + let engine = wasmtime::Engine::default(); + let mut store = wasmtime::Store::new(&engine, ()); + + let mod1 = wasmtime::Module::new(&engine, PRELOAD1.as_bytes())?; + let mod2 = wasmtime::Module::new(&engine, PRELOAD2.as_bytes())?; + let testmod = wasmtime::Module::new(&engine, &processed[..])?; + + let mod1_inst = wasmtime::Instance::new(&mut store, &mod1, &[])?; + let mod2_inst = wasmtime::Instance::new(&mut store, &mod2, &[])?; + let mut linker = wasmtime::Linker::new(&engine); + linker.instance(&mut store, "mod1", mod1_inst)?; + linker.instance(&mut store, "mod2", mod2_inst)?; + + let inst = linker.instantiate(&mut store, &testmod)?; + let run = inst + .get_func(&mut store, "run") + .ok_or_else(|| anyhow::anyhow!("no `run` function on test module"))?; + let mut returned = vec![wasmtime::Val::I32(0)]; + run.call(&mut store, args, &mut returned)?; + Ok(returned[0].clone()) +} + +#[test] +fn test_preloads() { + const WAT: &'static str = r#" + (module + (import "mod1" "f" (func $mod1f (param i32) (result i32))) + (import "mod2" "f" (func $mod2f (param i32) (result i32))) + (global $g1 (mut i32) (i32.const 0)) + (global $g2 (mut i32) (i32.const 0)) + (func (export "wizer.initialize") + i32.const 100 + call $mod1f + global.set $g1 + i32.const 100 + call $mod2f + global.set $g2) + (func (export "run") (param i32 i32) (result i32) + local.get 0 + call $mod1f + local.get 1 + call $mod2f + i32.add + global.get $g1 + global.get $g2 + i32.add + i32.add)) + "#; + + let result = + run_with_preloads(&[wasmtime::Val::I32(200), wasmtime::Val::I32(201)], WAT).unwrap(); + assert!(matches!(result, wasmtime::Val::I32(607))); +} diff --git a/crates/wizer/tests/regex-test/Cargo.toml b/crates/wizer/tests/regex-test/Cargo.toml new file mode 100644 index 000000000000..43b7575aa1d2 --- /dev/null +++ b/crates/wizer/tests/regex-test/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "regex-test" +version = "0.1.0" +authors = ["Nick Fitzgerald "] +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +regex = "1.10.4" diff --git a/crates/wizer/tests/regex-test/README.md b/crates/wizer/tests/regex-test/README.md new file mode 100644 index 000000000000..103b4d203c5b --- /dev/null +++ b/crates/wizer/tests/regex-test/README.md @@ -0,0 +1,8 @@ +Source code used to create `/wizer/tests/regex_test.wasm`. + +Rebuild with: + +``` +$ cargo build --release --target wasm32-wasi -p regex-test +$ cp target/wasm32-wasi/release/regex_test.wasm tests/regex_test.wasm +``` diff --git a/crates/wizer/tests/regex-test/src/lib.rs b/crates/wizer/tests/regex-test/src/lib.rs new file mode 100644 index 000000000000..1d71433f7a9e --- /dev/null +++ b/crates/wizer/tests/regex-test/src/lib.rs @@ -0,0 +1,22 @@ +use regex::Regex; + +/// A regex that matches numbers that start with "1". +static mut REGEX: Option = None; + +#[export_name = "wizer.initialize"] +pub fn init() { + unsafe { + REGEX = Some(Regex::new(r"^1\d*$").unwrap()); + } +} + +#[no_mangle] +pub fn run(n: i32) -> i32 { + let s = format!("{}", n); + let regex = unsafe { REGEX.as_ref().unwrap() }; + if regex.is_match(&s) { + 42 + } else { + 0 + } +} diff --git a/crates/wizer/tests/tests.rs b/crates/wizer/tests/tests.rs new file mode 100644 index 000000000000..a6ea92e0611e --- /dev/null +++ b/crates/wizer/tests/tests.rs @@ -0,0 +1,831 @@ +use anyhow::{Context, Result}; +use wasm_encoder::ConstExpr; +use wasmtime_wasi::{preview1, WasiCtxBuilder}; +use wat::parse_str as wat_to_wasm; +use wizer::{StoreData, Wizer}; + +fn run_wat(args: &[wasmtime::Val], expected: i32, wat: &str) -> Result<()> { + let _ = env_logger::try_init(); + let wasm = wat_to_wasm(wat)?; + wizen_and_run_wasm(args, expected, &wasm, get_wizer()) +} + +fn get_wizer() -> Wizer { + let mut wizer = Wizer::new(); + wizer.allow_wasi(true).unwrap(); + wizer.wasm_multi_memory(true); + wizer.wasm_bulk_memory(true); + wizer +} + +fn wizen_and_run_wasm( + args: &[wasmtime::Val], + expected: i32, + wasm: &[u8], + wizer: Wizer, +) -> Result<()> { + let _ = env_logger::try_init(); + + log::debug!( + "=== PreWizened Wasm ==========================================================\n\ + {}\n\ + ===========================================================================", + wasmprinter::print_bytes(&wasm).unwrap() + ); + let wasm = wizer.run(&wasm)?; + log::debug!( + "=== Wizened Wasm ==========================================================\n\ + {}\n\ + ===========================================================================", + wasmprinter::print_bytes(&wasm).unwrap() + ); + if log::log_enabled!(log::Level::Debug) { + std::fs::write("test.wasm", &wasm).unwrap(); + } + + let mut config = wasmtime::Config::new(); + wasmtime::Cache::from_file(None) + .map(|cache| config.cache(Some(cache))) + .unwrap(); + config.wasm_multi_memory(true); + config.wasm_multi_value(true); + + let engine = wasmtime::Engine::new(&config)?; + let wasi_ctx = WasiCtxBuilder::new().build_p1(); + let mut store = wasmtime::Store::new( + &engine, + StoreData { + wasi_ctx: Some(wasi_ctx), + }, + ); + let module = + wasmtime::Module::new(store.engine(), wasm).context("Wasm test case failed to compile")?; + + let mut linker = wasmtime::Linker::new(&engine); + let thunk = wasmtime::Func::wrap(&mut store, || {}); + linker + .define_name(&mut store, "dummy_func", thunk)? + .define(&mut store, "env", "f", thunk)? + .define_name(&mut store, "f", thunk)? + .define(&mut store, "x", "f", thunk)?; + + preview1::add_to_linker_sync(&mut linker, |wasi| wasi.wasi_ctx.as_mut().unwrap())?; + + let instance = linker.instantiate(&mut store, &module)?; + + let run = instance + .get_func(&mut store, "run") + .ok_or_else(|| anyhow::anyhow!("the test Wasm module does not export a `run` function"))?; + + let mut actual = vec![wasmtime::Val::I32(0)]; + run.call(&mut store, args, &mut actual)?; + anyhow::ensure!(actual.len() == 1, "expected one result"); + let actual = match actual[0] { + wasmtime::Val::I32(x) => x, + _ => anyhow::bail!("expected an i32 result"), + }; + anyhow::ensure!( + expected == actual, + "expected `{}`, found `{}`", + expected, + actual, + ); + + Ok(()) +} + +fn fails_wizening(wat: &str) -> Result<()> { + let _ = env_logger::try_init(); + + let wasm = wat_to_wasm(wat)?; + + let mut features = wasmparser::WasmFeatures::WASM2; + features.set(wasmparser::WasmFeatures::MULTI_MEMORY, true); + + let mut validator = wasmparser::Validator::new_with_features(features); + validator + .validate_all(&wasm) + .context("initial Wasm should be valid")?; + + anyhow::ensure!( + get_wizer().run(&wasm).is_err(), + "Expected an error when wizening, but didn't get one" + ); + Ok(()) +} + +#[test] +fn basic_global() -> Result<()> { + run_wat( + &[], + 42, + r#" +(module + (global $g (mut i32) i32.const 0) + (func (export "wizer.initialize") + i32.const 42 + global.set $g) + (func (export "run") (result i32) + global.get $g)) + "#, + ) +} + +#[test] +fn basic_memory() -> Result<()> { + run_wat( + &[], + 42, + r#" +(module + (memory 1) + (func (export "wizer.initialize") + i32.const 0 + i32.const 42 + i32.store offset=1337) + (func (export "run") (result i32) + i32.const 0 + i32.load offset=1337)) + "#, + ) +} + +#[test] +fn multi_memory() -> Result<()> { + run_wat( + &[], + 42, + r#" +(module + (memory $m1 1) + (memory $m2 1) + (func (export "wizer.initialize") + i32.const 0 + i32.const 41 + i32.store $m1 offset=1337 + i32.const 0 + i32.const 1 + i32.store $m2 offset=1337) + (func (export "run") (result i32) + i32.const 0 + i32.load $m1 offset=1337 + i32.const 0 + i32.load $m2 offset=1337 + i32.add)) +"#, + ) +} +#[test] +fn reject_imported_memory() -> Result<()> { + fails_wizening( + r#" + (module + (import "" "" (memory 1))) + "#, + ) +} + +#[test] +fn reject_imported_global() -> Result<()> { + fails_wizening( + r#" + (module + (import "" "" (global i32))) + "#, + ) +} + +#[test] +fn reject_imported_table() -> Result<()> { + fails_wizening( + r#" + (module + (import "" "" (table 0 externref))) + "#, + ) +} + +#[test] +fn reject_table_copy() -> Result<()> { + let result = run_wat( + &[], + 42, + r#" +(module + (table 3 funcref) + + (func $f (result i32) (i32.const 0)) + (func $g (result i32) (i32.const 0)) + (func $h (result i32) (i32.const 0)) + + (func (export "main") + i32.const 0 + i32.const 1 + i32.const 1 + table.copy) + + (elem (i32.const 0) $f $g $h) +) +"#, + ); + assert!(result.is_err()); + + let err = result.unwrap_err(); + assert!(err + .to_string() + .contains("unsupported `table.copy` instruction")); + + Ok(()) +} + +#[test] +fn reject_table_get_set() -> Result<()> { + let wat = r#" +(module + (table 3 funcref) + + (func $f (result i32) (i32.const 0)) + (func $g (result i32) (i32.const 0)) + (func $h (result i32) (i32.const 0)) + + (func (export "main") + i32.const 0 + i32.const 1 + table.get + table.set) + + (elem (i32.const 0) $f $g $h) +) +"#; + + let _ = env_logger::try_init(); + let mut wizer = Wizer::new(); + wizer.wasm_reference_types(false); + + let wasm = wat_to_wasm(wat)?; + let result = wizen_and_run_wasm(&[], 42, &wasm, wizer); + + assert!(result.is_err()); + + let err = result.unwrap_err(); + assert!(err + .to_string() + .contains("reference types support is not enabled"),); + + Ok(()) +} + +#[test] +fn reject_table_get_set_with_reference_types_enabled() -> Result<()> { + let result = run_wat( + &[], + 42, + r#" + (module + (table 3 funcref) + + (func $f (result i32) (i32.const 0)) + (func $g (result i32) (i32.const 0)) + (func $h (result i32) (i32.const 0)) + + (func (export "main") + i32.const 0 + i32.const 1 + table.get + table.set) + + (elem (i32.const 0) $f $g $h) + )"#, + ); + assert!(result.is_err()); + + let err = result.unwrap_err(); + assert!(err + .to_string() + .contains("unsupported `table.get` instruction"),); + + Ok(()) +} + +#[test] +fn reject_table_grow_with_reference_types_enabled() -> anyhow::Result<()> { + let wat = r#" + (module + (elem declare func $f) + (func $f) + (table 0 funcref) + (func (export "_initialize") (result i32) + ref.func $f + i32.const 1 + table.grow + ) + )"#; + + let _ = env_logger::try_init(); + let mut wizer = Wizer::new(); + wizer.wasm_reference_types(true); + + let wasm = wat_to_wasm(wat)?; + let result = wizen_and_run_wasm(&[], 42, &wasm, wizer); + + assert!(result.is_err()); + + let err = result.unwrap_err(); + assert!(err + .to_string() + .contains("unsupported `ref.func` instruction")); + + Ok(()) +} + +#[test] +fn indirect_call_with_reference_types() -> anyhow::Result<()> { + let wat = r#" + (module + (type $sig (func (result i32))) + (table 0 funcref) + (table $table1 1 funcref) + (elem (table $table1) (i32.const 0) func $f) + (func $f (type $sig) + i32.const 42 + ) + (func (export "wizer.initialize")) + (func (export "run") (result i32) + i32.const 0 + call_indirect $table1 (type $sig) + ) + )"#; + + let _ = env_logger::try_init(); + let mut wizer = Wizer::new(); + wizer.wasm_reference_types(true); + wizer.wasm_bulk_memory(true); + + let wasm = wat_to_wasm(wat)?; + wizen_and_run_wasm(&[], 42, &wasm, wizer) +} + +#[test] +fn reject_table_init() -> Result<()> { + let result = run_wat( + &[], + 42, + r#" +(module + (table 3 funcref) + + (func $f (result i32) (i32.const 0)) + (func $g (result i32) (i32.const 0)) + (func $h (result i32) (i32.const 0)) + + (elem $elem func $f $g $h) + + (func (export "main") + i32.const 0 + i32.const 0 + i32.const 3 + table.init $elem) +) +"#, + ); + assert!(result.is_err()); + + let err = result.unwrap_err(); + assert!(err + .to_string() + .contains("unsupported `table.init` instruction")); + + Ok(()) +} + +#[test] +fn reject_elem_drop() -> Result<()> { + let result = run_wat( + &[], + 42, + r#" +(module + (table 3 funcref) + + (func $f (result i32) (i32.const 0)) + (func $g (result i32) (i32.const 0)) + (func $h (result i32) (i32.const 0)) + + (elem $elem func $f $g $h) + + (func (export "main") + elem.drop $elem) +) +"#, + ); + assert!(result.is_err()); + + let err = result.unwrap_err(); + assert!(err + .to_string() + .contains("unsupported `elem.drop` instruction")); + + Ok(()) +} + +#[test] +fn reject_data_drop() -> Result<()> { + let result = run_wat( + &[], + 42, + r#" +(module + (memory 1) + (data $data "hello, wizer!") + + (func (export "main") + data.drop $data) +) +"#, + ); + assert!(result.is_err()); + + let err = result.unwrap_err(); + assert!(err + .to_string() + .contains("unsupported `data.drop` instruction")); + + Ok(()) +} + +#[test] +fn rust_regex() -> Result<()> { + wizen_and_run_wasm( + &[wasmtime::Val::I32(13)], + 42, + &include_bytes!("./regex_test.wasm")[..], + get_wizer(), + ) +} + +#[test] +fn data_segment_at_end_of_memory() -> Result<()> { + // Test that we properly synthesize data segments for data at the end of + // memory. + run_wat( + &[], + 42, + r#" +(module + (memory 1) + (func (export "wizer.initialize") + ;; Store 42 to the last byte in memory. + i32.const 0 + i32.const 42 + i32.store8 offset=65535 + ) + (func (export "run") (result i32) + i32.const 0 + i32.load8_u offset=65535 + ) +) +"#, + ) +} + +#[test] +fn too_many_data_segments_for_engines() -> Result<()> { + run_wat( + &[], + 42, + r#" +(module + ;; Enough memory to create more segments than engines will allow: + ;; + ;; // The maximum number of segments that engines will allow a module to + ;; // have. + ;; let max_segments = 100_000; + ;; + ;; // The minimum gap that Wizer won't automatically merge two data + ;; // segments (see `MIN_ACTIVE_SEGMENT_OVERHEAD`). + ;; let wizer_min_gap = 6; + ;; + ;; // Wasm page size. + ;; let wasm_page_size = 65_536; + ;; + ;; let num_pages = round_up(max_segments * wizer_min_gap / wasm_page_size); + (memory 10) + + (func (export "wizer.initialize") + (local i32) + loop + (i32.ge_u (local.get 0) (i32.const 655360)) ;; 10 * wasm_page_size + if + return + end + + (i32.store8 (local.get 0) (i32.const 42)) + (local.set 0 (i32.add (local.get 0) (i32.const 6))) + br 0 + end + ) + (func (export "run") (result i32) + i32.const 0 + i32.load8_u + ) +) +"#, + ) +} + +#[test] +fn rename_functions() -> Result<()> { + let wat = r#" +(module + (func (export "wizer.initialize")) + (func (export "func_a") (result i32) + i32.const 1) + (func (export "func_b") (result i32) + i32.const 2) + (func (export "func_c") (result i32) + i32.const 3)) + "#; + + let wasm = wat_to_wasm(wat)?; + let mut wizer = Wizer::new(); + wizer.allow_wasi(true).unwrap(); + wizer.func_rename("func_a", "func_b"); + wizer.func_rename("func_b", "func_c"); + let wasm = wizer.run(&wasm)?; + let wat = wasmprinter::print_bytes(&wasm)?; + + let expected_wat = r#" +(module + (type (;0;) (func)) + (type (;1;) (func (result i32))) + (export "func_a" (func 2)) + (export "func_b" (func 3)) + (func (;0;) (type 0)) + (func (;1;) (type 1) (result i32) + i32.const 1 + ) + (func (;2;) (type 1) (result i32) + i32.const 2 + ) + (func (;3;) (type 1) (result i32) + i32.const 3 + ) +) + "#; + + assert_eq!(wat.trim(), expected_wat.trim()); + Ok(()) +} + +#[test] +fn wasi_reactor() -> anyhow::Result<()> { + run_wat( + &[], + 42, + r#" + (module + (global $g (mut i32) i32.const 0) + (func (export "_initialize") + i32.const 6 + global.set $g + ) + (func (export "wizer.initialize") + global.get $g + i32.const 7 + i32.mul + global.set $g) + (func (export "run") (result i32) + global.get $g + ) + ) + "#, + ) +} + +#[test] +fn wasi_reactor_initializer_as_init_func() -> anyhow::Result<()> { + let wat = r#" + (module + (global $g (mut i32) i32.const 0) + (func (export "_initialize") + global.get $g + i32.const 1 + i32.add + global.set $g + ) + (func (export "run") (result i32) + global.get $g + ) + )"#; + + let _ = env_logger::try_init(); + let mut wizer = Wizer::new(); + wizer.init_func("_initialize"); + let wasm = wat_to_wasm(wat)?; + // we expect `_initialize` to be called _exactly_ once + wizen_and_run_wasm(&[], 1, &wasm, wizer) +} + +#[test] +fn wasi_reactor_initializer_with_keep_init() -> anyhow::Result<()> { + let wat = r#" + (module + (global $g (mut i32) i32.const 0) + (func (export "_initialize") + i32.const 1 + global.set $g + ) + (func (export "wizer.initialize") + i32.const 2 + global.set $g) + (func (export "run") (result i32) + global.get $g + ) + )"#; + + let _ = env_logger::try_init(); + let mut wizer = Wizer::new(); + wizer.keep_init_func(true); + let wasm = wat_to_wasm(wat)?; + // we expect `_initialize` to be un-exported and not called at run + wizen_and_run_wasm(&[], 2, &wasm, wizer) +} + +#[test] +fn call_undefined_import_function_during_init() -> Result<()> { + fails_wizening( + r#" + (module + (import "x" "f" (func $import)) + (func (export "wizer.initialize") + (call $import) + ) + ) + "#, + ) +} + +#[test] +fn allow_undefined_import_function() -> Result<()> { + run_wat( + &[], + 42, + r#" + (module + (import "x" "f" (func $import)) + (func (export "wizer.initialize")) + (func (export "run") (result i32) + i32.const 42 + ) + ) + "#, + ) +} + +#[test] +fn accept_bulk_memory_copy() -> Result<()> { + run_wat( + &[], + ('h' as i32) + ('w' as i32), + r#" + (module + (memory $memory (data "hello, wizer!")) + (func (export "wizer.initialize") + i32.const 42 ;; dst + i32.const 0 ;; src + i32.const 13 ;; size + memory.copy) + (func (export "run") (result i32) + i32.const 42 + i32.load8_u + i32.const 42 + i32.load8_u offset=7 + i32.add)) + "#, + ) +} + +#[test] +fn accept_bulk_memory_data_count() -> Result<()> { + let mut module = wasm_encoder::Module::new(); + let mut types = wasm_encoder::TypeSection::new(); + types.ty().func_type(&wasm_encoder::FuncType::new( + vec![], + vec![wasm_encoder::ValType::I32], + )); + types + .ty() + .func_type(&wasm_encoder::FuncType::new(vec![], vec![])); + module.section(&types); + + let mut functions = wasm_encoder::FunctionSection::new(); + functions.function(0); + functions.function(1); + module.section(&functions); + + let mut memory = wasm_encoder::MemorySection::new(); + memory.memory(wasm_encoder::MemoryType { + minimum: 1, + maximum: Some(1), + memory64: false, + shared: false, + page_size_log2: None, + }); + module.section(&memory); + + let mut exports = wasm_encoder::ExportSection::new(); + exports.export("run", wasm_encoder::ExportKind::Func, 0); + exports.export("wizer.initialize", wasm_encoder::ExportKind::Func, 1); + module.section(&exports); + + module.section(&wasm_encoder::DataCountSection { count: 2 }); + + let mut code = wasm_encoder::CodeSection::new(); + let mut func = wasm_encoder::Function::new(vec![]); + func.instruction(&wasm_encoder::Instruction::I32Const(42)); + func.instruction(&wasm_encoder::Instruction::End); + code.function(&func); + + let mut func = wasm_encoder::Function::new(vec![]); + func.instruction(&wasm_encoder::Instruction::End); + code.function(&func); + + module.section(&code); + + // We're expecting these two data segments to be merge into one, which will exercise wizer's + // ability to output the correct data count (1 instead of 2 above). + let mut data = wasm_encoder::DataSection::new(); + data.active(0, &ConstExpr::i32_const(0), vec![0, 1, 2, 3]); + data.active(0, &ConstExpr::i32_const(4), vec![5, 6, 7, 8]); + module.section(&data); + + wizen_and_run_wasm(&[], 42, &module.finish(), get_wizer()).unwrap(); + Ok(()) +} + +#[test] +fn accept_bulk_memory_fill() -> Result<()> { + run_wat( + &[], + 77 + 77, + r#" + (module + (memory 1) + (func (export "wizer.initialize") + i32.const 42 ;; dst + i32.const 77 ;; value + i32.const 13 ;; size + memory.fill) + (func (export "run") (result i32) + i32.const 42 + i32.load8_u + i32.const 42 + i32.load8_u offset=7 + i32.add)) + "#, + ) +} + +#[test] +fn accept_bulk_memory_init() -> Result<()> { + run_wat( + &[], + ('h' as i32) + ('w' as i32), + r#" + (module + (memory 1) + (data $data "hello, wizer!") + (func (export "wizer.initialize") + i32.const 42 ;; dst + i32.const 0 ;; offset + i32.const 13 ;; size + memory.init $data) + (func (export "run") (result i32) + i32.const 42 + i32.load8_u + i32.const 42 + i32.load8_u offset=7 + i32.add)) + "#, + ) +} + +#[test] +fn accept_simd128() -> Result<()> { + run_wat( + &[], + 49, + r#" + (module + (global $g (mut v128) (v128.const i32x4 2 3 5 7)) + (func (export "wizer.initialize") + global.get $g + global.get $g + i32x4.mul + global.set $g) + (func (export "run") (result i32) + global.get $g + i32x4.extract_lane 3)) + "#, + ) +} diff --git a/scripts/publish.rs b/scripts/publish.rs index c9f8feea6404..0d410336a632 100644 --- a/scripts/publish.rs +++ b/scripts/publish.rs @@ -244,6 +244,12 @@ fn run_cmd(cmd: &mut Command) { } fn find_crates(dir: &Path, ws: &Workspace, dst: &mut Vec) { + // Temporary exclusion of Wizer to get reverted in #11805 with full Wizer + // integration. + if dir.ends_with("wizer") { + return; + } + if dir.join("Cargo.toml").exists() { let krate = read_crate(Some(ws), &dir.join("Cargo.toml")); if !krate.publish || CRATES_TO_PUBLISH.iter().any(|c| krate.name == *c) {