From ca7036dcb86bcb8a601fa95163137be1833a27a1 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 11 Jul 2023 15:36:19 -0700 Subject: [PATCH] Initial work on env virtualization (#1) --- .github/workflows/main.yml | 51 + .gitignore | 2 + CODE_OF_CONDUCT.md | 3 +- Cargo.lock | 2843 +++++++++++++++++++++++ Cargo.toml | 49 + README.md | 63 +- build.rs | 37 + lib/virtual_adapter.wasm | Bin 0 -> 28359 bytes lib/wasi_snapshot_preview1.command.wasm | Bin 0 -> 106094 bytes lib/wasi_snapshot_preview1.reactor.wasm | Bin 0 -> 105973 bytes src/bin/wasi-virt.rs | 48 + src/env.rs | 218 ++ src/lib.rs | 46 + src/walrus_ops.rs | 99 + tests/cases/env-allow.toml | 14 + tests/cases/env-deny.toml | 14 + tests/cases/env-none-overrides.toml | 11 + tests/cases/env-none.toml | 11 + tests/cases/env-passthrough.toml | 11 + tests/components/get-env/Cargo.toml | 11 + tests/components/get-env/src/lib.rs | 16 + tests/virt.rs | 191 ++ virtual-adapter/Cargo.toml | 12 + virtual-adapter/src/lib.rs | 97 + wit/deps/clocks/monotonic-clock.wit | 34 + wit/deps/clocks/timezone.wit | 63 + wit/deps/clocks/wall-clock.wit | 43 + wit/deps/filesystem/filesystem.wit | 782 +++++++ wit/deps/http/incoming-handler.wit | 24 + wit/deps/http/outgoing-handler.wit | 18 + wit/deps/http/types.wit | 159 ++ wit/deps/io/streams.wit | 215 ++ wit/deps/logging/handler.wit | 34 + wit/deps/poll/poll.wit | 41 + wit/deps/random/insecure-seed.wit | 24 + wit/deps/random/insecure.wit | 21 + wit/deps/random/random.wit | 25 + wit/deps/sockets/instance-network.wit | 9 + wit/deps/sockets/ip-name-lookup.wit | 69 + wit/deps/sockets/network.wit | 187 ++ wit/deps/sockets/tcp-create-socket.wit | 27 + wit/deps/sockets/tcp.wit | 255 ++ wit/deps/sockets/udp-create-socket.wit | 27 + wit/deps/sockets/udp.wit | 211 ++ wit/deps/wasi-cli-base/environment.wit | 16 + wit/deps/wasi-cli-base/exit.wit | 4 + wit/deps/wasi-cli-base/preopens.wit | 7 + wit/deps/wasi-cli-base/stdio.wit | 17 + wit/virt.wit | 36 + 49 files changed, 6189 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/main.yml create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 build.rs create mode 100755 lib/virtual_adapter.wasm create mode 100644 lib/wasi_snapshot_preview1.command.wasm create mode 100644 lib/wasi_snapshot_preview1.reactor.wasm create mode 100644 src/bin/wasi-virt.rs create mode 100644 src/env.rs create mode 100644 src/lib.rs create mode 100644 src/walrus_ops.rs create mode 100644 tests/cases/env-allow.toml create mode 100644 tests/cases/env-deny.toml create mode 100644 tests/cases/env-none-overrides.toml create mode 100644 tests/cases/env-none.toml create mode 100644 tests/cases/env-passthrough.toml create mode 100644 tests/components/get-env/Cargo.toml create mode 100644 tests/components/get-env/src/lib.rs create mode 100644 tests/virt.rs create mode 100644 virtual-adapter/Cargo.toml create mode 100644 virtual-adapter/src/lib.rs create mode 100644 wit/deps/clocks/monotonic-clock.wit create mode 100644 wit/deps/clocks/timezone.wit create mode 100644 wit/deps/clocks/wall-clock.wit create mode 100644 wit/deps/filesystem/filesystem.wit create mode 100644 wit/deps/http/incoming-handler.wit create mode 100644 wit/deps/http/outgoing-handler.wit create mode 100644 wit/deps/http/types.wit create mode 100644 wit/deps/io/streams.wit create mode 100644 wit/deps/logging/handler.wit create mode 100644 wit/deps/poll/poll.wit create mode 100644 wit/deps/random/insecure-seed.wit create mode 100644 wit/deps/random/insecure.wit create mode 100644 wit/deps/random/random.wit create mode 100644 wit/deps/sockets/instance-network.wit create mode 100644 wit/deps/sockets/ip-name-lookup.wit create mode 100644 wit/deps/sockets/network.wit create mode 100644 wit/deps/sockets/tcp-create-socket.wit create mode 100644 wit/deps/sockets/tcp.wit create mode 100644 wit/deps/sockets/udp-create-socket.wit create mode 100644 wit/deps/sockets/udp.wit create mode 100644 wit/deps/wasi-cli-base/environment.wit create mode 100644 wit/deps/wasi-cli-base/exit.wit create mode 100644 wit/deps/wasi-cli-base/preopens.wit create mode 100644 wit/deps/wasi-cli-base/stdio.wit create mode 100644 wit/virt.wit diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..c2d2d6a --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,51 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] +defaults: + run: + shell: bash + +# 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: + test: + name: Test + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Install Rust + run: rustup update nightly --no-self-update && rustup default nightly && rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu + + - name: Install wasm32-unknown target + run: rustup target add wasm32-unknown-unknown + + - name: Install wasm32-wasi target + run: rustup target add wasm32-wasi + + - name: Test + run: cargo test + + rustfmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update nightly && rustup default nightly && rustup component add rustfmt + - name: Format source code + run: cargo fmt -- --check diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d14bda9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/tests/generated diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 69cee1d..f54a9ef 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,6 +1,6 @@ # Contributor Covenant Code of Conduct -*Note*: this Code of Conduct pertains to individuals' behavior. Please also see the [Organizational Code of Conduct][OCoC]. +*Note*: this Code of Conduct pertains to individuals' behavior. Please also see the [Organizational Code of Conduct](ORG_CODE_OF_CONDUCT.md). ## Our Pledge @@ -44,6 +44,5 @@ Project maintainers who do not follow or enforce the Code of Conduct in good fai 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/ \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..3c0392b --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2843 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli 0.27.3", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "ambient-authority" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" + +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + +[[package]] +name = "arbitrary" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" +dependencies = [ + "async-lock", + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite", + "log", + "parking", + "polling", + "rustix 0.37.23", + "slab", + "socket2", + "waker-fn", +] + +[[package]] +name = "async-lock" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-attributes", + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" + +[[package]] +name = "async-trait" +version = "0.1.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.23", +] + +[[package]] +name = "atomic-waker" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + +[[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 = "blocking" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "log", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cap-fs-ext" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bc48200a1a0fa6fba138b1802ad7def18ec1cdd92f7b2a04e21f1bd887f7b9" +dependencies = [ + "cap-primitives", + "cap-std", + "io-lifetimes 1.0.11", + "windows-sys", +] + +[[package]] +name = "cap-primitives" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b6df5b295dca8d56f35560be8c391d59f0420f72e546997154e24e765e6451" +dependencies = [ + "ambient-authority", + "fs-set-times", + "io-extras", + "io-lifetimes 1.0.11", + "ipnet", + "maybe-owned", + "rustix 0.37.23", + "windows-sys", + "winx 0.35.1", +] + +[[package]] +name = "cap-rand" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25555efacb0b5244cf1d35833d55d21abc916fff0eaad254b8e2453ea9b8ab" +dependencies = [ + "ambient-authority", + "rand", +] + +[[package]] +name = "cap-std" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3373a62accd150b4fcba056d4c5f3b552127f0ec86d3c8c102d60b978174a012" +dependencies = [ + "cap-primitives", + "io-extras", + "io-lifetimes 1.0.11", + "rustix 0.37.23", +] + +[[package]] +name = "cap-time-ext" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e95002993b7baee6b66c8950470e59e5226a23b3af39fc59c47fe416dd39821a" +dependencies = [ + "cap-primitives", + "once_cell", + "rustix 0.37.23", + "winx 0.35.1", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.23", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "concurrent-queue" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "cpp_demangle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-bforest" +version = "0.97.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c289b8eac3a97329a524e953b5fd68a8416ca629e1a37287f12d9e0760aadbc" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.97.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bf07ba80f53fa7f7dc97b11087ea867f7ae4621cfca21a909eca92c0b96c7d9" +dependencies = [ + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-isle", + "gimli 0.27.3", + "hashbrown 0.13.2", + "log", + "regalloc2", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.97.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40a7ca088173130c5c033e944756e3e441fbf3f637f32b4f6eb70252580c6dd4" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.97.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0114095ec7d2fbd658ed100bd007006360bc2530f57c6eee3d3838869140dbf9" + +[[package]] +name = "cranelift-control" +version = "0.97.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d56031683a55a949977e756d21826eb17a1f346143a1badc0e120a15615cd38" +dependencies = [ + "arbitrary", +] + +[[package]] +name = "cranelift-entity" +version = "0.97.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6565198b5684367371e2b946ceca721eb36965e75e3592fad12fc2e15f65d7b" +dependencies = [ + "serde", +] + +[[package]] +name = "cranelift-frontend" +version = "0.97.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f28cc44847c8b98cb921e6bfc0f7b228f4d27519376fea724d181da91709a6" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.97.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80b658177e72178c438f7de5d6645c56d97af38e17fcb0b500459007b4e05cc5" + +[[package]] +name = "cranelift-native" +version = "0.97.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf1c7de7221e6afcc5e13ced3b218faab3bc65b47eac67400046a05418aecd6a" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-wasm" +version = "0.97.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b0d28ebe8edb6b503630c489aa4669f1e2d13b97bec7271a0fcb0e159be3ad" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "itertools", + "log", + "smallvec", + "wasmparser 0.107.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmtime-types", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset 0.9.0", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[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 = "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" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[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.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fd-lock" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b0377f1edc77dbd1118507bc7a66e4ab64d2b90c66f90726dc801e73a8c68f9" +dependencies = [ + "cfg-if", + "rustix 0.38.3", + "windows-sys", +] + +[[package]] +name = "file-per-thread-logger" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a3cc21c33af89af0930c8cae4ade5e6fdc17b5d2c97b3d2e2edb67a1cf683f3" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs-set-times" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d167b646a876ba8fda6b50ac645cfd96242553cbaf0ca4fccaa39afcbf0801f" +dependencies = [ + "io-lifetimes 1.0.11", + "rustix 0.38.3", + "windows-sys", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[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.3.3", + "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 = "get-env" +version = "0.1.0" +dependencies = [ + "anyhow", + "wit-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +dependencies = [ + "fallible-iterator", + "indexmap 1.9.3", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +dependencies = [ + "fallible-iterator", + "indexmap 1.9.3", + "stable_deref_trait", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[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.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", + "serde", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-extras" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde93d48f0d9277f977a333eca8313695ddd5301dc96f7e02aeddcb0dd99096f" +dependencies = [ + "io-lifetimes 1.0.11", + "windows-sys", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "io-lifetimes" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bffb4def18c48926ccac55c1223e02865ce1a821751a95920448662696e7472c" + +[[package]] +name = "ipnet" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix 0.38.3", + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "ittapi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e648c437172ce7d3ac35ca11a068755072054826fa455a916b43524fa4a62a7" +dependencies = [ + "anyhow", + "ittapi-sys", + "log", +] + +[[package]] +name = "ittapi-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9b32a4d23f72548178dde54f3c12c6b6a08598e25575c0d0fa5bd861e0dc1a5" +dependencies = [ + "cc", +] + +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +dependencies = [ + "value-bag", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +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.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memfd" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" +dependencies = [ + "rustix 0.37.23", +] + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "crc32fast", + "hashbrown 0.13.2", + "indexmap 1.9.3", + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "parking" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" + +[[package]] +name = "paste" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b27ab7be369122c218afc2079489cdcb4b517c0a3fc386ff11e1fedfcc2b35" + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "petgraph" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +dependencies = [ + "fixedbitset", + "indexmap 1.9.3", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" + +[[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.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "pulldown-cmark" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8" +dependencies = [ + "bitflags 1.3.2", + "memchr", + "unicase", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +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.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "regalloc2" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12513beb38dd35aab3ac5f5b89fd0330159a0dc21d5309d75073011bbc8032b0" +dependencies = [ + "hashbrown 0.13.2", + "log", + "rustc-hash", + "slice-group-by", + "smallvec", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9aaecc05d5c4b5f7da074b9a0d1a0867e71fd36e7fc0482d8bcfe8e8fc56290" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" + +[[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 = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.37.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes 1.0.11", + "itoa", + "libc", + "linux-raw-sys 0.3.8", + "once_cell", + "windows-sys", +] + +[[package]] +name = "rustix" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys 0.4.3", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.167" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7daf513456463b42aa1d94cff7e0c24d682b429f020b9afa4f5ba5c40a22b237" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.167" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b69b106b68bc8054f0e974e70d19984040f8a5cf9215ca82626ea4853f82c4b9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.23", +] + +[[package]] +name = "serde_json" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap 1.9.3", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shellexpand" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" +dependencies = [ + "dirs", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slice-group-by" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + +[[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.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[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.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-interface" +version = "0.25.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10081a99cbecbc363d381b9503563785f0b02735fccbb0d4c1a2cb3d39f7e7fe" +dependencies = [ + "bitflags 2.3.3", + "cap-fs-ext", + "cap-std", + "fd-lock", + "io-lifetimes 2.0.2", + "rustix 0.38.3", + "windows-sys", + "winx 0.36.1", +] + +[[package]] +name = "target-lexicon" +version = "0.12.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1c7f239eb94671427157bd93b3694320f3668d4e1eff08c7285366fd777fac" + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.23", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" +dependencies = [ + "indexmap 2.0.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.23", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +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.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" + +[[package]] +name = "value-bag" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "virtual-adapter" +version = "0.1.0" +dependencies = [ + "anyhow", + "wit-bindgen", +] + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "walrus" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc27d837c587f863d99515dc8cae7cef1098bd1d99fa29373e3660c12766265e" +dependencies = [ + "anyhow", + "gimli 0.26.2", + "id-arena", + "leb128", + "log", + "walrus-macro", + "wasm-encoder 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.80.2", +] + +[[package]] +name = "walrus-macro" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e5bd22c71e77d60140b0bd5be56155a37e5bd14e24f5f87298040d0cc40d7" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi-cap-std-sync" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291862f1014dd7e674f93b263d57399de4dd1907ea37e74cf7d36454536ba2f0" +dependencies = [ + "anyhow", + "async-trait", + "cap-fs-ext", + "cap-rand", + "cap-std", + "cap-time-ext", + "fs-set-times", + "io-extras", + "io-lifetimes 1.0.11", + "is-terminal", + "once_cell", + "rustix 0.37.23", + "system-interface", + "tracing", + "wasi-common", + "windows-sys", +] + +[[package]] +name = "wasi-common" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b422ae2403cae9ca603864272a402cf5001dd6fef8632e090e00c4fb475741b" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "cap-rand", + "cap-std", + "io-extras", + "log", + "rustix 0.37.23", + "thiserror", + "tracing", + "wasmtime", + "wiggle", + "windows-sys", +] + +[[package]] +name = "wasi-virt" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-std", + "cap-std", + "clap", + "heck 0.4.1", + "serde", + "toml 0.7.6", + "walrus", + "wasm-compose", + "wasmtime", + "wasmtime-wasi", + "wit-component", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.23", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.23", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasm-compose" +version = "0.2.17" +source = "git+https://github.com/bytecodealliance/wasm-tools?branch=main#3379199d0d58da323740f7fdaa4618b459851b5d" +dependencies = [ + "anyhow", + "heck 0.4.1", + "indexmap 2.0.0", + "log", + "petgraph", + "serde", + "serde_yaml", + "smallvec", + "wasm-encoder 0.29.0 (git+https://github.com/bytecodealliance/wasm-tools?branch=main)", + "wasmparser 0.107.0 (git+https://github.com/bytecodealliance/wasm-tools?branch=main)", + "wat 1.0.66 (git+https://github.com/bytecodealliance/wasm-tools?branch=main)", +] + +[[package]] +name = "wasm-encoder" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18c41dbd92eaebf3612a39be316540b8377c871cb9bde6b064af962984912881" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.29.0" +source = "git+https://github.com/bytecodealliance/wasm-tools?branch=main#3379199d0d58da323740f7fdaa4618b459851b5d" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-metadata" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36e5156581ff4a302405c44ca7c85347563ca431d15f1a773f12c9c7b9a6cdc9" +dependencies = [ + "anyhow", + "indexmap 1.9.3", + "serde", + "wasm-encoder 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.107.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmparser" +version = "0.80.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "449167e2832691a1bff24cde28d2804e90e09586a448c8e76984792c44334a6b" + +[[package]] +name = "wasmparser" +version = "0.107.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29e3ac9b780c7dda0cac7a52a5d6d2d6707cc6e3451c9db209b6c758f40d7acb" +dependencies = [ + "indexmap 1.9.3", + "semver", +] + +[[package]] +name = "wasmparser" +version = "0.107.0" +source = "git+https://github.com/bytecodealliance/wasm-tools?branch=main#3379199d0d58da323740f7fdaa4618b459851b5d" +dependencies = [ + "indexmap 2.0.0", + "semver", +] + +[[package]] +name = "wasmprinter" +version = "0.2.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc960b30b84abca377768f3c62cff3a1c74db8c0f6759ed581827da0bd3a3fed" +dependencies = [ + "anyhow", + "wasmparser 0.107.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmtime" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd02b992d828b91efaf2a7499b21205fe4ab3002e401e3fe0f227aaeb4001d93" +dependencies = [ + "anyhow", + "async-trait", + "bincode", + "bumpalo", + "cfg-if", + "encoding_rs", + "fxprof-processed-profile", + "indexmap 1.9.3", + "libc", + "log", + "object", + "once_cell", + "paste", + "psm", + "rayon", + "serde", + "serde_json", + "target-lexicon", + "wasmparser 0.107.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmtime-cache", + "wasmtime-component-macro", + "wasmtime-component-util", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-fiber", + "wasmtime-jit", + "wasmtime-runtime", + "wasmtime-winch", + "wat 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", + "windows-sys", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284466ef356ce2d909bc0ad470b60c4d0df5df2de9084457e118131b3c779b92" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-cache" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efc78cfe1a758d1336f447a47af6ec05e0df2c03c93440d70faf80e17fbb001e" +dependencies = [ + "anyhow", + "base64", + "bincode", + "directories-next", + "file-per-thread-logger", + "log", + "rustix 0.37.23", + "serde", + "sha2", + "toml 0.5.11", + "windows-sys", + "zstd", +] + +[[package]] +name = "wasmtime-component-macro" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e916103436a6d84faa4c2083e2e98612a323c2cc6147ec419124f67c764c9c" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn 1.0.109", + "wasmtime-component-util", + "wasmtime-wit-bindgen", + "wit-parser", +] + +[[package]] +name = "wasmtime-component-util" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f20a5135ec5ef01080e674979b02d6fa5eebaa2b0c2d6660513ee9956a1bf624" + +[[package]] +name = "wasmtime-cranelift" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1aa99cbf3f8edb5ad8408ba380f5ab481528ecd8a5053acf758e006d6727fd" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-control", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli 0.27.3", + "log", + "object", + "target-lexicon", + "thiserror", + "wasmparser 0.107.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmtime-cranelift-shared", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-cranelift-shared" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cce31fd55978601acc103acbb8a26f81c89a6eae12d3a1c59f34151dfa609484" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-control", + "cranelift-native", + "gimli 0.27.3", + "object", + "target-lexicon", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-environ" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f9e58e0ee7d43ff13e75375c726b16bce022db798d3a099a65eeaa7d7a544b" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli 0.27.3", + "indexmap 1.9.3", + "log", + "object", + "serde", + "target-lexicon", + "thiserror", + "wasm-encoder 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.107.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmprinter", + "wasmtime-component-util", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-fiber" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14309cbdf2c395258b124a24757c727403070c0465a28bcc780c4f82f4bca5ff" +dependencies = [ + "cc", + "cfg-if", + "rustix 0.37.23", + "wasmtime-asm-macros", + "windows-sys", +] + +[[package]] +name = "wasmtime-jit" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0f2eaeb01bb67266416507829bd8e0bb60278444e4cbd048e280833ebeaa02" +dependencies = [ + "addr2line", + "anyhow", + "bincode", + "cfg-if", + "cpp_demangle", + "gimli 0.27.3", + "ittapi", + "log", + "object", + "rustc-demangle", + "rustix 0.37.23", + "serde", + "target-lexicon", + "wasmtime-environ", + "wasmtime-jit-debug", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f42e59d62542bfb73ce30672db7eaf4084a60b434b688ac4f05b287d497de082" +dependencies = [ + "object", + "once_cell", + "rustix 0.37.23", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b49ceb7e2105a8ebe5614d7bbab6f6ef137a284e371633af60b34925493081f" +dependencies = [ + "cfg-if", + "libc", + "windows-sys", +] + +[[package]] +name = "wasmtime-runtime" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a5de4762421b0b2b19e02111ca403632852b53e506e03b4b227ffb0fbfa63c2" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "encoding_rs", + "indexmap 1.9.3", + "libc", + "log", + "mach", + "memfd", + "memoffset 0.8.0", + "paste", + "rand", + "rustix 0.37.23", + "sptr", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-fiber", + "wasmtime-jit-debug", + "windows-sys", +] + +[[package]] +name = "wasmtime-types" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb7c138f797192f46afdd3ec16f85ef007c3bb45fa8e5174031f17b0be4c4a" +dependencies = [ + "cranelift-entity", + "serde", + "thiserror", + "wasmparser 0.107.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmtime-wasi" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01686e859249d4dffe3d7ce9957ae35bcf4161709dfafd165ee136bd54d179f1" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 1.3.2", + "cap-fs-ext", + "cap-rand", + "cap-std", + "cap-time-ext", + "fs-set-times", + "io-extras", + "libc", + "rustix 0.37.23", + "system-interface", + "thiserror", + "tracing", + "wasi-cap-std-sync", + "wasi-common", + "wasmtime", + "wiggle", + "windows-sys", +] + +[[package]] +name = "wasmtime-winch" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60160d8f7d2b301790730dac8ff25156c61d4fed79481e7074c21dd1283cfe2f" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli 0.27.3", + "object", + "target-lexicon", + "wasmparser 0.107.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmtime-cranelift-shared", + "wasmtime-environ", + "winch-codegen", +] + +[[package]] +name = "wasmtime-wit-bindgen" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3334b0466a4d340de345cda83474d1d2c429770c3d667877971407672bc618a" +dependencies = [ + "anyhow", + "heck 0.4.1", + "wit-parser", +] + +[[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 = "60.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd06cc744b536e30387e72a48fdd492105b9c938bb4f415c39c616a7a0a697ad" +dependencies = [ + "leb128", + "memchr", + "unicode-width", + "wasm-encoder 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wast" +version = "60.0.0" +source = "git+https://github.com/bytecodealliance/wasm-tools?branch=main#3379199d0d58da323740f7fdaa4618b459851b5d" +dependencies = [ + "leb128", + "memchr", + "unicode-width", + "wasm-encoder 0.29.0 (git+https://github.com/bytecodealliance/wasm-tools?branch=main)", +] + +[[package]] +name = "wat" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5abe520f0ab205366e9ac7d3e6b2fc71de44e32a2b58f2ec871b6b575bdcea3b" +dependencies = [ + "wast 60.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wat" +version = "1.0.66" +source = "git+https://github.com/bytecodealliance/wasm-tools?branch=main#3379199d0d58da323740f7fdaa4618b459851b5d" +dependencies = [ + "wast 60.0.0 (git+https://github.com/bytecodealliance/wasm-tools?branch=main)", +] + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wiggle" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea93d31f59f2b2fa4196990b684771500072d385eaac12587c63db2bc185d705" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 1.3.2", + "thiserror", + "tracing", + "wasmtime", + "wiggle-macro", +] + +[[package]] +name = "wiggle-generate" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df96ee6bea595fabf0346c08c553f684b08e88fad6fdb125e6efde047024f7b" +dependencies = [ + "anyhow", + "heck 0.4.1", + "proc-macro2", + "quote", + "shellexpand", + "syn 1.0.109", + "witx", +] + +[[package]] +name = "wiggle-macro" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8649011a011ecca6197c4db6ee630735062ba20595ea56ce58529b3b1c20aa2f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +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 = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525fdd0d4e82d1bd3083bd87e8ca8014abfbdc5bf290d1d5371dac440d351e89" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli 0.27.3", + "regalloc2", + "smallvec", + "target-lexicon", + "wasmparser 0.107.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmtime-environ", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winnow" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9482fe6ceabdf32f3966bfdd350ba69256a97c30253dc616fe0005af24f164e" +dependencies = [ + "memchr", +] + +[[package]] +name = "winx" +version = "0.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c52a121f0fbf9320d5f2a9a5d82f6cb7557eda5e8b47fc3e7f359ec866ae960" +dependencies = [ + "bitflags 1.3.2", + "io-lifetimes 1.0.11", + "windows-sys", +] + +[[package]] +name = "winx" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4857cedf8371f690bb6782a3e2b065c54d1b6661be068aaf3eac8b45e813fdf8" +dependencies = [ + "bitflags 2.3.3", + "windows-sys", +] + +[[package]] +name = "wit-bindgen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "392d16e9e46cc7ca98125bc288dd5e4db469efe8323d3e0dac815ca7f2398522" +dependencies = [ + "bitflags 2.3.3", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d422d36cbd78caa0e18c3371628447807c66ee72466b69865ea7e33682598158" +dependencies = [ + "anyhow", + "wit-component", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b76db68264f5d2089dc4652581236d8e75c5b89338de6187716215fd0e68ba3" +dependencies = [ + "heck 0.4.1", + "wasm-metadata", + "wit-bindgen-core", + "wit-bindgen-rust-lib", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-lib" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c50f334bc08b0903a43387f6eea6ef6aa9eb2a085729f1677b29992ecef20ba" +dependencies = [ + "heck 0.4.1", + "wit-bindgen-core", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced38a5e174940c6a41ae587babeadfd2e2c2dc32f3b6488bcdca0e8922cf3f3" +dependencies = [ + "anyhow", + "proc-macro2", + "syn 2.0.23", + "wit-bindgen-core", + "wit-bindgen-rust", + "wit-component", +] + +[[package]] +name = "wit-component" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cbd4c7f8f400327c482c88571f373844b7889e61460650d650fc5881bb3575c" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "indexmap 1.9.3", + "log", + "wasm-encoder 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-metadata", + "wasmparser 0.107.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6daec9f093dbaea0e94043eeb92ece327bbbe70c86b1f41aca9bbfefd7f050f0" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 1.9.3", + "log", + "pulldown-cmark", + "semver", + "unicode-xid", + "url", +] + +[[package]] +name = "witx" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" +dependencies = [ + "anyhow", + "log", + "thiserror", + "wast 35.0.2", +] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.8+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +dependencies = [ + "cc", + "libc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b170e5e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,49 @@ +cargo-features = ["profile-rustflags"] + +[package] +name = "wasi-virt" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["lib"] + +[[bin]] +name = "wasi-virt" + +[workspace] +members = [ + "virtual-adapter", + "tests/components/get-env" +] + +[profile.release] +opt-level = "z" +lto = true +codegen-units = 1 +panic = "abort" +rustflags = ["-Zoom=panic"] + +[dependencies] +anyhow = "1" +clap = { version = "4", features = ["derive"] } +serde = { version = "1", features = ["derive"] } +toml = "0.7" +walrus = "0.20.1" +wit-component = "0.11.0" + +[build-dependencies] +anyhow = "1" + +[dev-dependencies] +anyhow = "1" +async-std = { version = "1", features = ["attributes"] } +cap-std = "1.0.12" +heck = { version = "0.4" } +wasm-compose = { git = "https://github.com/bytecodealliance/wasm-tools", branch = "main" } +wasmtime = { version = "10.0.1", features = ["component-model"] } +wasmtime-wasi = "10.0.1" + +[workspace.dependencies] +anyhow = "1" +wit-bindgen = "0.8.0" diff --git a/README.md b/README.md index acc13c9..6b0c8f5 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,72 @@

WASI Virt

- Virtualization Component Generator for WASI + Virtualization Component Generator for WASI Preview 2

A Bytecode Alliance project

- build status + build status

-WIP: Check back soon. +The virtualized component can be composed into a WASI Preview2 component with `wasm-tools compose`, providing fully-configurable WASI virtualization with host pass through or full encapsulation as needed. + +Subsystem support: + +- [x] Environment virtualization +- [ ] Filesystem virtualization + +### Example + +```rs +use std::fs; +use wasi_virt::WasiVirt; + +fn main() { + let virt_component_bytes = WasiVirt::new() + // provide an allow list of host env vars + .env_host_allow(&["PUBLIC_ENV_VAR"]) + // provide custom env overrides + .env_overrides(&[("SOME", "ENV"), ("VAR", "OVERRIDES")]) + .create() + .unwrap(); + fs::write("virt.component.wasm", virt_component_bytes).unwrap(); +} +``` + +With the created `virt.component.wasm` component, this can now be composed into a component with the `wasm-tools compose` "definitions" feature: + +``` +wasm-tools compose mycomponent.wasm -d virt.component.wasm -o out.component.wasm +``` + +## CLI + +A CLI is also provided in this crate supporting: + +``` +wasi-virt config.toml -o virt.wasm +``` + +With the configuration file format: + +``` +[env] +# Support all env vars on the final host (apart from the overrides) +# Set to "none" to entirely encapsulate the host env +host = "all" +# Always ensures that this env var and value is set +overrides = [["CUSTOM", "VAL"]] +``` + +Allow lists and deny lists can also be provided via: + +``` +[env.host] +allow = ["ENV_KEY"] # Or Deny = ... +``` # License @@ -23,4 +78,4 @@ See [LICENSE](LICENSE) for more details. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, -shall be licensed as above, without any additional terms or conditions. \ No newline at end of file +shall be licensed as above, without any additional terms or conditions. diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..daef9d2 --- /dev/null +++ b/build.rs @@ -0,0 +1,37 @@ +use anyhow::{anyhow, Result}; +use std::{env, process::Command}; + +fn cmd(arg: &str) -> Result<()> { + let mut cmd = if cfg!(target_os = "windows") { + let mut cmd = Command::new("cmd"); + cmd.arg("/C"); + cmd + } else { + let mut cmd = Command::new("sh"); + cmd.arg("-c"); + cmd + }; + let output = cmd.arg(arg).output()?; + if !output.status.success() { + return Err(anyhow!( + "failed running command: {}\n{}", + arg, + &String::from_utf8(output.stderr)? + )); + } + Ok(()) +} + +fn main() -> Result<()> { + if env::var("BUILDING_VIRT").is_err() { + env::set_var("BUILDING_VIRT", "1"); + cmd("cargo +nightly build -p virtual-adapter --target wasm32-wasi --release -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort")?; + cmd("cp target/wasm32-wasi/release/virtual_adapter.wasm lib/")?; + } + + println!("cargo:rerun-if-changed=Cargo.toml"); + println!("cargo:rerun-if-changed=virtual-adapter/Cargo.toml"); + println!("cargo:rerun-if-changed=virtual-adapter/src/lib.rs"); + println!("cargo:rerun-if-changed=build.rs"); + Ok(()) +} diff --git a/lib/virtual_adapter.wasm b/lib/virtual_adapter.wasm new file mode 100755 index 0000000000000000000000000000000000000000..44ad6bea059c429759f385631bc221bd48146b99 GIT binary patch literal 28359 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 |aV}2&z!d{qY-IF{#^QwIr*t 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 literal 0 HcmV?d00001 diff --git a/lib/wasi_snapshot_preview1.reactor.wasm b/lib/wasi_snapshot_preview1.reactor.wasm new file mode 100644 index 0000000000000000000000000000000000000000..5d4017c752afa427a92f5d8e957b245ba408740c GIT binary patch literal 105973 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 literal 0 HcmV?d00001 diff --git a/src/bin/wasi-virt.rs b/src/bin/wasi-virt.rs new file mode 100644 index 0000000..bc3e6ef --- /dev/null +++ b/src/bin/wasi-virt.rs @@ -0,0 +1,48 @@ +use anyhow::Result; +use clap::Parser; +use std::fs; +use wasi_virt::{create_virt, VirtOpts}; + +#[derive(Parser, Debug)] +#[command(verbatim_doc_comment, author, version, about)] +/// WASI Virt CLI +/// +/// Creates a virtualization component with the provided virtualization configuration. +/// +/// This virtualization component can then be composed into a WASI component via: +/// +/// wasm-tools compose component.wasm -d virt.wasm -o final.wasm +/// +struct Args { + /// Virtualization TOML configuration + /// + /// Example configuration: + /// + /// [env] + /// host = "All" # or "None" + /// overrides = [["CUSTOM", "VAL"]] + /// + /// Alternatively, allow or deny env keys for the host can be configured via: + /// + /// [env.host] + /// Allow = ["ENV_KEY"] # Or Deny = ... + /// + #[arg(short, long, verbatim_doc_comment)] + config: String, + + /// Output virtualization component Wasm file + #[arg(short, long)] + out: String, +} + +fn main() -> Result<()> { + let args = Args::parse(); + + let virt_cfg: VirtOpts = toml::from_str(&fs::read_to_string(&args.config)?)?; + + let virt_component = create_virt(&virt_cfg)?; + + fs::write(args.out, virt_component)?; + + Ok(()) +} diff --git a/src/env.rs b/src/env.rs new file mode 100644 index 0000000..02bcf8d --- /dev/null +++ b/src/env.rs @@ -0,0 +1,218 @@ +use anyhow::{bail, Context, Result}; +use serde::Deserialize; +use walrus::{ + ir::Value, ActiveData, ActiveDataLocation, DataKind, ExportItem, GlobalKind, InitExpr, Module, +}; + +use crate::{ + walrus_ops::{bump_stack_global, get_active_data_segment, stub_imported_func}, + WasiVirt, +}; + +#[derive(Deserialize, Debug, Clone, Default)] +pub struct VirtEnv { + /// Set specific environment variable overrides + overrides: Vec<(String, String)>, + /// Define how to embed into the host environment + /// (Pass-through / encapsulate / allow / deny) + host: HostEnv, +} + +#[derive(Deserialize, Debug, Clone, Default)] +#[serde(rename_all = "snake_case")] +pub enum HostEnv { + /// Apart from the overrides, pass through all environment + /// variables from the host + #[default] + All, + /// Fully encapsulate the environment, removing all host + /// environment import checks + None, + /// Only allow the provided environment variable keys + Allow(Vec), + /// Allow all environment variables, except the provided keys + Deny(Vec), +} + +impl WasiVirt { + fn get_or_create_env(&mut self) -> &mut VirtEnv { + self.virt_opts.env.get_or_insert_with(Default::default) + } + + pub fn env_host_allow(mut self, allow_list: &[&str]) -> Self { + let env = self.get_or_create_env(); + env.host = HostEnv::Allow(allow_list.iter().map(|s| s.to_string()).collect()); + self + } + + pub fn env_host_deny(mut self, deny_list: &[&str]) -> Self { + let env = self.get_or_create_env(); + env.host = HostEnv::Deny(deny_list.iter().map(|s| s.to_string()).collect()); + self + } + + pub fn env_host_all(mut self) -> Self { + let env = self.get_or_create_env(); + env.host = HostEnv::All; + self + } + + pub fn env_host_none(mut self) -> Self { + let env = self.get_or_create_env(); + env.host = HostEnv::None; + self + } + + pub fn env_overrides(mut self, overrides: &[(&str, &str)]) -> Self { + let env = self.get_or_create_env(); + for (key, val) in overrides { + env.overrides.push((key.to_string(), val.to_string())); + } + self + } +} + +pub fn create_env_virt<'a>(module: &'a mut Module, env: &VirtEnv) -> Result<()> { + let env_ptr_addr = { + let env_ptr_export = module + .exports + .iter() + .find(|expt| expt.name.as_str() == "env") + .context("Adapter 'env' is not exported")?; + let ExportItem::Global(env_ptr_global) = env_ptr_export.item else { + bail!("Adapter 'env' not a global"); + }; + let GlobalKind::Local(InitExpr::Value(Value::I32(env_ptr_addr))) = + &module.globals.get(env_ptr_global).kind + else { + bail!("Adapter 'env' not a local I32 global value"); + }; + *env_ptr_addr as u32 + }; + + // If host env is disabled, remove its import entirely + // replacing it with a stub panic + if matches!(env.host, HostEnv::None) { + stub_imported_func(module, "wasi:cli-base/environment", "get-environment")?; + // we do arguments as well because virt assumes reactors for now... + stub_imported_func(module, "wasi:cli-base/environment", "get-arguments")?; + } + + let memory = module + .memories + .iter() + .nth(0) + .context("Adapter does not export a memory")? + .id(); + + // prepare the field data list vector for writing + // strings must be sorted as binary searches are used against this data + let mut field_data_vec: Vec<&str> = Vec::new(); + let mut sorted_overrides = env.overrides.clone(); + sorted_overrides.sort_by(|(a, _), (b, _)| a.cmp(b)); + for (key, value) in &sorted_overrides { + field_data_vec.push(key.as_ref()); + field_data_vec.push(value.as_ref()); + } + match &env.host { + HostEnv::Allow(allow_list) => { + let mut allow_list: Vec<&str> = allow_list.iter().map(|item| item.as_ref()).collect(); + allow_list.sort(); + for key in allow_list { + field_data_vec.push(key); + } + } + HostEnv::Deny(deny_list) => { + let mut deny_list: Vec<&str> = deny_list.iter().map(|item| item.as_ref()).collect(); + deny_list.sort(); + for key in deny_list { + field_data_vec.push(key); + } + } + _ => {} + } + + let mut field_data_bytes = Vec::new(); + for str in field_data_vec { + assert!(field_data_bytes.len() % 4 == 0); + // write the length at the aligned offset + field_data_bytes.extend_from_slice(&(str.len() as u32).to_le_bytes()); + let str_bytes = str.as_bytes(); + field_data_bytes.extend_from_slice(str_bytes); + let rem = str_bytes.len() % 4; + // add padding for alignment if necessary + if rem > 0 { + field_data_bytes.extend((0..4 - rem).map(|_| 0)); + } + } + + let field_data_addr = if field_data_bytes.len() > 0 { + // Offset the stack global by the static field data length + let field_data_addr = bump_stack_global(module, field_data_bytes.len() as i32)?; + + // Add a new data segment for this new range created at the top of the stack + module.data.add( + DataKind::Active(ActiveData { + memory, + location: ActiveDataLocation::Absolute(field_data_addr), + }), + field_data_bytes, + ); + Some(field_data_addr) + } else { + None + }; + + // In the existing static data segment, update the static data options. + // + // From virtual-adapter/src/lib.js: + // + // #[repr(C)] + // pub struct Env { + // /// Whether to fallback to the host env + // /// [byte 0] + // host_fallback: bool, + // /// Whether we are providing an allow list or a deny list + // /// on the fallback lookups + // /// [byte 1] + // host_fallback_allow: bool, + // /// How many host fields are defined in the data pointer + // /// [byte 4] + // host_field_cnt: u32, + // /// Host many host fields are defined to be allow or deny + // /// (these are concatenated at the end of the data with empty values) + // /// [byte 8] + // host_allow_or_deny_cnt: u32, + // /// Byte data of u32 byte len followed by string bytes + // /// up to the lengths previously provided. + // /// [byte 12] + // host_field_data: *const u8, + // } + let (data, data_offset) = get_active_data_segment(module, memory, env_ptr_addr)?; + let bytes = data.value.as_mut_slice(); + + let host_field_cnt = env.overrides.len() as u32; + bytes[data_offset + 4..data_offset + 8].copy_from_slice(&host_field_cnt.to_le_bytes()); + match &env.host { + // All is already the default data + HostEnv::All => {} + HostEnv::None => { + bytes[data_offset] = 0; + } + HostEnv::Allow(allow_list) => { + bytes[data_offset + 1] = 1; + bytes[data_offset + 8..data_offset + 12] + .copy_from_slice(&(allow_list.len() as u32).to_le_bytes()); + } + HostEnv::Deny(deny_list) => { + bytes[data_offset + 1] = 0; + bytes[data_offset + 8..data_offset + 12] + .copy_from_slice(&(deny_list.len() as u32).to_le_bytes()); + } + }; + if let Some(field_data_addr) = field_data_addr { + bytes[data_offset + 12..data_offset + 16].copy_from_slice(&field_data_addr.to_le_bytes()); + } + + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..87a1466 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,46 @@ +use anyhow::Result; +use env::{create_env_virt, VirtEnv}; +use serde::Deserialize; +use wit_component::ComponentEncoder; + +mod env; +mod walrus_ops; + +#[derive(Deserialize, Debug, Default, Clone)] +pub struct VirtOpts { + /// Environment virtualization + env: Option, +} + +#[derive(Debug, Default, Clone)] +pub struct WasiVirt { + virt_opts: VirtOpts, +} + +impl WasiVirt { + pub fn new() -> Self { + Self::default() + } + + pub fn create(&self) -> Result> { + create_virt(&self.virt_opts) + } +} + +pub fn create_virt<'a>(opts: &VirtOpts) -> Result> { + let virt_adapter = include_bytes!("../lib/virtual_adapter.wasm"); + + let config = walrus::ModuleConfig::new(); + let mut module = config.parse(virt_adapter)?; + + // env virtualization injection + if let Some(env) = &opts.env { + create_env_virt(&mut module, env)?; + } + + let bytes = module.emit_wasm(); + + // now adapt the virtualized component + let encoder = ComponentEncoder::default().validate(true).module(&bytes)?; + encoder.encode() +} diff --git a/src/walrus_ops.rs b/src/walrus_ops.rs new file mode 100644 index 0000000..0e8f159 --- /dev/null +++ b/src/walrus_ops.rs @@ -0,0 +1,99 @@ +use anyhow::{bail, Context, Result}; +use walrus::{ + ir::Value, ActiveData, ActiveDataLocation, Data, DataKind, Function, FunctionBuilder, + FunctionKind, GlobalKind, ImportKind, ImportedFunction, InitExpr, MemoryId, Module, +}; + +pub(crate) fn get_active_data_segment( + module: &mut Module, + mem: MemoryId, + addr: u32, +) -> Result<(&mut Data, usize)> { + let data = module + .data + .iter() + .find(|&data| { + let DataKind::Active(active_data) = &data.kind else { + return false; + }; + if active_data.memory != mem { + return false; + }; + let ActiveDataLocation::Absolute(loc) = &active_data.location else { + return false; + }; + *loc <= addr && *loc + data.value.len() as u32 > addr + }) + .context("Unable to find data section for env ptr")?; + let DataKind::Active(ActiveData { + location: ActiveDataLocation::Absolute(loc), + .. + }) = &data.kind + else { + unreachable!() + }; + let data_id = data.id(); + let offset = (addr - *loc) as usize; + Ok((module.data.get_mut(data_id), offset)) +} + +pub(crate) fn bump_stack_global(module: &mut Module, offset: i32) -> Result { + let stack_global_id = module + .globals + .iter() + .find(|&global| global.name.as_deref() == Some("__stack_pointer")) + .context("Unable to find __stack_pointer global name")? + .id(); + let stack_global = module.globals.get_mut(stack_global_id); + let GlobalKind::Local(InitExpr::Value(Value::I32(stack_value))) = &mut stack_global.kind else { + bail!("Stack global is not a constant I32"); + }; + if offset > *stack_value { + bail!( + "Stack size {} is smaller than the offset {offset}", + *stack_value + ); + } + let new_stack_value = *stack_value - offset; + *stack_value = new_stack_value; + Ok(new_stack_value as u32) +} + +pub(crate) fn stub_imported_func( + module: &mut Module, + import_module: &str, + import_name: &str, +) -> Result<()> { + let imported_fn = module + .imports + .iter() + .find(|impt| impt.module == import_module && impt.name == import_name) + .unwrap(); + + let ImportKind::Function(fid) = imported_fn.kind else { + bail!("Unable to stub import {import_module}#{import_name}, as it is not an imported function"); + }; + let Function { + kind: FunctionKind::Import(ImportedFunction { ty: tid, .. }), + .. + } = module.funcs.get(fid) + else { + bail!("Unable to stub import {import_module}#{import_name}, as it is not an imported function"); + }; + + let ty = module.types.get(*tid); + let (params, results) = (ty.params().to_vec(), ty.results().to_vec()); + + let mut builder = FunctionBuilder::new(&mut module.types, ¶ms, &results); + builder.func_body().unreachable(); + let local_func = builder.local_func(vec![]); + + // substitute the local func into the imported func id + let func = module.funcs.get_mut(fid); + func.kind = FunctionKind::Local(local_func); + + // remove the import + module.imports.delete(imported_fn.id()); + + Ok(()) +} diff --git a/tests/cases/env-allow.toml b/tests/cases/env-allow.toml new file mode 100644 index 0000000..0e0bab1 --- /dev/null +++ b/tests/cases/env-allow.toml @@ -0,0 +1,14 @@ +component = "get-env" + +[host_env] +PRIVATE_TOKEN = "PRIVATE" +PUBLIC_VAR = "VAL" + +[virt_opts.env] +overrides = [["CUSTOM", "VAL"]] + +[virt_opts.env.host] +allow = ["PUBLIC_VAR"] + +[expect] +env = [["CUSTOM", "VAL"], ["PUBLIC_VAR", "VAL"]] diff --git a/tests/cases/env-deny.toml b/tests/cases/env-deny.toml new file mode 100644 index 0000000..1890153 --- /dev/null +++ b/tests/cases/env-deny.toml @@ -0,0 +1,14 @@ +component = "get-env" + +[host_env] +PRIVATE_TOKEN = "PRIVATE" +PUBLIC_VAR = "VAL" + +[virt_opts.env] +overrides = [["CUSTOM", "VAL"]] + +[virt_opts.env.host] +deny = ["PRIVATE_TOKEN"] + +[expect] +env = [["CUSTOM", "VAL"], ["PUBLIC_VAR", "VAL"]] diff --git a/tests/cases/env-none-overrides.toml b/tests/cases/env-none-overrides.toml new file mode 100644 index 0000000..28e8a66 --- /dev/null +++ b/tests/cases/env-none-overrides.toml @@ -0,0 +1,11 @@ +component = "get-env" + +[host_env] +CUSTOM = "TEST" + +[virt_opts.env] +host = "none" +overrides = [["ENV_OVERRIDE", "Value"]] + +[expect] +env = [["ENV_OVERRIDE", "Value"]] diff --git a/tests/cases/env-none.toml b/tests/cases/env-none.toml new file mode 100644 index 0000000..c303343 --- /dev/null +++ b/tests/cases/env-none.toml @@ -0,0 +1,11 @@ +component = "get-env" + +[host_env] +CUSTOM = "TEST" + +[virt_opts.env] +host = "none" +overrides = [] + +[expect] +env = [] diff --git a/tests/cases/env-passthrough.toml b/tests/cases/env-passthrough.toml new file mode 100644 index 0000000..6839e64 --- /dev/null +++ b/tests/cases/env-passthrough.toml @@ -0,0 +1,11 @@ +component = "get-env" + +[host_env] +CUSTOM = "ENV" + +[virt_opts.env] +host = "all" +overrides = [] + +[expect] +env = [["CUSTOM", "ENV"]] diff --git a/tests/components/get-env/Cargo.toml b/tests/components/get-env/Cargo.toml new file mode 100644 index 0000000..ed1f5ac --- /dev/null +++ b/tests/components/get-env/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "get-env" +version = "0.1.0" +publish = false + +[lib] +crate-type = ["cdylib"] + +[dependencies] +anyhow = { workspace = true } +wit-bindgen = { workspace = true } diff --git a/tests/components/get-env/src/lib.rs b/tests/components/get-env/src/lib.rs new file mode 100644 index 0000000..1f0d614 --- /dev/null +++ b/tests/components/get-env/src/lib.rs @@ -0,0 +1,16 @@ +use std::env; + +wit_bindgen::generate!({ + path: "../../../wit", + world: "virt-test" +}); + +struct VirtTestImpl; + +export_virt_test!(VirtTestImpl); + +impl VirtTest for VirtTestImpl { + fn test_get_env() -> Vec<(String, String)> { + env::vars().collect() + } +} diff --git a/tests/virt.rs b/tests/virt.rs new file mode 100644 index 0000000..d67cc7e --- /dev/null +++ b/tests/virt.rs @@ -0,0 +1,191 @@ +use anyhow::{anyhow, Result}; +use heck::ToSnakeCase; +use serde::Deserialize; +use std::collections::BTreeMap; +use std::process::Command; +use std::{fs, path::PathBuf}; +use wasi_virt::{create_virt, VirtOpts}; +use wasm_compose::composer::ComponentComposer; +use wasmtime::{ + component::{Component, Linker}, + Config, Engine, Store, WasmBacktraceDetails, +}; +use wasmtime_wasi::preview2::{wasi as wasi_preview2, Table, WasiCtx, WasiCtxBuilder, WasiView}; +use wit_component::ComponentEncoder; + +wasmtime::component::bindgen!({ + world: "virt-test", + path: "wit", + async: true +}); + +fn cmd(arg: &str) -> Result<()> { + let mut cmd = if cfg!(target_os = "windows") { + let mut cmd = Command::new("cmd"); + cmd.arg("/C"); + cmd + } else { + let mut cmd = Command::new("sh"); + cmd.arg("-c"); + cmd + }; + let output = cmd.arg(arg).output()?; + if !output.status.success() { + return Err(anyhow!( + "failed running command: {}\n{}", + arg, + &String::from_utf8(output.stderr)? + )); + } + Ok(()) +} + +#[derive(Deserialize, Debug)] +struct TestExpectation { + env: Option>, +} + +#[derive(Deserialize, Debug)] +struct TestCase { + component: String, + host_env: Option>, + virt_opts: Option, + expect: TestExpectation, +} + +#[async_std::test] +async fn virt_test() -> Result<()> { + let wasi_adapter = fs::read("lib/wasi_snapshot_preview1.reactor.wasm")?; + + for test_case in fs::read_dir("tests/cases")? { + let test_case = test_case?; + let test_case_path = test_case.path(); + let test_case_file_name = test_case.file_name().to_string_lossy().to_string(); + let test_case_name = test_case_file_name.strip_suffix(".toml").unwrap(); + + // load the test case JSON data + let test: TestCase = toml::from_str(&fs::read_to_string(&test_case_path)?)?; + + let component_name = &test.component; + + // build the generated test component + let generated_path = PathBuf::from("tests/generated"); + fs::create_dir_all(&generated_path)?; + + let mut generated_component_path = generated_path.join(component_name); + generated_component_path.set_extension("component.wasm"); + cmd(&format!( + "cargo build -p {component_name} --target wasm32-wasi --release" + ))?; + + // encode the component + let component_core = fs::read(&format!( + "target/wasm32-wasi/release/{}.wasm", + component_name.to_snake_case() + ))?; + let mut encoder = ComponentEncoder::default() + .validate(true) + .module(&component_core)?; + encoder = encoder.adapter("wasi_snapshot_preview1", wasi_adapter.as_slice())?; + fs::write(&generated_component_path, encoder.encode()?)?; + + // create the test case specific virtualization + let mut virt_component_path = generated_path.join(test_case_name); + virt_component_path.set_extension("virt.wasm"); + let virt_opts = test.virt_opts.clone().unwrap_or_default(); + let virt_component = create_virt(&virt_opts)?; + fs::write(&virt_component_path, virt_component)?; + + // compose the test component with the defined test virtualization + let component_bytes = ComponentComposer::new( + &generated_component_path, + &wasm_compose::config::Config { + definitions: vec![virt_component_path], + ..Default::default() + }, + ) + .compose()?; + + if true { + let mut composed_path = generated_path.join(test_case_name); + composed_path.set_extension("composed.wasm"); + fs::write(composed_path, &component_bytes)?; + } + + // execute the composed virtualized component test function + let mut builder = WasiCtxBuilder::new().inherit_stdio(); + if let Some(host_env) = &test.host_env { + let env: Vec<(String, String)> = host_env + .iter() + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect(); + builder = builder.set_env(env.as_slice()); + } + let mut table = Table::new(); + let wasi = builder.build(&mut table)?; + + let mut config = Config::new(); + config.cache_config_load_default().unwrap(); + config.wasm_backtrace_details(WasmBacktraceDetails::Enable); + config.wasm_component_model(true); + config.async_support(true); + + let engine = Engine::new(&config)?; + let mut linker = Linker::new(&engine); + + let component = Component::from_binary(&engine, &component_bytes).unwrap(); + + struct CommandCtx { + table: Table, + wasi: WasiCtx, + } + impl WasiView for CommandCtx { + fn table(&self) -> &Table { + &self.table + } + fn table_mut(&mut self) -> &mut Table { + &mut self.table + } + fn ctx(&self) -> &WasiCtx { + &self.wasi + } + fn ctx_mut(&mut self) -> &mut WasiCtx { + &mut self.wasi + } + } + + // simple logger for debugging + let mut log_builder = linker.instance("console")?; + log_builder.func_wrap("log", |_store, params: (String,)| { + println!("LOG: {}", params.0); + Ok(()) + })?; + + wasi_preview2::command::add_to_linker(&mut linker)?; + let mut store = Store::new(&engine, CommandCtx { table, wasi }); + + let (instance, _instance) = + VirtTest::instantiate_async(&mut store, &component, &linker).await?; + + // env var expectation check + if let Some(env) = &test.expect.env { + let env_vars = instance.call_test_get_env(&mut store).await?; + if !env_vars.eq(env) { + return Err(anyhow!( + "Unexpected env vars testing {:?}: + + \x1b[1mExpected:\x1b[0m {:?} + \x1b[1mActual:\x1b[0m {:?} + + {:?}", + test_case_path, + env, + env_vars, + test + )); + } + } + println!("\x1b[1;32m√\x1b[0m {:?}", test_case_path); + } + Ok(()) +} diff --git a/virtual-adapter/Cargo.toml b/virtual-adapter/Cargo.toml new file mode 100644 index 0000000..fe3c12f --- /dev/null +++ b/virtual-adapter/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "virtual-adapter" +publish = false +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +anyhow = { workspace = true } +wit-bindgen = { workspace = true } diff --git a/virtual-adapter/src/lib.rs b/virtual-adapter/src/lib.rs new file mode 100644 index 0000000..3630368 --- /dev/null +++ b/virtual-adapter/src/lib.rs @@ -0,0 +1,97 @@ +#![no_main] + +wit_bindgen::generate!({ + path: "../wit", + world: "virtual-adapter" +}); + +use crate::exports::wasi::cli_base::environment::Environment; + +struct VirtAdapter; + +export_virtual_adapter!(VirtAdapter); + +#[repr(C)] +pub struct Env { + /// Whether to fallback to the host env + /// [byte 0] + host_fallback: bool, + /// Whether we are providing an allow list or a deny list + /// on the fallback lookups + /// [byte 1] + host_fallback_allow: bool, + /// How many host fields are defined in the data pointer + /// [byte 4] + host_field_cnt: u32, + /// Host many host fields are defined to be allow or deny + /// (these are concatenated at the end of the data with empty values) + /// [byte 8] + host_allow_or_deny_cnt: u32, + /// Byte data of u32 byte len followed by string bytes + /// up to the lengths previously provided. + /// [byte 12] + host_field_data: *const u8, +} + +#[no_mangle] +pub static mut env: Env = Env { + host_fallback: true, + host_fallback_allow: false, + host_field_cnt: 0, + host_allow_or_deny_cnt: 0, + host_field_data: 0 as *const u8, +}; + +fn read_data_str(offset: &mut isize) -> &'static str { + let data: *const u8 = unsafe { env.host_field_data.offset(*offset) }; + let byte_len = unsafe { (data as *const u32).read() } as usize; + *offset += 4; + let data: *const u8 = unsafe { env.host_field_data.offset(*offset) }; + let str_data = unsafe { std::slice::from_raw_parts(data, byte_len) }; + *offset += byte_len as isize; + let rem = *offset % 4; + if rem > 0 { + *offset += 4 - rem; + } + unsafe { core::str::from_utf8_unchecked(str_data) } +} + +impl Environment for VirtAdapter { + fn get_environment() -> Vec<(String, String)> { + let mut environment = Vec::new(); + let mut data_offset: isize = 0; + for _ in 0..unsafe { env.host_field_cnt } { + let env_key = read_data_str(&mut data_offset); + let env_val = read_data_str(&mut data_offset); + environment.push((env_key.to_string(), env_val.to_string())); + } + let override_len = environment.len(); + // fallback ASSUMES that all data is alphabetically ordered + if unsafe { env.host_fallback } { + let mut allow_or_deny = Vec::new(); + for _ in 0..unsafe { env.host_allow_or_deny_cnt } { + let allow_or_deny_key = read_data_str(&mut data_offset); + allow_or_deny.push(allow_or_deny_key); + } + + let is_allow_list = unsafe { env.host_fallback_allow }; + for (key, value) in wasi::cli_base::environment::get_environment() { + if environment[0..override_len] + .binary_search_by_key(&&key, |(s, _)| s) + .is_ok() + { + continue; + } + let in_list = allow_or_deny.binary_search(&key.as_ref()).is_ok(); + if is_allow_list && in_list || !is_allow_list && !in_list { + environment.push((key, value)); + } + } + } + environment + } + + fn get_arguments() -> Vec { + wasi::cli_base::environment::get_arguments() + } +} diff --git a/wit/deps/clocks/monotonic-clock.wit b/wit/deps/clocks/monotonic-clock.wit new file mode 100644 index 0000000..50eb4de --- /dev/null +++ b/wit/deps/clocks/monotonic-clock.wit @@ -0,0 +1,34 @@ +package wasi:clocks + +/// WASI Monotonic Clock is a clock API intended to let users measure elapsed +/// time. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +/// +/// A monotonic clock is a clock which has an unspecified initial value, and +/// successive reads of the clock will produce non-decreasing values. +/// +/// It is intended for measuring elapsed time. +interface monotonic-clock { + use wasi:poll/poll.{pollable} + + /// A timestamp in nanoseconds. + type instant = u64 + + /// Read the current value of the clock. + /// + /// The clock is monotonic, therefore calling this function repeatedly will + /// produce a sequence of non-decreasing values. + now: func() -> instant + + /// Query the resolution of the clock. + resolution: func() -> instant + + /// Create a `pollable` which will resolve once the specified time has been + /// reached. + subscribe: func( + when: instant, + absolute: bool + ) -> pollable +} diff --git a/wit/deps/clocks/timezone.wit b/wit/deps/clocks/timezone.wit new file mode 100644 index 0000000..2b68556 --- /dev/null +++ b/wit/deps/clocks/timezone.wit @@ -0,0 +1,63 @@ +package wasi:clocks + +interface timezone { + use wall-clock.{datetime} + + /// A timezone. + /// + /// In timezones that recognize daylight saving time, also known as daylight + /// time and summer time, the information returned from the functions varies + /// over time to reflect these adjustments. + /// + /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). + type timezone = u32 + + /// Return information needed to display the given `datetime`. This includes + /// the UTC offset, the time zone name, and a flag indicating whether + /// daylight saving time is active. + /// + /// If the timezone cannot be determined for the given `datetime`, return a + /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight + /// saving time. + display: func(this: timezone, when: datetime) -> timezone-display + + /// The same as `display`, but only return the UTC offset. + utc-offset: func(this: timezone, when: datetime) -> s32 + + /// Dispose of the specified input-stream, after which it may no longer + /// be used. + drop-timezone: func(this: timezone) + + /// Information useful for displaying the timezone of a specific `datetime`. + /// + /// This information may vary within a single `timezone` to reflect daylight + /// saving time adjustments. + record timezone-display { + /// The number of seconds difference between UTC time and the local + /// time of the timezone. + /// + /// The returned value will always be less than 86400 which is the + /// number of seconds in a day (24*60*60). + /// + /// In implementations that do not expose an actual time zone, this + /// should return 0. + utc-offset: s32, + + /// The abbreviated name of the timezone to display to a user. The name + /// `UTC` indicates Coordinated Universal Time. Otherwise, this should + /// reference local standards for the name of the time zone. + /// + /// In implementations that do not expose an actual time zone, this + /// should be the string `UTC`. + /// + /// In time zones that do not have an applicable name, a formatted + /// representation of the UTC offset may be returned, such as `-04:00`. + name: string, + + /// Whether daylight saving time is active. + /// + /// In implementations that do not expose an actual time zone, this + /// should return false. + in-daylight-saving-time: bool, + } +} diff --git a/wit/deps/clocks/wall-clock.wit b/wit/deps/clocks/wall-clock.wit new file mode 100644 index 0000000..6137724 --- /dev/null +++ b/wit/deps/clocks/wall-clock.wit @@ -0,0 +1,43 @@ +package wasi:clocks + +/// WASI Wall Clock is a clock API intended to let users query the current +/// time. The name "wall" makes an analogy to a "clock on the wall", which +/// is not necessarily monotonic as it may be reset. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +/// +/// A wall clock is a clock which measures the date and time according to +/// some external reference. +/// +/// External references may be reset, so this clock is not necessarily +/// monotonic, making it unsuitable for measuring elapsed time. +/// +/// It is intended for reporting the current date and time for humans. +interface wall-clock { + /// A time and date in seconds plus nanoseconds. + record datetime { + seconds: u64, + nanoseconds: u32, + } + + /// Read the current value of the clock. + /// + /// This clock is not monotonic, therefore calling this function repeatedly + /// will not necessarily produce a sequence of non-decreasing values. + /// + /// The returned timestamps represent the number of seconds since + /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], + /// also known as [Unix Time]. + /// + /// The nanoseconds field of the output is always less than 1000000000. + /// + /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 + /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time + now: func() -> datetime + + /// Query the resolution of the clock. + /// + /// The nanoseconds field of the output is always less than 1000000000. + resolution: func() -> datetime +} diff --git a/wit/deps/filesystem/filesystem.wit b/wit/deps/filesystem/filesystem.wit new file mode 100644 index 0000000..930f375 --- /dev/null +++ b/wit/deps/filesystem/filesystem.wit @@ -0,0 +1,782 @@ +package wasi:filesystem + +/// WASI filesystem is a filesystem API primarily intended to let users run WASI +/// programs that access their files on their existing filesystems, without +/// significant overhead. +/// +/// It is intended to be roughly portable between Unix-family platforms and +/// Windows, though it does not hide many of the major differences. +/// +/// Paths are passed as interface-type `string`s, meaning they must consist of +/// a sequence of Unicode Scalar Values (USVs). Some filesystems may contain +/// paths which are not accessible by this API. +/// +/// The directory separator in WASI is always the forward-slash (`/`). +/// +/// All paths in WASI are relative paths, and are interpreted relative to a +/// `descriptor` referring to a base directory. If a `path` argument to any WASI +/// function starts with `/`, or if any step of resolving a `path`, including +/// `..` and symbolic link steps, reaches a directory outside of the base +/// directory, or reaches a symlink to an absolute or rooted path in the +/// underlying filesystem, the function fails with `error-code::not-permitted`. +interface filesystem { + use wasi:io/streams.{input-stream, output-stream} + use wasi:clocks/wall-clock.{datetime} + + /// File size or length of a region within a file. + type filesize = u64 + + /// The type of a filesystem object referenced by a descriptor. + /// + /// Note: This was called `filetype` in earlier versions of WASI. + enum descriptor-type { + /// The type of the descriptor or file is unknown or is different from + /// any of the other types specified. + unknown, + /// The descriptor refers to a block device inode. + block-device, + /// The descriptor refers to a character device inode. + character-device, + /// The descriptor refers to a directory inode. + directory, + /// The descriptor refers to a named pipe. + fifo, + /// The file refers to a symbolic link inode. + symbolic-link, + /// The descriptor refers to a regular file inode. + regular-file, + /// The descriptor refers to a socket. + socket, + } + + /// Descriptor flags. + /// + /// Note: This was called `fdflags` in earlier versions of WASI. + flags descriptor-flags { + /// Read mode: Data can be read. + read, + /// Write mode: Data can be written to. + write, + /// Request that writes be performed according to synchronized I/O file + /// integrity completion. The data stored in the file and the file's + /// metadata are synchronized. This is similar to `O_SYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + file-integrity-sync, + /// Request that writes be performed according to synchronized I/O data + /// integrity completion. Only the data stored in the file is + /// synchronized. This is similar to `O_DSYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + data-integrity-sync, + /// Requests that reads be performed at the same level of integrety + /// requested for writes. This is similar to `O_RSYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + requested-write-sync, + /// Mutating directories mode: Directory contents may be mutated. + /// + /// When this flag is unset on a descriptor, operations using the + /// descriptor which would create, rename, delete, modify the data or + /// metadata of filesystem objects, or obtain another handle which + /// would permit any of those, shall fail with `error-code::read-only` if + /// they would otherwise succeed. + /// + /// This may only be set on directories. + mutate-directory, + } + + /// File attributes. + /// + /// Note: This was called `filestat` in earlier versions of WASI. + record descriptor-stat { + /// Device ID of device containing the file. + device: device, + /// File serial number. + inode: inode, + /// File type. + %type: descriptor-type, + /// Number of hard links to the file. + link-count: link-count, + /// For regular files, the file size in bytes. For symbolic links, the + /// length in bytes of the pathname contained in the symbolic link. + size: filesize, + /// Last data access timestamp. + data-access-timestamp: datetime, + /// Last data modification timestamp. + data-modification-timestamp: datetime, + /// Last file status change timestamp. + status-change-timestamp: datetime, + } + + /// Flags determining the method of how paths are resolved. + flags path-flags { + /// As long as the resolved path corresponds to a symbolic link, it is + /// expanded. + symlink-follow, + } + + /// Open flags used by `open-at`. + flags open-flags { + /// Create file if it does not exist, similar to `O_CREAT` in POSIX. + create, + /// Fail if not a directory, similar to `O_DIRECTORY` in POSIX. + directory, + /// Fail if file already exists, similar to `O_EXCL` in POSIX. + exclusive, + /// Truncate file to size 0, similar to `O_TRUNC` in POSIX. + truncate, + } + + /// Permissions mode used by `open-at`, `change-file-permissions-at`, and + /// similar. + flags modes { + /// True if the resource is considered readable by the containing + /// filesystem. + readable, + /// True if the resource is considered writable by the containing + /// filesystem. + writable, + /// True if the resource is considered executable by the containing + /// filesystem. This does not apply to directories. + executable, + } + + /// Access type used by `access-at`. + variant access-type { + /// Test for readability, writeability, or executability. + access(modes), + + /// Test whether the path exists. + exists, + } + + /// Number of hard links to an inode. + type link-count = u64 + + /// Identifier for a device containing a file system. Can be used in + /// combination with `inode` to uniquely identify a file or directory in + /// the filesystem. + type device = u64 + + /// Filesystem object serial number that is unique within its file system. + type inode = u64 + + /// When setting a timestamp, this gives the value to set it to. + variant new-timestamp { + /// Leave the timestamp set to its previous value. + no-change, + /// Set the timestamp to the current time of the system clock associated + /// with the filesystem. + now, + /// Set the timestamp to the given value. + timestamp(datetime), + } + + /// A directory entry. + record directory-entry { + /// The serial number of the object referred to by this directory entry. + /// May be none if the inode value is not known. + /// + /// When this is none, libc implementations might do an extra `stat-at` + /// call to retrieve the inode number to fill their `d_ino` fields, so + /// implementations which can set this to a non-none value should do so. + inode: option, + + /// The type of the file referred to by this directory entry. + %type: descriptor-type, + + /// The name of the object. + name: string, + } + + /// Error codes returned by functions, similar to `errno` in POSIX. + /// Not all of these error codes are returned by the functions provided by this + /// API; some are used in higher-level library layers, and others are provided + /// merely for alignment with POSIX. + enum error-code { + /// Permission denied, similar to `EACCES` in POSIX. + access, + /// Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK` in POSIX. + would-block, + /// Connection already in progress, similar to `EALREADY` in POSIX. + already, + /// Bad descriptor, similar to `EBADF` in POSIX. + bad-descriptor, + /// Device or resource busy, similar to `EBUSY` in POSIX. + busy, + /// Resource deadlock would occur, similar to `EDEADLK` in POSIX. + deadlock, + /// Storage quota exceeded, similar to `EDQUOT` in POSIX. + quota, + /// File exists, similar to `EEXIST` in POSIX. + exist, + /// File too large, similar to `EFBIG` in POSIX. + file-too-large, + /// Illegal byte sequence, similar to `EILSEQ` in POSIX. + illegal-byte-sequence, + /// Operation in progress, similar to `EINPROGRESS` in POSIX. + in-progress, + /// Interrupted function, similar to `EINTR` in POSIX. + interrupted, + /// Invalid argument, similar to `EINVAL` in POSIX. + invalid, + /// I/O error, similar to `EIO` in POSIX. + io, + /// Is a directory, similar to `EISDIR` in POSIX. + is-directory, + /// Too many levels of symbolic links, similar to `ELOOP` in POSIX. + loop, + /// Too many links, similar to `EMLINK` in POSIX. + too-many-links, + /// Message too large, similar to `EMSGSIZE` in POSIX. + message-size, + /// Filename too long, similar to `ENAMETOOLONG` in POSIX. + name-too-long, + /// No such device, similar to `ENODEV` in POSIX. + no-device, + /// No such file or directory, similar to `ENOENT` in POSIX. + no-entry, + /// No locks available, similar to `ENOLCK` in POSIX. + no-lock, + /// Not enough space, similar to `ENOMEM` in POSIX. + insufficient-memory, + /// No space left on device, similar to `ENOSPC` in POSIX. + insufficient-space, + /// Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX. + not-directory, + /// Directory not empty, similar to `ENOTEMPTY` in POSIX. + not-empty, + /// State not recoverable, similar to `ENOTRECOVERABLE` in POSIX. + not-recoverable, + /// Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX. + unsupported, + /// Inappropriate I/O control operation, similar to `ENOTTY` in POSIX. + no-tty, + /// No such device or address, similar to `ENXIO` in POSIX. + no-such-device, + /// Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX. + overflow, + /// Operation not permitted, similar to `EPERM` in POSIX. + not-permitted, + /// Broken pipe, similar to `EPIPE` in POSIX. + pipe, + /// Read-only file system, similar to `EROFS` in POSIX. + read-only, + /// Invalid seek, similar to `ESPIPE` in POSIX. + invalid-seek, + /// Text file busy, similar to `ETXTBSY` in POSIX. + text-file-busy, + /// Cross-device link, similar to `EXDEV` in POSIX. + cross-device, + } + + /// File or memory access pattern advisory information. + enum advice { + /// The application has no advice to give on its behavior with respect + /// to the specified data. + normal, + /// The application expects to access the specified data sequentially + /// from lower offsets to higher offsets. + sequential, + /// The application expects to access the specified data in a random + /// order. + random, + /// The application expects to access the specified data in the near + /// future. + will-need, + /// The application expects that it will not access the specified data + /// in the near future. + dont-need, + /// The application expects to access the specified data once and then + /// not reuse it thereafter. + no-reuse, + } + + /// A descriptor is a reference to a filesystem object, which may be a file, + /// directory, named pipe, special file, or other object on which filesystem + /// calls may be made. + /// + /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). + type descriptor = u32 + + /// Return a stream for reading from a file. + /// + /// Multiple read, write, and append streams may be active on the same open + /// file and they do not interfere with each other. + /// + /// Note: This allows using `wasi:io/streams.read`, which is similar to `read` in POSIX. + read-via-stream: func( + this: descriptor, + /// The offset within the file at which to start reading. + offset: filesize, + ) -> result + + /// Return a stream for writing to a file. + /// + /// Note: This allows using `wasi:io/streams.write`, which is similar to `write` in + /// POSIX. + write-via-stream: func( + this: descriptor, + /// The offset within the file at which to start writing. + offset: filesize, + ) -> result + + /// Return a stream for appending to a file. + /// + /// Note: This allows using `wasi:io/streams.write`, which is similar to `write` with + /// `O_APPEND` in in POSIX. + append-via-stream: func( + this: descriptor, + ) -> result + + /// Provide file advisory information on a descriptor. + /// + /// This is similar to `posix_fadvise` in POSIX. + advise: func( + this: descriptor, + /// The offset within the file to which the advisory applies. + offset: filesize, + /// The length of the region to which the advisory applies. + length: filesize, + /// The advice. + advice: advice + ) -> result<_, error-code> + + /// Synchronize the data of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fdatasync` in POSIX. + sync-data: func(this: descriptor) -> result<_, error-code> + + /// Get flags associated with a descriptor. + /// + /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. + /// + /// Note: This returns the value that was the `fs_flags` value returned + /// from `fdstat_get` in earlier versions of WASI. + get-flags: func(this: descriptor) -> result + + /// Get the dynamic type of a descriptor. + /// + /// Note: This returns the same value as the `type` field of the `fd-stat` + /// returned by `stat`, `stat-at` and similar. + /// + /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided + /// by `fstat` in POSIX. + /// + /// Note: This returns the value that was the `fs_filetype` value returned + /// from `fdstat_get` in earlier versions of WASI. + get-type: func(this: descriptor) -> result + + /// Adjust the size of an open file. If this increases the file's size, the + /// extra bytes are filled with zeros. + /// + /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. + set-size: func(this: descriptor, size: filesize) -> result<_, error-code> + + /// Adjust the timestamps of an open file or directory. + /// + /// Note: This is similar to `futimens` in POSIX. + /// + /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. + set-times: func( + this: descriptor, + /// The desired values of the data access timestamp. + data-access-timestamp: new-timestamp, + /// The desired values of the data modification timestamp. + data-modification-timestamp: new-timestamp, + ) -> result<_, error-code> + + /// Read from a descriptor, without using and updating the descriptor's offset. + /// + /// This function returns a list of bytes containing the data that was + /// read, along with a bool which, when true, indicates that the end of the + /// file was reached. The returned list will contain up to `length` bytes; it + /// may return fewer than requested, if the end of the file is reached or + /// if the I/O operation is interrupted. + /// + /// In the future, this may change to return a `stream`. + /// + /// Note: This is similar to `pread` in POSIX. + read: func( + this: descriptor, + /// The maximum number of bytes to read. + length: filesize, + /// The offset within the file at which to read. + offset: filesize, + ) -> result, bool>, error-code> + + /// Write to a descriptor, without using and updating the descriptor's offset. + /// + /// It is valid to write past the end of a file; the file is extended to the + /// extent of the write, with bytes between the previous end and the start of + /// the write set to zero. + /// + /// In the future, this may change to take a `stream`. + /// + /// Note: This is similar to `pwrite` in POSIX. + write: func( + this: descriptor, + /// Data to write + buffer: list, + /// The offset within the file at which to write. + offset: filesize, + ) -> result + + /// Read directory entries from a directory. + /// + /// On filesystems where directories contain entries referring to themselves + /// and their parents, often named `.` and `..` respectively, these entries + /// are omitted. + /// + /// This always returns a new stream which starts at the beginning of the + /// directory. Multiple streams may be active on the same directory, and they + /// do not interfere with each other. + read-directory: func( + this: descriptor + ) -> result + + /// Synchronize the data and metadata of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fsync` in POSIX. + sync: func(this: descriptor) -> result<_, error-code> + + /// Create a directory. + /// + /// Note: This is similar to `mkdirat` in POSIX. + create-directory-at: func( + this: descriptor, + /// The relative path at which to create the directory. + path: string, + ) -> result<_, error-code> + + /// Return the attributes of an open file or directory. + /// + /// Note: This is similar to `fstat` in POSIX. + /// + /// Note: This was called `fd_filestat_get` in earlier versions of WASI. + stat: func(this: descriptor) -> result + + /// Return the attributes of a file or directory. + /// + /// Note: This is similar to `fstatat` in POSIX. + /// + /// Note: This was called `path_filestat_get` in earlier versions of WASI. + stat-at: func( + this: descriptor, + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the file or directory to inspect. + path: string, + ) -> result + + /// Adjust the timestamps of a file or directory. + /// + /// Note: This is similar to `utimensat` in POSIX. + /// + /// Note: This was called `path_filestat_set_times` in earlier versions of + /// WASI. + set-times-at: func( + this: descriptor, + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the file or directory to operate on. + path: string, + /// The desired values of the data access timestamp. + data-access-timestamp: new-timestamp, + /// The desired values of the data modification timestamp. + data-modification-timestamp: new-timestamp, + ) -> result<_, error-code> + + /// Create a hard link. + /// + /// Note: This is similar to `linkat` in POSIX. + link-at: func( + this: descriptor, + /// Flags determining the method of how the path is resolved. + old-path-flags: path-flags, + /// The relative source path from which to link. + old-path: string, + /// The base directory for `new-path`. + new-descriptor: descriptor, + /// The relative destination path at which to create the hard link. + new-path: string, + ) -> result<_, error-code> + + /// Open a file or directory. + /// + /// The returned descriptor is not guaranteed to be the lowest-numbered + /// descriptor not currently open/ it is randomized to prevent applications + /// from depending on making assumptions about indexes, since this is + /// error-prone in multi-threaded contexts. The returned descriptor is + /// guaranteed to be less than 2**31. + /// + /// If `flags` contains `descriptor-flags::mutate-directory`, and the base + /// descriptor doesn't have `descriptor-flags::mutate-directory` set, + /// `open-at` fails with `error-code::read-only`. + /// + /// If `flags` contains `write` or `mutate-directory`, or `open-flags` + /// contains `truncate` or `create`, and the base descriptor doesn't have + /// `descriptor-flags::mutate-directory` set, `open-at` fails with + /// `error-code::read-only`. + /// + /// Note: This is similar to `openat` in POSIX. + open-at: func( + this: descriptor, + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the object to open. + path: string, + /// The method by which to open the file. + open-flags: open-flags, + /// Flags to use for the resulting descriptor. + %flags: descriptor-flags, + /// Permissions to use when creating a new file. + modes: modes + ) -> result + + /// Read the contents of a symbolic link. + /// + /// If the contents contain an absolute or rooted path in the underlying + /// filesystem, this function fails with `error-code::not-permitted`. + /// + /// Note: This is similar to `readlinkat` in POSIX. + readlink-at: func( + this: descriptor, + /// The relative path of the symbolic link from which to read. + path: string, + ) -> result + + /// Remove a directory. + /// + /// Return `error-code::not-empty` if the directory is not empty. + /// + /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + remove-directory-at: func( + this: descriptor, + /// The relative path to a directory to remove. + path: string, + ) -> result<_, error-code> + + /// Rename a filesystem object. + /// + /// Note: This is similar to `renameat` in POSIX. + rename-at: func( + this: descriptor, + /// The relative source path of the file or directory to rename. + old-path: string, + /// The base directory for `new-path`. + new-descriptor: descriptor, + /// The relative destination path to which to rename the file or directory. + new-path: string, + ) -> result<_, error-code> + + /// Create a symbolic link (also known as a "symlink"). + /// + /// If `old-path` starts with `/`, the function fails with + /// `error-code::not-permitted`. + /// + /// Note: This is similar to `symlinkat` in POSIX. + symlink-at: func( + this: descriptor, + /// The contents of the symbolic link. + old-path: string, + /// The relative destination path at which to create the symbolic link. + new-path: string, + ) -> result<_, error-code> + + /// Check accessibility of a filesystem path. + /// + /// Check whether the given filesystem path names an object which is + /// readable, writable, or executable, or whether it exists. + /// + /// This does not a guarantee that subsequent accesses will succeed, as + /// filesystem permissions may be modified asynchronously by external + /// entities. + /// + /// Note: This is similar to `faccessat` with the `AT_EACCESS` flag in POSIX. + access-at: func( + this: descriptor, + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path to check. + path: string, + /// The type of check to perform. + %type: access-type + ) -> result<_, error-code> + + /// Unlink a filesystem object that is not a directory. + /// + /// Return `error-code::is-directory` if the path refers to a directory. + /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + unlink-file-at: func( + this: descriptor, + /// The relative path to a file to unlink. + path: string, + ) -> result<_, error-code> + + /// Change the permissions of a filesystem object that is not a directory. + /// + /// Note that the ultimate meanings of these permissions is + /// filesystem-specific. + /// + /// Note: This is similar to `fchmodat` in POSIX. + change-file-permissions-at: func( + this: descriptor, + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path to operate on. + path: string, + /// The new permissions for the filesystem object. + modes: modes, + ) -> result<_, error-code> + + /// Change the permissions of a directory. + /// + /// Note that the ultimate meanings of these permissions is + /// filesystem-specific. + /// + /// Unlike in POSIX, the `executable` flag is not reinterpreted as a "search" + /// flag. `read` on a directory implies readability and searchability, and + /// `execute` is not valid for directories. + /// + /// Note: This is similar to `fchmodat` in POSIX. + change-directory-permissions-at: func( + this: descriptor, + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path to operate on. + path: string, + /// The new permissions for the directory. + modes: modes, + ) -> result<_, error-code> + + /// Request a shared advisory lock for an open file. + /// + /// This requests a *shared* lock; more than one shared lock can be held for + /// a file at the same time. + /// + /// If the open file has an exclusive lock, this function downgrades the lock + /// to a shared lock. If it has a shared lock, this function has no effect. + /// + /// This requests an *advisory* lock, meaning that the file could be accessed + /// by other programs that don't hold the lock. + /// + /// It is unspecified how shared locks interact with locks acquired by + /// non-WASI programs. + /// + /// This function blocks until the lock can be acquired. + /// + /// Not all filesystems support locking; on filesystems which don't support + /// locking, this function returns `error-code::unsupported`. + /// + /// Note: This is similar to `flock(fd, LOCK_SH)` in Unix. + lock-shared: func(this: descriptor) -> result<_, error-code> + + /// Request an exclusive advisory lock for an open file. + /// + /// This requests an *exclusive* lock; no other locks may be held for the + /// file while an exclusive lock is held. + /// + /// If the open file has a shared lock and there are no exclusive locks held + /// for the file, this function upgrades the lock to an exclusive lock. If the + /// open file already has an exclusive lock, this function has no effect. + /// + /// This requests an *advisory* lock, meaning that the file could be accessed + /// by other programs that don't hold the lock. + /// + /// It is unspecified whether this function succeeds if the file descriptor + /// is not opened for writing. It is unspecified how exclusive locks interact + /// with locks acquired by non-WASI programs. + /// + /// This function blocks until the lock can be acquired. + /// + /// Not all filesystems support locking; on filesystems which don't support + /// locking, this function returns `error-code::unsupported`. + /// + /// Note: This is similar to `flock(fd, LOCK_EX)` in Unix. + lock-exclusive: func(this: descriptor) -> result<_, error-code> + + /// Request a shared advisory lock for an open file. + /// + /// This requests a *shared* lock; more than one shared lock can be held for + /// a file at the same time. + /// + /// If the open file has an exclusive lock, this function downgrades the lock + /// to a shared lock. If it has a shared lock, this function has no effect. + /// + /// This requests an *advisory* lock, meaning that the file could be accessed + /// by other programs that don't hold the lock. + /// + /// It is unspecified how shared locks interact with locks acquired by + /// non-WASI programs. + /// + /// This function returns `error-code::would-block` if the lock cannot be + /// acquired. + /// + /// Not all filesystems support locking; on filesystems which don't support + /// locking, this function returns `error-code::unsupported`. + /// + /// Note: This is similar to `flock(fd, LOCK_SH | LOCK_NB)` in Unix. + try-lock-shared: func(this: descriptor) -> result<_, error-code> + + /// Request an exclusive advisory lock for an open file. + /// + /// This requests an *exclusive* lock; no other locks may be held for the + /// file while an exclusive lock is held. + /// + /// If the open file has a shared lock and there are no exclusive locks held + /// for the file, this function upgrades the lock to an exclusive lock. If the + /// open file already has an exclusive lock, this function has no effect. + /// + /// This requests an *advisory* lock, meaning that the file could be accessed + /// by other programs that don't hold the lock. + /// + /// It is unspecified whether this function succeeds if the file descriptor + /// is not opened for writing. It is unspecified how exclusive locks interact + /// with locks acquired by non-WASI programs. + /// + /// This function returns `error-code::would-block` if the lock cannot be + /// acquired. + /// + /// Not all filesystems support locking; on filesystems which don't support + /// locking, this function returns `error-code::unsupported`. + /// + /// Note: This is similar to `flock(fd, LOCK_EX | LOCK_NB)` in Unix. + try-lock-exclusive: func(this: descriptor) -> result<_, error-code> + + /// Release a shared or exclusive lock on an open file. + /// + /// Note: This is similar to `flock(fd, LOCK_UN)` in Unix. + unlock: func(this: descriptor) -> result<_, error-code> + + /// Dispose of the specified `descriptor`, after which it may no longer + /// be used. + drop-descriptor: func(this: descriptor) + + /// A stream of directory entries. + /// + /// This [represents a stream of `dir-entry`](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Streams). + type directory-entry-stream = u32 + + /// Read a single directory entry from a `directory-entry-stream`. + read-directory-entry: func( + this: directory-entry-stream + ) -> result, error-code> + + /// Dispose of the specified `directory-entry-stream`, after which it may no longer + /// be used. + drop-directory-entry-stream: func(this: directory-entry-stream) +} diff --git a/wit/deps/http/incoming-handler.wit b/wit/deps/http/incoming-handler.wit new file mode 100644 index 0000000..d0e2704 --- /dev/null +++ b/wit/deps/http/incoming-handler.wit @@ -0,0 +1,24 @@ +// The `wasi:http/incoming-handler` interface is meant to be exported by +// components and called by the host in response to a new incoming HTTP +// response. +// +// NOTE: in Preview3, this interface will be merged with +// `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface +// that takes a `request` parameter and returns a `response` result. +// +interface incoming-handler { + use types.{incoming-request, response-outparam} + + // The `handle` function takes an outparam instead of returning its response + // so that the component may stream its response while streaming any other + // request or response bodies. The callee MUST write a response to the + // `response-out` and then finish the response before returning. The `handle` + // function is allowed to continue execution after finishing the response's + // output stream. While this post-response execution is taken off the + // critical path, since there is no return value, there is no way to report + // its success or failure. + handle: func( + request: incoming-request, + response-out: response-outparam + ) +} diff --git a/wit/deps/http/outgoing-handler.wit b/wit/deps/http/outgoing-handler.wit new file mode 100644 index 0000000..06c8e46 --- /dev/null +++ b/wit/deps/http/outgoing-handler.wit @@ -0,0 +1,18 @@ +// The `wasi:http/outgoing-handler` interface is meant to be imported by +// components and implemented by the host. +// +// NOTE: in Preview3, this interface will be merged with +// `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface +// that takes a `request` parameter and returns a `response` result. +// +interface outgoing-handler { + use types.{outgoing-request, request-options, future-incoming-response} + + // The parameter and result types of the `handle` function allow the caller + // to concurrently stream the bodies of the outgoing request and the incoming + // response. + handle: func( + request: outgoing-request, + options: option + ) -> future-incoming-response +} diff --git a/wit/deps/http/types.wit b/wit/deps/http/types.wit new file mode 100644 index 0000000..ee4227f --- /dev/null +++ b/wit/deps/http/types.wit @@ -0,0 +1,159 @@ +package wasi:http + +// The `wasi:http/types` interface is meant to be imported by components to +// define the HTTP resource types and operations used by the component's +// imported and exported interfaces. +interface types { + use wasi:io/streams.{input-stream, output-stream} + use wasi:poll/poll.{pollable} + + // This type corresponds to HTTP standard Methods. + variant method { + get, + head, + post, + put, + delete, + connect, + options, + trace, + patch, + other(string) + } + + // This type corresponds to HTTP standard Related Schemes. + variant scheme { + HTTP, + HTTPS, + other(string) + } + + // TODO: perhaps better align with HTTP semantics? + // This type enumerates the different kinds of errors that may occur when + // initially returning a response. + variant error { + invalid-url(string), + timeout-error(string), + protocol-error(string), + unexpected-error(string) + } + + // This following block defines the `fields` resource which corresponds to + // HTTP standard Fields. Soon, when resource types are added, the `type + // fields = u32` type alias can be replaced by a proper `resource fields` + // definition containing all the functions using the method syntactic sugar. + type fields = u32 + drop-fields: func(fields: fields) + new-fields: func(entries: list>) -> fields + fields-get: func(fields: fields, name: string) -> list + fields-set: func(fields: fields, name: string, value: list) + fields-delete: func(fields: fields, name: string) + fields-append: func(fields: fields, name: string, value: string) + fields-entries: func(fields: fields) -> list> + fields-clone: func(fields: fields) -> fields + + type headers = fields + type trailers = fields + + // The following block defines stream types which corresponds to the HTTP + // standard Contents and Trailers. With Preview3, all of these fields can be + // replaced by a stream>. In the interim, we need to + // build on separate resource types defined by `wasi:io/streams`. The + // `finish-` functions emulate the stream's result value and MUST be called + // exactly once after the final read/write from/to the stream before dropping + // the stream. + type incoming-stream = input-stream + type outgoing-stream = output-stream + finish-incoming-stream: func(s: incoming-stream) -> option + finish-outgoing-stream: func(s: outgoing-stream, trailers: option) + + // The following block defines the `incoming-request` and `outgoing-request` + // resource types that correspond to HTTP standard Requests. Soon, when + // resource types are added, the `u32` type aliases can be replaced by + // proper `resource` type definitions containing all the functions as + // methods. Later, Preview2 will allow both types to be merged together into + // a single `request` type (that uses the single `stream` type mentioned + // above). The `consume` and `write` methods may only be called once (and + // return failure thereafter). + type incoming-request = u32 + type outgoing-request = u32 + drop-incoming-request: func(request: incoming-request) + drop-outgoing-request: func(request: outgoing-request) + incoming-request-method: func(request: incoming-request) -> method + incoming-request-path: func(request: incoming-request) -> string + incoming-request-query: func(request: incoming-request) -> string + incoming-request-scheme: func(request: incoming-request) -> option + incoming-request-authority: func(request: incoming-request) -> string + incoming-request-headers: func(request: incoming-request) -> headers + incoming-request-consume: func(request: incoming-request) -> result + new-outgoing-request: func( + method: method, + path: string, + query: string, + scheme: option, + authority: string, + headers: headers + ) -> outgoing-request + outgoing-request-write: func(request: outgoing-request) -> result + + // Additional optional parameters that can be set when making a request. + record request-options { + // The following timeouts are specific to the HTTP protocol and work + // independently of the overall timeouts passed to `io.poll.poll-oneoff`. + + // The timeout for the initial connect. + connect-timeout-ms: option, + + // The timeout for receiving the first byte of the response body. + first-byte-timeout-ms: option, + + // The timeout for receiving the next chunk of bytes in the response body + // stream. + between-bytes-timeout-ms: option + } + + // The following block defines a special resource type used by the + // `wasi:http/incoming-handler` interface. When resource types are added, this + // block can be replaced by a proper `resource response-outparam { ... }` + // definition. Later, with Preview3, the need for an outparam goes away entirely + // (the `wasi:http/handler` interface used for both incoming and outgoing can + // simply return a `stream`). + type response-outparam = u32 + drop-response-outparam: func(response: response-outparam) + set-response-outparam: func(response: result) -> result + + // This type corresponds to the HTTP standard Status Code. + type status-code = u16 + + // The following block defines the `incoming-response` and `outgoing-response` + // resource types that correspond to HTTP standard Responses. Soon, when + // resource types are added, the `u32` type aliases can be replaced by proper + // `resource` type definitions containing all the functions as methods. Later, + // Preview2 will allow both types to be merged together into a single `response` + // type (that uses the single `stream` type mentioned above). The `consume` and + // `write` methods may only be called once (and return failure thereafter). + type incoming-response = u32 + type outgoing-response = u32 + drop-incoming-response: func(response: incoming-response) + drop-outgoing-response: func(response: outgoing-response) + incoming-response-status: func(response: incoming-response) -> status-code + incoming-response-headers: func(response: incoming-response) -> headers + incoming-response-consume: func(response: incoming-response) -> result + new-outgoing-response: func( + status-code: status-code, + headers: headers + ) -> outgoing-response + outgoing-response-write: func(response: outgoing-response) -> result + + // The following block defines a special resource type used by the + // `wasi:http/outgoing-handler` interface to emulate + // `future>` in advance of Preview3. Given a + // `future-incoming-response`, the client can call the non-blocking `get` + // method to get the result if it is available. If the result is not available, + // the client can call `listen` to get a `pollable` that can be passed to + // `io.poll.poll-oneoff`. + type future-incoming-response = u32 + drop-future-incoming-response: func(f: future-incoming-response) + future-incoming-response-get: func(f: future-incoming-response) -> option> + listen-to-future-incoming-response: func(f: future-incoming-response) -> pollable +} diff --git a/wit/deps/io/streams.wit b/wit/deps/io/streams.wit new file mode 100644 index 0000000..008e36c --- /dev/null +++ b/wit/deps/io/streams.wit @@ -0,0 +1,215 @@ +package wasi:io + +/// WASI I/O is an I/O abstraction API which is currently focused on providing +/// stream types. +/// +/// In the future, the component model is expected to add built-in stream types; +/// when it does, they are expected to subsume this API. +interface streams { + use wasi:poll/poll.{pollable} + + /// An error type returned from a stream operation. Currently this + /// doesn't provide any additional information. + record stream-error {} + + /// An input bytestream. In the future, this will be replaced by handle + /// types. + /// + /// This conceptually represents a `stream`. It's temporary + /// scaffolding until component-model's async features are ready. + /// + /// `input-stream`s are *non-blocking* to the extent practical on underlying + /// platforms. I/O operations always return promptly; if fewer bytes are + /// promptly available than requested, they return the number of bytes promptly + /// available, which could even be zero. To wait for data to be available, + /// use the `subscribe-to-input-stream` function to obtain a `pollable` which + /// can be polled for using `wasi_poll`. + /// + /// And at present, it is a `u32` instead of being an actual handle, until + /// the wit-bindgen implementation of handles and resources is ready. + /// + /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). + type input-stream = u32 + + /// Read bytes from a stream. + /// + /// This function returns a list of bytes containing the data that was + /// read, along with a bool which, when true, indicates that the end of the + /// stream was reached. The returned list will contain up to `len` bytes; it + /// may return fewer than requested, but not more. + /// + /// Once a stream has reached the end, subsequent calls to read or + /// `skip` will always report end-of-stream rather than producing more + /// data. + /// + /// If `len` is 0, it represents a request to read 0 bytes, which should + /// always succeed, assuming the stream hasn't reached its end yet, and + /// return an empty list. + /// + /// The len here is a `u64`, but some callees may not be able to allocate + /// a buffer as large as that would imply. + /// FIXME: describe what happens if allocation fails. + read: func( + this: input-stream, + /// The maximum number of bytes to read + len: u64 + ) -> result, bool>, stream-error> + + /// Read bytes from a stream, with blocking. + /// + /// This is similar to `read`, except that it blocks until at least one + /// byte can be read. + blocking-read: func( + this: input-stream, + /// The maximum number of bytes to read + len: u64 + ) -> result, bool>, stream-error> + + /// Skip bytes from a stream. + /// + /// This is similar to the `read` function, but avoids copying the + /// bytes into the instance. + /// + /// Once a stream has reached the end, subsequent calls to read or + /// `skip` will always report end-of-stream rather than producing more + /// data. + /// + /// This function returns the number of bytes skipped, along with a bool + /// indicating whether the end of the stream was reached. The returned + /// value will be at most `len`; it may be less. + skip: func( + this: input-stream, + /// The maximum number of bytes to skip. + len: u64, + ) -> result, stream-error> + + /// Skip bytes from a stream, with blocking. + /// + /// This is similar to `skip`, except that it blocks until at least one + /// byte can be consumed. + blocking-skip: func( + this: input-stream, + /// The maximum number of bytes to skip. + len: u64, + ) -> result, stream-error> + + /// Create a `pollable` which will resolve once either the specified stream + /// has bytes available to read or the other end of the stream has been + /// closed. + subscribe-to-input-stream: func(this: input-stream) -> pollable + + /// Dispose of the specified `input-stream`, after which it may no longer + /// be used. + drop-input-stream: func(this: input-stream) + + /// An output bytestream. In the future, this will be replaced by handle + /// types. + /// + /// This conceptually represents a `stream`. It's temporary + /// scaffolding until component-model's async features are ready. + /// + /// `output-stream`s are *non-blocking* to the extent practical on + /// underlying platforms. Except where specified otherwise, I/O operations also + /// always return promptly, after the number of bytes that can be written + /// promptly, which could even be zero. To wait for the stream to be ready to + /// accept data, the `subscribe-to-output-stream` function to obtain a + /// `pollable` which can be polled for using `wasi_poll`. + /// + /// And at present, it is a `u32` instead of being an actual handle, until + /// the wit-bindgen implementation of handles and resources is ready. + /// + /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). + type output-stream = u32 + + /// Write bytes to a stream. + /// + /// This function returns a `u64` indicating the number of bytes from + /// `buf` that were written; it may be less than the full list. + write: func( + this: output-stream, + /// Data to write + buf: list + ) -> result + + /// Write bytes to a stream, with blocking. + /// + /// This is similar to `write`, except that it blocks until at least one + /// byte can be written. + blocking-write: func( + this: output-stream, + /// Data to write + buf: list + ) -> result + + /// Write multiple zero bytes to a stream. + /// + /// This function returns a `u64` indicating the number of zero bytes + /// that were written; it may be less than `len`. + write-zeroes: func( + this: output-stream, + /// The number of zero bytes to write + len: u64 + ) -> result + + /// Write multiple zero bytes to a stream, with blocking. + /// + /// This is similar to `write-zeroes`, except that it blocks until at least + /// one byte can be written. + blocking-write-zeroes: func( + this: output-stream, + /// The number of zero bytes to write + len: u64 + ) -> result + + /// Read from one stream and write to another. + /// + /// This function returns the number of bytes transferred; it may be less + /// than `len`. + /// + /// Unlike other I/O functions, this function blocks until all the data + /// read from the input stream has been written to the output stream. + splice: func( + this: output-stream, + /// The stream to read from + src: input-stream, + /// The number of bytes to splice + len: u64, + ) -> result, stream-error> + + /// Read from one stream and write to another, with blocking. + /// + /// This is similar to `splice`, except that it blocks until at least + /// one byte can be read. + blocking-splice: func( + this: output-stream, + /// The stream to read from + src: input-stream, + /// The number of bytes to splice + len: u64, + ) -> result, stream-error> + + /// Forward the entire contents of an input stream to an output stream. + /// + /// This function repeatedly reads from the input stream and writes + /// the data to the output stream, until the end of the input stream + /// is reached, or an error is encountered. + /// + /// Unlike other I/O functions, this function blocks until the end + /// of the input stream is seen and all the data has been written to + /// the output stream. + /// + /// This function returns the number of bytes transferred. + forward: func( + this: output-stream, + /// The stream to read from + src: input-stream + ) -> result + + /// Create a `pollable` which will resolve once either the specified stream + /// is ready to accept bytes or the other end of the stream has been closed. + subscribe-to-output-stream: func(this: output-stream) -> pollable + + /// Dispose of the specified `output-stream`, after which it may no longer + /// be used. + drop-output-stream: func(this: output-stream) +} diff --git a/wit/deps/logging/handler.wit b/wit/deps/logging/handler.wit new file mode 100644 index 0000000..e6b077b --- /dev/null +++ b/wit/deps/logging/handler.wit @@ -0,0 +1,34 @@ +package wasi:logging + +/// WASI Logging is a logging API intended to let users emit log messages with +/// simple priority levels and context values. +interface handler { + /// A log level, describing a kind of message. + enum level { + /// Describes messages about the values of variables and the flow of + /// control within a program. + trace, + + /// Describes messages likely to be of interest to someone debugging a + /// program. + debug, + + /// Describes messages likely to be of interest to someone monitoring a + /// program. + info, + + /// Describes messages indicating hazardous situations. + warn, + + /// Describes messages indicating serious errors. + error, + } + + /// Emit a log message. + /// + /// A log message has a `level` describing what kind of message is being + /// sent, a context, which is an uninterpreted string meant to help + /// consumers group similar messages, and a string containing the message + /// text. + log: func(level: level, context: string, message: string) +} diff --git a/wit/deps/poll/poll.wit b/wit/deps/poll/poll.wit new file mode 100644 index 0000000..cf5d877 --- /dev/null +++ b/wit/deps/poll/poll.wit @@ -0,0 +1,41 @@ +package wasi:poll + +/// A poll API intended to let users wait for I/O events on multiple handles +/// at once. +interface poll { + /// A "pollable" handle. + /// + /// This is conceptually represents a `stream<_, _>`, or in other words, + /// a stream that one can wait on, repeatedly, but which does not itself + /// produce any data. It's temporary scaffolding until component-model's + /// async features are ready. + /// + /// And at present, it is a `u32` instead of being an actual handle, until + /// the wit-bindgen implementation of handles and resources is ready. + /// + /// `pollable` lifetimes are not automatically managed. Users must ensure + /// that they do not outlive the resource they reference. + /// + /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). + type pollable = u32 + + /// Dispose of the specified `pollable`, after which it may no longer + /// be used. + drop-pollable: func(this: pollable) + + /// Poll for completion on a set of pollables. + /// + /// The "oneoff" in the name refers to the fact that this function must do a + /// linear scan through the entire list of subscriptions, which may be + /// inefficient if the number is large and the same subscriptions are used + /// many times. In the future, this is expected to be obsoleted by the + /// component model async proposal, which will include a scalable waiting + /// facility. + /// + /// Note that the return type would ideally be `list`, but that would + /// be more difficult to polyfill given the current state of `wit-bindgen`. + /// See + /// for details. For now, we use zero to mean "not ready" and non-zero to + /// mean "ready". + poll-oneoff: func(in: list) -> list +} diff --git a/wit/deps/random/insecure-seed.wit b/wit/deps/random/insecure-seed.wit new file mode 100644 index 0000000..ff2ff65 --- /dev/null +++ b/wit/deps/random/insecure-seed.wit @@ -0,0 +1,24 @@ +/// The insecure-seed interface for seeding hash-map DoS resistance. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +interface insecure-seed { + /// Return a 128-bit value that may contain a pseudo-random value. + /// + /// The returned value is not required to be computed from a CSPRNG, and may + /// even be entirely deterministic. Host implementations are encouraged to + /// provide pseudo-random values to any program exposed to + /// attacker-controlled content, to enable DoS protection built into many + /// languages' hash-map implementations. + /// + /// This function is intended to only be called once, by a source language + /// to initialize Denial Of Service (DoS) protection in its hash-map + /// implementation. + /// + /// # Expected future evolution + /// + /// This will likely be changed to a value import, to prevent it from being + /// called multiple times and potentially used for purposes other than DoS + /// protection. + insecure-seed: func() -> tuple +} diff --git a/wit/deps/random/insecure.wit b/wit/deps/random/insecure.wit new file mode 100644 index 0000000..ff08268 --- /dev/null +++ b/wit/deps/random/insecure.wit @@ -0,0 +1,21 @@ +/// The insecure interface for insecure pseudo-random numbers. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +interface insecure { + /// Return `len` insecure pseudo-random bytes. + /// + /// This function is not cryptographically secure. Do not use it for + /// anything related to security. + /// + /// There are no requirements on the values of the returned bytes, however + /// implementations are encouraged to return evenly distributed values with + /// a long period. + get-insecure-random-bytes: func(len: u64) -> list + + /// Return an insecure pseudo-random `u64` value. + /// + /// This function returns the same type of pseudo-random data as + /// `get-insecure-random-bytes`, represented as a `u64`. + get-insecure-random-u64: func() -> u64 +} diff --git a/wit/deps/random/random.wit b/wit/deps/random/random.wit new file mode 100644 index 0000000..f2bd635 --- /dev/null +++ b/wit/deps/random/random.wit @@ -0,0 +1,25 @@ +package wasi:random + +/// WASI Random is a random data API. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +interface random { + /// Return `len` cryptographically-secure pseudo-random bytes. + /// + /// This function must produce data from an adequately seeded + /// cryptographically-secure pseudo-random number generator (CSPRNG), so it + /// must not block, from the perspective of the calling program, and the + /// returned data is always unpredictable. + /// + /// This function must always return fresh pseudo-random data. Deterministic + /// environments must omit this function, rather than implementing it with + /// deterministic data. + get-random-bytes: func(len: u64) -> list + + /// Return a cryptographically-secure pseudo-random `u64` value. + /// + /// This function returns the same type of pseudo-random data as + /// `get-random-bytes`, represented as a `u64`. + get-random-u64: func() -> u64 +} diff --git a/wit/deps/sockets/instance-network.wit b/wit/deps/sockets/instance-network.wit new file mode 100644 index 0000000..d911a29 --- /dev/null +++ b/wit/deps/sockets/instance-network.wit @@ -0,0 +1,9 @@ + +/// This interface provides a value-export of the default network handle.. +interface instance-network { + use network.{network} + + /// Get a handle to the default network. + instance-network: func() -> network + +} diff --git a/wit/deps/sockets/ip-name-lookup.wit b/wit/deps/sockets/ip-name-lookup.wit new file mode 100644 index 0000000..6c64b46 --- /dev/null +++ b/wit/deps/sockets/ip-name-lookup.wit @@ -0,0 +1,69 @@ + +interface ip-name-lookup { + use wasi:poll/poll.{pollable} + use network.{network, error-code, ip-address, ip-address-family} + + + /// Resolve an internet host name to a list of IP addresses. + /// + /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. + /// + /// # Parameters + /// - `name`: The name to look up. IP addresses are not allowed. Unicode domain names are automatically converted + /// to ASCII using IDNA encoding. + /// - `address-family`: If provided, limit the results to addresses of this specific address family. + /// - `include-unavailable`: When set to true, this function will also return addresses of which the runtime + /// thinks (or knows) can't be connected to at the moment. For example, this will return IPv6 addresses on + /// systems without an active IPv6 interface. Notes: + /// - Even when no public IPv6 interfaces are present or active, names like "localhost" can still resolve to an IPv6 address. + /// - Whatever is "available" or "unavailable" is volatile and can change everytime a network cable is unplugged. + /// + /// This function never blocks. It either immediately fails or immediately returns successfully with a `resolve-address-stream` + /// that can be used to (asynchronously) fetch the results. + /// + /// At the moment, the stream never completes successfully with 0 items. Ie. the first call + /// to `resolve-next-address` never returns `ok(none)`. This may change in the future. + /// + /// # Typical errors + /// - `invalid-name`: `name` is a syntactically invalid domain name. + /// - `invalid-name`: `name` is an IP address. + /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAI_FAMILY) + /// + /// # References: + /// - + /// - + /// - + /// - + resolve-addresses: func(network: network, name: string, address-family: option, include-unavailable: bool) -> result + + + + type resolve-address-stream = u32 + + /// Returns the next address from the resolver. + /// + /// This function should be called multiple times. On each call, it will + /// return the next address in connection order preference. If all + /// addresses have been exhausted, this function returns `none`. + /// After which, you should release the stream with `drop-resolve-address-stream`. + /// + /// This function never returns IPv4-mapped IPv6 addresses. + /// + /// # Typical errors + /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) + /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) + /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) + /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) + resolve-next-address: func(this: resolve-address-stream) -> result, error-code> + + /// Dispose of the specified `resolve-address-stream`, after which it may no longer be used. + /// + /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. + drop-resolve-address-stream: func(this: resolve-address-stream) + + /// Create a `pollable` which will resolve once the stream is ready for I/O. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func(this: resolve-address-stream) -> pollable +} diff --git a/wit/deps/sockets/network.wit b/wit/deps/sockets/network.wit new file mode 100644 index 0000000..c370214 --- /dev/null +++ b/wit/deps/sockets/network.wit @@ -0,0 +1,187 @@ +package wasi:sockets + +interface network { + /// An opaque resource that represents access to (a subset of) the network. + /// This enables context-based security for networking. + /// There is no need for this to map 1:1 to a physical network interface. + /// + /// FYI, In the future this will be replaced by handle types. + type network = u32 + + /// Dispose of the specified `network`, after which it may no longer be used. + /// + /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. + drop-network: func(this: network) + + + /// Error codes. + /// + /// In theory, every API can return any error code. + /// In practice, API's typically only return the errors documented per API + /// combined with a couple of errors that are always possible: + /// - `unknown` + /// - `access-denied` + /// - `not-supported` + /// - `out-of-memory` + /// + /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. + enum error-code { + // ### GENERAL ERRORS ### + + /// Unknown error + unknown, + + /// Access denied. + /// + /// POSIX equivalent: EACCES, EPERM + access-denied, + + /// The operation is not supported. + /// + /// POSIX equivalent: EOPNOTSUPP + not-supported, + + /// Not enough memory to complete the operation. + /// + /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY + out-of-memory, + + /// The operation timed out before it could finish completely. + timeout, + + /// This operation is incompatible with another asynchronous operation that is already in progress. + concurrency-conflict, + + /// Trying to finish an asynchronous operation that: + /// - has not been started yet, or: + /// - was already finished by a previous `finish-*` call. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + not-in-progress, + + /// The operation has been aborted because it could not be completed immediately. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + would-block, + + + // ### IP ERRORS ### + + /// The specified address-family is not supported. + address-family-not-supported, + + /// An IPv4 address was passed to an IPv6 resource, or vice versa. + address-family-mismatch, + + /// The socket address is not a valid remote address. E.g. the IP address is set to INADDR_ANY, or the port is set to 0. + invalid-remote-address, + + /// The operation is only supported on IPv4 resources. + ipv4-only-operation, + + /// The operation is only supported on IPv6 resources. + ipv6-only-operation, + + + + // ### TCP & UDP SOCKET ERRORS ### + + /// A new socket resource could not be created because of a system limit. + new-socket-limit, + + /// The socket is already attached to another network. + already-attached, + + /// The socket is already bound. + already-bound, + + /// The socket is already in the Connection state. + already-connected, + + /// The socket is not bound to any local address. + not-bound, + + /// The socket is not in the Connection state. + not-connected, + + /// A bind operation failed because the provided address is not an address that the `network` can bind to. + address-not-bindable, + + /// A bind operation failed because the provided address is already in use. + address-in-use, + + /// A bind operation failed because there are no ephemeral ports available. + ephemeral-ports-exhausted, + + /// The remote address is not reachable + remote-unreachable, + + + // ### TCP SOCKET ERRORS ### + + /// The socket is already in the Listener state. + already-listening, + + /// The socket is already in the Listener state. + not-listening, + + /// The connection was forcefully rejected + connection-refused, + + /// The connection was reset. + connection-reset, + + + // ### UDP SOCKET ERRORS ### + datagram-too-large, + + + // ### NAME LOOKUP ERRORS ### + + /// The provided name is a syntactically invalid domain name. + invalid-name, + + /// Name does not exist or has no suitable associated IP addresses. + name-unresolvable, + + /// A temporary failure in name resolution occurred. + temporary-resolver-failure, + + /// A permanent failure in name resolution occurred. + permanent-resolver-failure, + } + + enum ip-address-family { + /// Similar to `AF_INET` in POSIX. + ipv4, + + /// Similar to `AF_INET6` in POSIX. + ipv6, + } + + type ipv4-address = tuple + type ipv6-address = tuple + + variant ip-address { + ipv4(ipv4-address), + ipv6(ipv6-address), + } + + record ipv4-socket-address { + port: u16, // sin_port + address: ipv4-address, // sin_addr + } + + record ipv6-socket-address { + port: u16, // sin6_port + flow-info: u32, // sin6_flowinfo + address: ipv6-address, // sin6_addr + scope-id: u32, // sin6_scope_id + } + + variant ip-socket-address { + ipv4(ipv4-socket-address), + ipv6(ipv6-socket-address), + } + +} diff --git a/wit/deps/sockets/tcp-create-socket.wit b/wit/deps/sockets/tcp-create-socket.wit new file mode 100644 index 0000000..f467d28 --- /dev/null +++ b/wit/deps/sockets/tcp-create-socket.wit @@ -0,0 +1,27 @@ + +interface tcp-create-socket { + use network.{network, error-code, ip-address-family} + use tcp.{tcp-socket} + + /// Create a new TCP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. + /// + /// This function does not require a network capability handle. This is considered to be safe because + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect` + /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The host does not support TCP sockets. (EOPNOTSUPP) + /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References + /// - + /// - + /// - + /// - + create-tcp-socket: func(address-family: ip-address-family) -> result +} diff --git a/wit/deps/sockets/tcp.wit b/wit/deps/sockets/tcp.wit new file mode 100644 index 0000000..7ed46a6 --- /dev/null +++ b/wit/deps/sockets/tcp.wit @@ -0,0 +1,255 @@ + +interface tcp { + use wasi:io/streams.{input-stream, output-stream} + use wasi:poll/poll.{pollable} + use network.{network, error-code, ip-socket-address, ip-address-family} + + /// A TCP socket handle. + type tcp-socket = u32 + + + enum shutdown-type { + /// Similar to `SHUT_RD` in POSIX. + receive, + + /// Similar to `SHUT_WR` in POSIX. + send, + + /// Similar to `SHUT_RDWR` in POSIX. + both, + } + + + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// + /// When a socket is not explicitly bound, the first invocation to a listen or connect operation will + /// implicitly bind the socket. + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) + /// - `already-bound`: The socket is already bound. (EINVAL) + /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-bind: func(this: tcp-socket, network: network, local-address: ip-socket-address) -> result<_, error-code> + finish-bind: func(this: tcp-socket) -> result<_, error-code> + + /// Connect to a remote endpoint. + /// + /// On success: + /// - the socket is transitioned into the Connection state + /// - a pair of streams is returned that can be used to read & write to the connection + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) + /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) + /// - `already-attached`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `already-connected`: The socket is already in the Connection state. (EISCONN) + /// - `already-listening`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) + /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `timeout`: Connection timed out. (ETIMEDOUT) + /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) + /// - `connection-reset`: The connection was reset. (ECONNRESET) + /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `not-in-progress`: A `connect` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-connect: func(this: tcp-socket, network: network, remote-address: ip-socket-address) -> result<_, error-code> + finish-connect: func(this: tcp-socket) -> result, error-code> + + /// Start listening for new connections. + /// + /// Transitions the socket into the Listener state. + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `already-attached`: The socket is already attached to a different network. The `network` passed to `listen` must be identical to the one passed to `bind`. + /// - `already-connected`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) + /// - `already-listening`: The socket is already in the Listener state. + /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EINVAL on BSD) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// - `not-in-progress`: A `listen` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-listen: func(this: tcp-socket, network: network) -> result<_, error-code> + finish-listen: func(this: tcp-socket) -> result<_, error-code> + + /// Accept a new client socket. + /// + /// The returned socket is bound and in the Connection state. + /// + /// On success, this function returns the newly accepted client socket along with + /// a pair of streams that can be used to read & write to the connection. + /// + /// # Typical errors + /// - `not-listening`: Socket is not in the Listener state. (EINVAL) + /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) + /// + /// Host implementations must skip over transient errors returned by the native accept syscall. + /// + /// # References + /// - + /// - + /// - + /// - + accept: func(this: tcp-socket) -> result, error-code> + + /// Get the bound local address. + /// + /// # Typical errors + /// - `not-bound`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + local-address: func(this: tcp-socket) -> result + + /// Get the bound remote address. + /// + /// # Typical errors + /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + remote-address: func(this: tcp-socket) -> result + + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + address-family: func(this: tcp-socket) -> ip-address-family + + /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. + /// + /// Equivalent to the IPV6_V6ONLY socket option. + /// + /// # Typical errors + /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. + /// - `already-bound`: (set) The socket is already bound. + /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + ipv6-only: func(this: tcp-socket) -> result + set-ipv6-only: func(this: tcp-socket, value: bool) -> result<_, error-code> + + /// Hints the desired listen queue size. Implementations are free to ignore this. + /// + /// # Typical errors + /// - `already-connected`: (set) The socket is already in the Connection state. + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + set-listen-backlog-size: func(this: tcp-socket, value: u64) -> result<_, error-code> + + /// Equivalent to the SO_KEEPALIVE socket option. + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + keep-alive: func(this: tcp-socket) -> result + set-keep-alive: func(this: tcp-socket, value: bool) -> result<_, error-code> + + /// Equivalent to the TCP_NODELAY socket option. + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + no-delay: func(this: tcp-socket) -> result + set-no-delay: func(this: tcp-socket, value: bool) -> result<_, error-code> + + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// # Typical errors + /// - `already-connected`: (set) The socket is already in the Connection state. + /// - `already-listening`: (set) The socket is already in the Listener state. + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + unicast-hop-limit: func(this: tcp-socket) -> result + set-unicast-hop-limit: func(this: tcp-socket, value: u8) -> result<_, error-code> + + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. + /// In other words, after setting a value, reading the same setting back may return a different value. + /// + /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of + /// actual data to be sent/received by the application, because the kernel might also use the buffer space + /// for internal metadata structures. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `already-connected`: (set) The socket is already in the Connection state. + /// - `already-listening`: (set) The socket is already in the Listener state. + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + receive-buffer-size: func(this: tcp-socket) -> result + set-receive-buffer-size: func(this: tcp-socket, value: u64) -> result<_, error-code> + send-buffer-size: func(this: tcp-socket) -> result + set-send-buffer-size: func(this: tcp-socket, value: u64) -> result<_, error-code> + + /// Create a `pollable` which will resolve once the socket is ready for I/O. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func(this: tcp-socket) -> pollable + + /// Initiate a graceful shutdown. + /// + /// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read + /// operations on the `input-stream` associated with this socket will return an End Of Stream indication. + /// Any data still in the receive queue at time of calling `shutdown` will be discarded. + /// - send: the socket is not expecting to send any more data to the peer. All subsequent write + /// operations on the `output-stream` associated with this socket will return an error. + /// - both: same effect as receive & send combined. + /// + /// The shutdown function does not close (drop) the socket. + /// + /// # Typical errors + /// - `not-connected`: The socket is not in the Connection state. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + shutdown: func(this: tcp-socket, shutdown-type: shutdown-type) -> result<_, error-code> + + /// Dispose of the specified `tcp-socket`, after which it may no longer be used. + /// + /// Similar to the POSIX `close` function. + /// + /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. + drop-tcp-socket: func(this: tcp-socket) +} diff --git a/wit/deps/sockets/udp-create-socket.wit b/wit/deps/sockets/udp-create-socket.wit new file mode 100644 index 0000000..1cfbd7f --- /dev/null +++ b/wit/deps/sockets/udp-create-socket.wit @@ -0,0 +1,27 @@ + +interface udp-create-socket { + use network.{network, error-code, ip-address-family} + use udp.{udp-socket} + + /// Create a new UDP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. + /// + /// This function does not require a network capability handle. This is considered to be safe because + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` is called, + /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The host does not support UDP sockets. (EOPNOTSUPP) + /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References: + /// - + /// - + /// - + /// - + create-udp-socket: func(address-family: ip-address-family) -> result +} diff --git a/wit/deps/sockets/udp.wit b/wit/deps/sockets/udp.wit new file mode 100644 index 0000000..9dd4573 --- /dev/null +++ b/wit/deps/sockets/udp.wit @@ -0,0 +1,211 @@ + +interface udp { + use wasi:poll/poll.{pollable} + use network.{network, error-code, ip-socket-address, ip-address-family} + + + /// A UDP socket handle. + type udp-socket = u32 + + + record datagram { + data: list, // Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes. + remote-address: ip-socket-address, + + /// Possible future additions: + /// local-address: ip-socket-address, // IP_PKTINFO / IP_RECVDSTADDR / IPV6_PKTINFO + /// local-interface: u32, // IP_PKTINFO / IP_RECVIF + /// ttl: u8, // IP_RECVTTL + /// dscp: u6, // IP_RECVTOS + /// ecn: u2, // IP_RECVTOS + } + + + + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// + /// When a socket is not explicitly bound, the first invocation to connect will implicitly bind the socket. + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) + /// - `already-bound`: The socket is already bound. (EINVAL) + /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-bind: func(this: udp-socket, network: network, local-address: ip-socket-address) -> result<_, error-code> + finish-bind: func(this: udp-socket) -> result<_, error-code> + + /// Set the destination address. + /// + /// The local-address is updated based on the best network path to `remote-address`. + /// + /// When a destination address is set: + /// - all receive operations will only return datagrams sent from the provided `remote-address`. + /// - the `send` function can only be used to send to this destination. + /// + /// Note that this function does not generate any network traffic and the peer is not aware of this "connection". + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `already-attached`: The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `not-in-progress`: A `connect` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-connect: func(this: udp-socket, network: network, remote-address: ip-socket-address) -> result<_, error-code> + finish-connect: func(this: udp-socket) -> result<_, error-code> + + /// Receive a message. + /// + /// Returns: + /// - The sender address of the datagram + /// - The number of bytes read. + /// + /// # Typical errors + /// - `not-bound`: The socket is not bound to any local address. (EINVAL) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + /// - `would-block`: There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + receive: func(this: udp-socket) -> result + + /// Send a message to a specific destination address. + /// + /// The remote address option is required. To send a message to the "connected" peer, + /// call `remote-address` to get their address. + /// + /// # Typical errors + /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `already-connected`: The socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN) + /// - `not-bound`: The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind. + /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + /// - `would-block`: The send buffer is currently full. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + send: func(this: udp-socket, datagram: datagram) -> result<_, error-code> + + /// Get the current bound address. + /// + /// # Typical errors + /// - `not-bound`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + local-address: func(this: udp-socket) -> result + + /// Get the address set with `connect`. + /// + /// # Typical errors + /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + remote-address: func(this: udp-socket) -> result + + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + address-family: func(this: udp-socket) -> ip-address-family + + /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. + /// + /// Equivalent to the IPV6_V6ONLY socket option. + /// + /// # Typical errors + /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. + /// - `already-bound`: (set) The socket is already bound. + /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) + /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + ipv6-only: func(this: udp-socket) -> result + set-ipv6-only: func(this: udp-socket, value: bool) -> result<_, error-code> + + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + unicast-hop-limit: func(this: udp-socket) -> result + set-unicast-hop-limit: func(this: udp-socket, value: u8) -> result<_, error-code> + + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. + /// In other words, after setting a value, reading the same setting back may return a different value. + /// + /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of + /// actual data to be sent/received by the application, because the kernel might also use the buffer space + /// for internal metadata structures. + /// + /// Fails when this socket is in the Listening state. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + receive-buffer-size: func(this: udp-socket) -> result + set-receive-buffer-size: func(this: udp-socket, value: u64) -> result<_, error-code> + send-buffer-size: func(this: udp-socket) -> result + set-send-buffer-size: func(this: udp-socket, value: u64) -> result<_, error-code> + + /// Create a `pollable` which will resolve once the socket is ready for I/O. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func(this: udp-socket) -> pollable + + /// Dispose of the specified `udp-socket`, after which it may no longer be used. + /// + /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. + drop-udp-socket: func(this: udp-socket) +} diff --git a/wit/deps/wasi-cli-base/environment.wit b/wit/deps/wasi-cli-base/environment.wit new file mode 100644 index 0000000..4c97c85 --- /dev/null +++ b/wit/deps/wasi-cli-base/environment.wit @@ -0,0 +1,16 @@ +package wasi:cli-base + +interface environment { + /// Get the POSIX-style environment variables. + /// + /// Each environment variable is provided as a pair of string variable names + /// and string value. + /// + /// Morally, these are a value import, but until value imports are available + /// in the component model, this import function should return the same + /// values each time it is called. + get-environment: func() -> list> + + /// Get the POSIX-style arguments to the program. + get-arguments: func() -> list +} diff --git a/wit/deps/wasi-cli-base/exit.wit b/wit/deps/wasi-cli-base/exit.wit new file mode 100644 index 0000000..66835aa --- /dev/null +++ b/wit/deps/wasi-cli-base/exit.wit @@ -0,0 +1,4 @@ +interface exit { + /// Exit the curerent instance and any linked instances. + exit: func(status: result) +} diff --git a/wit/deps/wasi-cli-base/preopens.wit b/wit/deps/wasi-cli-base/preopens.wit new file mode 100644 index 0000000..f268f6b --- /dev/null +++ b/wit/deps/wasi-cli-base/preopens.wit @@ -0,0 +1,7 @@ +interface preopens { + use wasi:filesystem/filesystem.{descriptor} + use wasi:io/streams.{input-stream, output-stream} + + /// Return the set of of preopened directories, and their path. + get-directories: func() -> list> +} diff --git a/wit/deps/wasi-cli-base/stdio.wit b/wit/deps/wasi-cli-base/stdio.wit new file mode 100644 index 0000000..6c9d4a4 --- /dev/null +++ b/wit/deps/wasi-cli-base/stdio.wit @@ -0,0 +1,17 @@ +interface stdin { + use wasi:io/streams.{input-stream} + + get-stdin: func() -> input-stream +} + +interface stdout { + use wasi:io/streams.{output-stream} + + get-stdout: func() -> output-stream +} + +interface stderr { + use wasi:io/streams.{output-stream} + + get-stderr: func() -> output-stream +} diff --git a/wit/virt.wit b/wit/virt.wit new file mode 100644 index 0000000..df99b6c --- /dev/null +++ b/wit/virt.wit @@ -0,0 +1,36 @@ +package local:virt + +world virtual-adapter { + import wasi:cli-base/environment + import console: interface { + log: func(msg: string) -> () + } + export wasi:cli-base/environment +} + +world virt-test { + import wasi:clocks/wall-clock + import wasi:clocks/monotonic-clock + import wasi:clocks/timezone + import wasi:filesystem/filesystem + import wasi:sockets/instance-network + import wasi:sockets/ip-name-lookup + import wasi:sockets/network + import wasi:sockets/tcp-create-socket + import wasi:sockets/tcp + import wasi:sockets/udp-create-socket + import wasi:sockets/udp + import wasi:random/random + import wasi:random/insecure + import wasi:random/insecure-seed + import wasi:poll/poll + import wasi:io/streams + import wasi:cli-base/environment + import wasi:cli-base/preopens + import wasi:cli-base/exit + import wasi:cli-base/stdin + import wasi:cli-base/stdout + import wasi:cli-base/stderr + + export test-get-env: func() -> list> +}