diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 17b1f3a748..65c54b7857 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -394,6 +394,10 @@ jobs: -p wasmtime-c-api --no-default-features -p wasmtime-c-api --no-default-features --features wat -p wasmtime-c-api --no-default-features --features wasi + + - name: wasmtime-wasi-http + checks: | + -p wasmtime-wasi-http --no-default-features runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -510,7 +514,7 @@ jobs: os: ubuntu-latest test: > cargo check -p wasmtime --no-default-features --features runtime,component-model && - cargo check -p wasmtime --no-default-features --features runtime,gc,component-model,async && + cargo check -p wasmtime --no-default-features --features runtime,gc,component-model,async,debug-builtins && cargo check -p cranelift-control --no-default-features && cargo check -p pulley-interpreter --features encode,decode,disas,interp && cargo check -p wasmtime-wasi-io --no-default-features @@ -1022,6 +1026,7 @@ jobs: - crate: "wasmtime-environ --all-features" - crate: "pulley-interpreter --all-features" - script: ./ci/miri-provenance-test.sh + - script: ./ci/miri-wast.sh ./tests/spec_testsuite/table.wast needs: determine if: needs.determine.outputs.test-miri && github.repository == 'bytecodealliance/wasmtime' name: Miri diff --git a/Cargo.lock b/Cargo.lock index e5f800c0c0..f8c2277c50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -388,7 +388,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0acb89ccf798a28683f00089d0630dfaceec087234eae0d308c05ddeaa941b40" dependencies = [ "ambient-authority", - "rand", + "rand 0.8.5", ] [[package]] @@ -625,7 +625,7 @@ dependencies = [ "test-programs-artifacts", "tokio", "wasm-compose", - "wasmparser 0.235.0", + "wasmparser 0.236.0", "wasmtime", "wasmtime-wasi", ] @@ -863,6 +863,7 @@ dependencies = [ "arbitrary", "cranelift", "cranelift-native", + "rand 0.9.2", "target-lexicon", ] @@ -1982,7 +1983,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe" dependencies = [ "bitmaps", - "rand_core", + "rand_core 0.6.3", "rand_xoshiro", "sized-chunks", "typenum", @@ -2170,6 +2171,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json-from-wast" +version = "0.236.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be48c53af281152b0d01019f15b87b9f37f92c3a3c11b003a5ee3ccf2901bb35" +dependencies = [ + "anyhow", + "serde", + "serde_derive", + "wast 236.0.0", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -2406,7 +2419,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31be5d2bfb418a4362eec78ca556ce9a15542a00d389860c3bf3adc132edb1d9" dependencies = [ - "rand", + "rand 0.8.5", ] [[package]] @@ -2683,7 +2696,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" dependencies = [ "base64ct", - "rand_core", + "rand_core 0.6.3", "subtle", ] @@ -2793,8 +2806,8 @@ dependencies = [ "lazy_static", "num-traits", "quick-error 2.0.1", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "rand_xorshift", "regex-syntax 0.6.25", "rusty-fork", @@ -2862,8 +2875,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.3", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -2873,7 +2896,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -2885,13 +2918,22 @@ dependencies = [ "getrandom 0.2.15", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.1", +] + [[package]] name = "rand_xorshift" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core", + "rand_core 0.6.3", ] [[package]] @@ -2900,7 +2942,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" dependencies = [ - "rand_core", + "rand_core 0.6.3", ] [[package]] @@ -3319,7 +3361,7 @@ checksum = "4ee9977fa98489d9006f4ab26fc5cbe2a139985baed09d2ec08dee6e506fc496" dependencies = [ "cfg-if", "libc", - "rand", + "rand 0.8.5", "winapi", ] @@ -3526,7 +3568,7 @@ dependencies = [ "lazy_static", "libc", "ndarray", - "rand", + "rand 0.8.5", "safetensors", "thiserror 1.0.65", "torch-sys", @@ -3606,7 +3648,7 @@ dependencies = [ "wasmtime", "wasmtime-test-util", "wat", - "wit-component", + "wit-component 0.236.0", ] [[package]] @@ -3868,17 +3910,6 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "trait-variant" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "try-lock" version = "0.2.4" @@ -4038,7 +4069,7 @@ name = "verify-component-adapter" version = "36.0.0" dependencies = [ "anyhow", - "wasmparser 0.235.0", + "wasmparser 0.236.0", "wat", ] @@ -4148,7 +4179,7 @@ dependencies = [ "byte-array-literals", "object 0.37.1", "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-encoder", + "wasm-encoder 0.236.0", "wit-bindgen-rust-macro", ] @@ -4209,9 +4240,9 @@ checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" [[package]] name = "wasm-compose" -version = "0.235.0" +version = "0.236.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa7ed9b34419145180cd3069525ee0945f93d97c0fc192d7a85d15c82527373b" +checksum = "98b5290a0aca685aab16c936f682b85e8e4e3a0bfe1843afd43372eb82e34f47" dependencies = [ "anyhow", "heck 0.4.1", @@ -4223,8 +4254,8 @@ dependencies = [ "serde_derive", "serde_yaml", "smallvec", - "wasm-encoder", - "wasmparser 0.235.0", + "wasm-encoder 0.236.0", + "wasmparser 0.236.0", "wat", ] @@ -4238,6 +4269,16 @@ dependencies = [ "wasmparser 0.235.0", ] +[[package]] +name = "wasm-encoder" +version = "0.236.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3108979166ab0d3c7262d2e16a2190ffe784b2a5beb963edef154b5e8e07680b" +dependencies = [ + "leb128fmt", + "wasmparser 0.236.0", +] + [[package]] name = "wasm-metadata" version = "0.235.0" @@ -4246,36 +4287,48 @@ checksum = "b055604ba04189d54b8c0ab2c2fc98848f208e103882d5c0b984f045d5ea4d20" dependencies = [ "anyhow", "indexmap 2.7.0", - "wasm-encoder", + "wasm-encoder 0.235.0", "wasmparser 0.235.0", ] +[[package]] +name = "wasm-metadata" +version = "0.236.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac1c212d9a151aefa45403315d8eb5c81d64dd06103e2d5e0f351034f20169" +dependencies = [ + "anyhow", + "indexmap 2.7.0", + "wasm-encoder 0.236.0", + "wasmparser 0.236.0", +] + [[package]] name = "wasm-mutate" -version = "0.235.0" +version = "0.236.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8917477e93c5c48b6d0dc9f94ea5d6fe9a0ccc3576cbb618f049011cc7e9261" +checksum = "52e91c171e64b6ea23b1e799f8d3cb8cd240b1922fb16e8f6af4b9969697a024" dependencies = [ "egg", "log", - "rand", - "thiserror 1.0.65", - "wasm-encoder", - "wasmparser 0.235.0", + "rand 0.9.2", + "thiserror 2.0.12", + "wasm-encoder 0.236.0", + "wasmparser 0.236.0", ] [[package]] name = "wasm-smith" -version = "0.235.0" +version = "0.236.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3be2ca016817c0732fdb615d41183ecd6fee1a0052c838b3cdffaa66e141883" +checksum = "36cbd9a143df8edb4dc307571399965e951f4998e4fbe7418f308c46fe41dd56" dependencies = [ "anyhow", "arbitrary", "flagset", "serde", "serde_derive", - "wasm-encoder", + "wasm-encoder 0.236.0", "wat", ] @@ -4289,14 +4342,14 @@ dependencies = [ [[package]] name = "wasm-wave" -version = "0.235.0" +version = "0.236.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034bc908dc5c1257ffc63b42c7ac7039092bd369e858c5e78e3242b662a8b7d1" +checksum = "9270d950e101bfa3a3af3ef1de16c5f27ec304b129bc3e61b81f04b84fbd70e6" dependencies = [ "indexmap 2.7.0", "logos", - "thiserror 1.0.65", - "wit-parser", + "thiserror 2.0.12", + "wit-parser 0.236.0", ] [[package]] @@ -4358,6 +4411,18 @@ name = "wasmparser" version = "0.235.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "161296c618fa2d63f6ed5fffd1112937e803cb9ec71b32b01a76321555660917" +dependencies = [ + "bitflags 2.6.0", + "hashbrown 0.15.2", + "indexmap 2.7.0", + "semver", +] + +[[package]] +name = "wasmparser" +version = "0.236.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d1eee846a705f6f3cb9d7b9f79b54583810f1fb57a1e3aea76d1742db2e3d2" dependencies = [ "bitflags 2.6.0", "hashbrown 0.15.2", @@ -4368,13 +4433,13 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.235.0" +version = "0.236.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75aa8e9076de6b9544e6dab4badada518cca0bf4966d35b131bbd057aed8fa0a" +checksum = "a64dc32256b566259d30be300eb142f366343b98f42077216c7dd5e0cf4dc086" dependencies = [ "anyhow", "termcolor", - "wasmparser 0.235.0", + "wasmparser 0.236.0", ] [[package]] @@ -4408,7 +4473,7 @@ dependencies = [ "postcard", "proptest", "pulley-interpreter", - "rand", + "rand 0.9.2", "rayon", "rustix 1.0.3", "semver", @@ -4418,11 +4483,10 @@ dependencies = [ "smallvec", "target-lexicon", "tempfile", - "trait-variant", "wasi-common", - "wasm-encoder", + "wasm-encoder 0.236.0", "wasm-wave", - "wasmparser 0.235.0", + "wasmparser 0.236.0", "wasmtime-environ", "wasmtime-internal-asm-macros", "wasmtime-internal-cache", @@ -4529,11 +4593,10 @@ dependencies = [ "tokio", "toml", "tracing", - "trait-variant", "walkdir", "wasi-common", - "wasm-encoder", - "wasmparser 0.235.0", + "wasm-encoder 0.236.0", + "wasmparser 0.236.0", "wasmtime", "wasmtime-cli-flags", "wasmtime-environ", @@ -4551,10 +4614,10 @@ dependencies = [ "wasmtime-wasi-threads", "wasmtime-wasi-tls", "wasmtime-wast", - "wast 235.0.0", + "wast 236.0.0", "wat", "windows-sys 0.60.2", - "wit-component", + "wit-component 0.236.0", ] [[package]] @@ -4593,8 +4656,8 @@ dependencies = [ "serde_derive", "smallvec", "target-lexicon", - "wasm-encoder", - "wasmparser 0.235.0", + "wasm-encoder 0.236.0", + "wasmparser 0.236.0", "wasmprinter", "wasmtime-internal-component-util", "wat", @@ -4607,7 +4670,7 @@ dependencies = [ "arbitrary", "env_logger 0.11.5", "libfuzzer-sys", - "wasmparser 0.235.0", + "wasmparser 0.236.0", "wasmprinter", "wasmtime-environ", "wasmtime-test-util", @@ -4631,13 +4694,16 @@ dependencies = [ "env_logger 0.11.5", "libfuzzer-sys", "log", + "mutatis", + "postcard", "proc-macro2", "pulley-interpreter-fuzz", "quote", - "rand", + "rand 0.8.5", + "rand 0.9.2", "smallvec", "target-lexicon", - "wasmparser 0.235.0", + "wasmparser 0.236.0", "wasmtime", "wasmtime-fuzzing", "wasmtime-test-util", @@ -4653,19 +4719,20 @@ dependencies = [ "futures", "log", "mutatis", - "rand", + "rand 0.8.5", "rayon", + "serde", "serde_json", "smallvec", "target-lexicon", "tempfile", "v8", - "wasm-encoder", + "wasm-encoder 0.236.0", "wasm-mutate", "wasm-smith", "wasm-spec-interpreter", "wasmi", - "wasmparser 0.235.0", + "wasmparser 0.236.0", "wasmprinter", "wasmtime", "wasmtime-cli-flags", @@ -4727,7 +4794,7 @@ dependencies = [ "wasmtime", "wasmtime-internal-component-util", "wasmtime-internal-wit-bindgen", - "wit-parser", + "wit-parser 0.236.0", ] [[package]] @@ -4753,7 +4820,7 @@ dependencies = [ "smallvec", "target-lexicon", "thiserror 2.0.12", - "wasmparser 0.235.0", + "wasmparser 0.236.0", "wasmtime-environ", "wasmtime-internal-math", "wasmtime-internal-versioned-export-macros", @@ -4849,7 +4916,7 @@ dependencies = [ "gimli 0.32.0", "object 0.37.1", "target-lexicon", - "wasmparser 0.235.0", + "wasmparser 0.236.0", "wasmtime-environ", "wasmtime-internal-cranelift", "winch-codegen", @@ -4860,9 +4927,10 @@ name = "wasmtime-internal-wit-bindgen" version = "36.0.0" dependencies = [ "anyhow", + "bitflags 2.6.0", "heck 0.5.0", "indexmap 2.7.0", - "wit-parser", + "wit-parser 0.236.0", ] [[package]] @@ -5025,7 +5093,7 @@ version = "36.0.0" dependencies = [ "anyhow", "log", - "rand", + "rand 0.8.5", "wasi-common", "wasmtime", "wasmtime-wasi", @@ -5067,10 +5135,14 @@ name = "wasmtime-wast" version = "36.0.0" dependencies = [ "anyhow", + "json-from-wast", "log", + "object 0.37.1", + "serde_json", "tokio", + "wasmparser 0.236.0", "wasmtime", - "wast 235.0.0", + "wast 236.0.0", ] [[package]] @@ -5084,25 +5156,25 @@ dependencies = [ [[package]] name = "wast" -version = "235.0.0" +version = "236.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eda4293f626c99021bb3a6fbe4fbbe90c0e31a5ace89b5f620af8925de72e13" +checksum = "11d6b6faeab519ba6fbf9b26add41617ca6f5553f99ebc33d876e591d2f4f3c6" dependencies = [ "bumpalo", "gimli 0.31.1", "leb128fmt", "memchr", "unicode-width 0.2.0", - "wasm-encoder", + "wasm-encoder 0.236.0", ] [[package]] name = "wat" -version = "1.235.0" +version = "1.236.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e777e0327115793cb96ab220b98f85327ec3d11f34ec9e8d723264522ef206aa" +checksum = "cc31704322400f461f7f31a5f9190d5488aaeafb63ae69ad2b5888d2704dcb08" dependencies = [ - "wast 235.0.0", + "wast 236.0.0", ] [[package]] @@ -5232,7 +5304,7 @@ dependencies = [ "smallvec", "target-lexicon", "thiserror 2.0.12", - "wasmparser 0.235.0", + "wasmparser 0.236.0", "wasmtime-environ", "wasmtime-internal-cranelift", "wasmtime-internal-math", @@ -5479,7 +5551,7 @@ checksum = "2c53468e077362201de11999c85c07c36e12048a990a3e0d69da2bd61da355d0" dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser", + "wit-parser 0.235.0", ] [[package]] @@ -5522,9 +5594,9 @@ dependencies = [ "indexmap 2.7.0", "prettyplease", "syn 2.0.100", - "wasm-metadata", + "wasm-metadata 0.235.0", "wit-bindgen-core", - "wit-component", + "wit-component 0.235.0", ] [[package]] @@ -5555,10 +5627,29 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder", - "wasm-metadata", + "wasm-encoder 0.235.0", + "wasm-metadata 0.235.0", "wasmparser 0.235.0", - "wit-parser", + "wit-parser 0.235.0", +] + +[[package]] +name = "wit-component" +version = "0.236.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f1404fddf6cdadb06a0812faa433c03208f444b867543814aa36a6322f33684" +dependencies = [ + "anyhow", + "bitflags 2.6.0", + "indexmap 2.7.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.236.0", + "wasm-metadata 0.236.0", + "wasmparser 0.236.0", + "wit-parser 0.236.0", ] [[package]] @@ -5579,6 +5670,24 @@ dependencies = [ "wasmparser 0.235.0", ] +[[package]] +name = "wit-parser" +version = "0.236.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c643fd8e1a5c25a6d50299f8047e9a61e31cb486f8e230e944408da9b63a859" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.7.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.236.0", +] + [[package]] name = "witx" version = "0.9.1" diff --git a/Cargo.toml b/Cargo.toml index 3745ca138f..fcda76f552 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,7 +79,6 @@ gimli = { workspace = true, optional = true } pulley-interpreter = { workspace = true, optional = true } async-trait = { workspace = true } -trait-variant = { workspace = true } bytes = { workspace = true } cfg-if = { workspace = true } tokio = { workspace = true, optional = true, features = [ "signal", "macros" ] } @@ -103,7 +102,6 @@ criterion = { workspace = true } num_cpus = "1.13.0" memchr = "2.4" async-trait = { workspace = true } -trait-variant = { workspace = true } wat = { workspace = true } rayon = "1.5.0" wasmtime-wast = { workspace = true, features = ['component-model'] } @@ -330,17 +328,18 @@ wit-bindgen-rt = { version = "0.43.0", default-features = false } wit-bindgen-rust-macro = { version = "0.43.0", default-features = false } # wasm-tools family: -wasmparser = { version = "0.235.0", default-features = false, features = ['simd'] } -wat = "1.235.0" -wast = "235.0.0" -wasmprinter = "0.235.0" -wasm-encoder = "0.235.0" -wasm-smith = "0.235.0" -wasm-mutate = "0.235.0" -wit-parser = "0.235.0" -wit-component = "0.235.0" -wasm-wave = "0.235.0" -wasm-compose = "0.235.0" +wasmparser = { version = "0.236.0", default-features = false, features = ['simd'] } +wat = "1.236.0" +wast = "236.0.0" +wasmprinter = "0.236.0" +wasm-encoder = "0.236.0" +wasm-smith = "0.236.0" +wasm-mutate = "0.236.0" +wit-parser = "0.236.0" +wit-component = "0.236.0" +wasm-wave = "0.236.0" +wasm-compose = "0.236.0" +json-from-wast = "0.236.0" # Non-Bytecode Alliance maintained dependencies: # -------------------------- @@ -363,7 +362,6 @@ tracing = "0.1.26" bitflags = "2.0" thiserror = "2.0.12" async-trait = "0.1.71" -trait-variant = "0.1.2" heck = "0.5" similar = "2.1.0" toml = "0.8.10" @@ -371,7 +369,7 @@ mach2 = "0.4.2" memfd = "0.6.2" psm = "0.1.11" proptest = "1.0.0" -rand = { version = "0.8.3", features = ["small_rng"] } +rand = { version = "0.9.2", features = ["small_rng"] } # serde and serde_derive must have the same version serde = { version = "1.0.215", default-features = false, features = ['alloc'] } serde_derive = "1.0.188" @@ -501,7 +499,7 @@ disable-logging = ["log/max_level_off", "tracing/max_level_off"] wasi-nn = ["dep:wasmtime-wasi-nn"] wasi-tls = ["dep:wasmtime-wasi-tls"] wasi-threads = ["dep:wasmtime-wasi-threads", "threads"] -wasi-http = ["component-model", "dep:wasmtime-wasi-http", "wasmtime-wasi-http/p3", "dep:tokio", "dep:hyper"] +wasi-http = ["component-model", "dep:wasmtime-wasi-http", "dep:tokio", "dep:hyper", "wasmtime-wasi-http/default-send-request", "wasmtime-wasi-http/p3"] wasi-config = ["dep:wasmtime-wasi-config"] wasi-keyvalue = ["dep:wasmtime-wasi-keyvalue"] pooling-allocator = ["wasmtime/pooling-allocator", "wasmtime-cli-flags/pooling-allocator"] diff --git a/README.md b/README.md index 2f7ce746a5..9d04a43355 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ Windows or otherwise interested users can download installers and binaries directly from the [GitHub Releases](https://github.com/bytecodealliance/wasmtime/releases) page. +For additional installation options, refer to the [online book CLI installation page](https://docs.wasmtime.dev/cli-install.html). + Documentation on Wasmtime's currently supported versions can be found [in the online book documentation](https://docs.wasmtime.dev/stability-release.html#current-versions). @@ -63,7 +65,7 @@ rustup target add wasm32-wasip2 rustc hello.rs --target wasm32-wasip2 ``` -Once compiled, you can can run your component: +Once compiled, you can run your component: ```console wasmtime hello.wasm diff --git a/RELEASES.md b/RELEASES.md index 5358c52b68..312fd70fa0 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -6,6 +6,16 @@ Unreleased. ### Changed +Users who implemented `WasiHttpView::is_forbidden_header` from `wasmtime-wasi-http` now need to include `DEFAULT_FORBIDDEN_HEADERS`, e.g. `DEFAULT_FORBIDDEN_HEADERS.contains(name) || name.as_str() == "custom-forbidden-header"` #11292 + +### Fixed + +* Fix a panic in the host caused by preview1 guests using `fd_renumber`. + [CVE-2025-53901](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-fm79-3f68-h2fc). + +* Fix a panic in the preview1 adapter caused by guests using `fd_renumber`. + [#11277](https://github.com/bytecodealliance/wasmtime/pull/11277) + -------------------------------------------------------------------------------- Release notes for previous releases of Wasmtime can be found on the respective diff --git a/ci/miri-wast.sh b/ci/miri-wast.sh new file mode 100755 index 0000000000..12929314b7 --- /dev/null +++ b/ci/miri-wast.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Helper script to execute a `*.wast` test in Miri. This is only lightly used on +# CI and is provided here to assist with development of anything that ends up +# using unsafe for example. +# +# Example usage is: +# +# ./ci/miri-wast.sh ./tests/spec_testsuite/br_if.wast +# +# extra flags to this script are passed to `cargo run wast` which means they +# must be suitable flags for the `wast` subcommand. + +set -ex + +rm -rf ./miri-wast +mkdir ./miri-wast +cargo run -- wast --target pulley64 --precompile-save ./miri-wast "$@" \ + -O memory-reservation=$((1 << 20)) \ + -O memory-guard-size=0 \ + -O signals-based-traps=n \ + -O memory-init-cow=n + +MIRIFLAGS="$MIRIFLAGS -Zmiri-disable-isolation -Zmiri-permissive-provenance" \ + cargo miri run -- wast -Ccache=n --target pulley64 --precompile-load ./miri-wast "$@" \ + -O memory-init-cow=n diff --git a/ci/vendor-wit.sh b/ci/vendor-wit.sh index 71092971d6..a831a0683a 100755 --- a/ci/vendor-wit.sh +++ b/ci/vendor-wit.sh @@ -73,7 +73,7 @@ make_vendor "wasi-keyvalue" "keyvalue@219ea36" # clocks@13d1c82@wit-0.3.0-draft # filesystem@e2a2ddc@wit-0.3.0-draft # random@4e94663@wit-0.3.0-draft -# sockets@bb247e2@wit-0.3.0-draft +# sockets@e863ee2@wit-0.3.0-draft # " rm -rf $cache_dir diff --git a/cranelift/assembler-x64/meta/src/dsl.rs b/cranelift/assembler-x64/meta/src/dsl.rs index 11621a19df..9d14c7afa0 100644 --- a/cranelift/assembler-x64/meta/src/dsl.rs +++ b/cranelift/assembler-x64/meta/src/dsl.rs @@ -13,7 +13,7 @@ pub use custom::{Custom, Customization}; pub use encoding::{Encoding, ModRmKind, OpcodeMod}; pub use encoding::{Evex, Length, Vex, VexEscape, VexPrefix, evex, vex}; pub use encoding::{ - Group1Prefix, Group2Prefix, Group3Prefix, Group4Prefix, Opcodes, Prefixes, Rex, rex, + Group1Prefix, Group2Prefix, Group3Prefix, Group4Prefix, Opcodes, Prefixes, Rex, TupleType, rex, }; pub use features::{ALL_FEATURES, Feature, Features}; pub use format::{Eflags, Extension, Format, Location, Mutability, Operand, OperandKind, RegClass}; diff --git a/cranelift/assembler-x64/meta/src/dsl/encoding.rs b/cranelift/assembler-x64/meta/src/dsl/encoding.rs index 752eece6d0..e54608f1ea 100644 --- a/cranelift/assembler-x64/meta/src/dsl/encoding.rs +++ b/cranelift/assembler-x64/meta/src/dsl/encoding.rs @@ -47,7 +47,7 @@ pub fn vex(length: Length) -> Vex { /// An abbreviated constructor for EVEX-encoded instructions. #[must_use] -pub fn evex(length: Length) -> Evex { +pub fn evex(length: Length, tuple_type: TupleType) -> Evex { Evex { length, pp: None, @@ -56,6 +56,7 @@ pub fn evex(length: Length) -> Evex { opcode: u8::MAX, modrm: None, imm: Imm::None, + tuple_type, } } @@ -1243,6 +1244,9 @@ pub struct Evex { pub modrm: Option, /// See [`Rex.imm`](Rex.imm). pub imm: Imm, + /// The "Tuple Type" corresponding to scaling of the 8-bit displacement + /// parameter for memory operands. See [`TupleType`] for more information. + pub tuple_type: TupleType, } impl Evex { @@ -1361,6 +1365,36 @@ impl Evex { _ => None, } } + + /// Set the digit extending the opcode; equivalent to `/` in the + /// reference manual. + /// + /// # Panics + /// + /// Panics if `extension` is too large. + #[must_use] + pub fn digit(self, extension: u8) -> Self { + assert!(extension <= 0b111, "must fit in 3 bits"); + Self { + modrm: Some(ModRmKind::Digit(extension)), + ..self + } + } + + /// Append a byte-sized immediate operand (8-bit); equivalent to `ib` in the + /// reference manual. + /// + /// # Panics + /// + /// Panics if an immediate operand is already set. + #[must_use] + pub fn ib(self) -> Self { + assert_eq!(self.imm, Imm::None); + Self { + imm: Imm::ib, + ..self + } + } } impl From for Encoding { @@ -1388,3 +1422,25 @@ impl fmt::Display for Evex { Ok(()) } } + +/// Tuple Type definitions used in EVEX encodings. +/// +/// This enumeration corresponds to table 2-34 and 2-35 in the Intel manual. +/// This is a property of all instruction formats listed in the encoding table +/// for each instruction. +#[expect(missing_docs, reason = "matching manual names")] +pub enum TupleType { + Full, + Half, + FullMem, + Tuple1Scalar, + Tuple1Fixed, + Tuple2, + Tuple4, + Tuple8, + HalfMem, + QuarterMem, + EigthMem, + Mem128, + Movddup, +} diff --git a/cranelift/assembler-x64/meta/src/dsl/features.rs b/cranelift/assembler-x64/meta/src/dsl/features.rs index 6e2630385f..ca584ded00 100644 --- a/cranelift/assembler-x64/meta/src/dsl/features.rs +++ b/cranelift/assembler-x64/meta/src/dsl/features.rs @@ -87,6 +87,9 @@ pub enum Feature { avx2, avx512f, avx512vl, + avx512dq, + avx512bitalg, + avx512vbmi, cmpxchg16b, fma, } @@ -115,6 +118,9 @@ pub const ALL_FEATURES: &[Feature] = &[ Feature::avx2, Feature::avx512f, Feature::avx512vl, + Feature::avx512dq, + Feature::avx512bitalg, + Feature::avx512vbmi, Feature::cmpxchg16b, Feature::fma, ]; diff --git a/cranelift/assembler-x64/meta/src/generate/format.rs b/cranelift/assembler-x64/meta/src/generate/format.rs index 84b5047676..c2b9526179 100644 --- a/cranelift/assembler-x64/meta/src/generate/format.rs +++ b/cranelift/assembler-x64/meta/src/generate/format.rs @@ -18,7 +18,11 @@ enum ModRmStyle { /// The R/M bits are encoded with `rm` which is a `GprMem` or `XmmMem`, and /// the Reg/Opcode bits are encoded with `reg`. - RegMem { reg: ModRmReg, rm: dsl::Location }, + RegMem { + reg: ModRmReg, + rm: dsl::Location, + evex_scaling: Option, + }, /// Same as `RegMem` above except that this is also used for VEX-encoded /// instructios with "/is4" which indicates that the 4th register operand @@ -27,6 +31,7 @@ enum ModRmStyle { reg: ModRmReg, rm: dsl::Location, is4: dsl::Location, + evex_scaling: Option, }, } @@ -183,6 +188,7 @@ impl dsl::Format { ModRmStyle::RegMem { reg: ModRmReg::Digit(digit), rm: *mem, + evex_scaling: None, } } [Reg(reg), RegMem(mem) | Mem(mem)] @@ -194,6 +200,7 @@ impl dsl::Format { ModRmStyle::RegMem { reg: ModRmReg::Reg(*reg), rm: *mem, + evex_scaling: None, } } [Reg(dst), Reg(src), Imm(_)] | [Reg(dst), Reg(src)] => { @@ -214,8 +221,6 @@ impl dsl::Format { } fn generate_vex_prefix(&self, f: &mut Formatter, vex: &dsl::Vex) -> ModRmStyle { - use dsl::OperandKind::{FixedReg, Imm, Mem, Reg, RegMem}; - f.empty_line(); f.comment("Emit VEX prefix."); fmtln!(f, "let len = {:#03b};", vex.length.vex_bits()); @@ -224,13 +229,95 @@ impl dsl::Format { fmtln!(f, "let w = {};", vex.w.as_bool()); let bits = "len, pp, mmmmm, w"; + self.generate_vex_or_evex_prefix(f, "VexPrefix", &bits, vex.is4, None, || { + vex.unwrap_digit() + }) + } + + fn generate_evex_prefix(&self, f: &mut Formatter, evex: &dsl::Evex) -> ModRmStyle { + f.empty_line(); + f.comment("Emit EVEX prefix."); + let ll = evex.length.evex_bits(); + fmtln!(f, "let ll = {ll:#04b};"); + fmtln!(f, "let pp = {:#04b};", evex.pp.map_or(0b00, |pp| pp.bits())); + fmtln!(f, "let mmm = {:#07b};", evex.mmm.unwrap().bits()); + fmtln!(f, "let w = {};", evex.w.as_bool()); + // NB: when bcast is supported in the future the `evex_scaling` + // calculation for `Full` and `Half` below need to be updated. + let bcast = false; + fmtln!(f, "let bcast = {bcast};"); + let bits = format!("ll, pp, mmm, w, bcast"); + let is4 = false; + + let length_bytes = match evex.length { + dsl::Length::LZ | dsl::Length::LIG => unimplemented!(), + dsl::Length::L128 => 16, + dsl::Length::L256 => 32, + dsl::Length::L512 => 64, + }; + + // Figure out, according to table 2-34 and 2-35 in the Intel manual, + // what the scaling factor is for 8-bit displacements to pass through to + // encoding. + let evex_scaling = Some(match evex.tuple_type { + dsl::TupleType::Full => { + assert!(!bcast); + length_bytes + } + dsl::TupleType::Half => { + assert!(!bcast); + length_bytes / 2 + } + dsl::TupleType::FullMem => length_bytes, + // FIXME: according to table 2-35 this needs to take into account + // "InputSize" which isn't accounted for in our `Evex` structure at + // this time. + dsl::TupleType::Tuple1Scalar => unimplemented!(), + dsl::TupleType::Tuple1Fixed => unimplemented!(), + dsl::TupleType::Tuple2 => unimplemented!(), + dsl::TupleType::Tuple4 => unimplemented!(), + dsl::TupleType::Tuple8 => 32, + dsl::TupleType::HalfMem => length_bytes / 2, + dsl::TupleType::QuarterMem => length_bytes / 4, + dsl::TupleType::EigthMem => length_bytes / 8, + dsl::TupleType::Mem128 => 16, + dsl::TupleType::Movddup => match evex.length { + dsl::Length::LZ | dsl::Length::LIG => unimplemented!(), + dsl::Length::L128 => 8, + dsl::Length::L256 => 32, + dsl::Length::L512 => 64, + }, + }); + + self.generate_vex_or_evex_prefix(f, "EvexPrefix", &bits, is4, evex_scaling, || { + evex.unwrap_digit() + }) + } + + /// Helper function to generate either a vex or evex prefix, mostly handling + /// all the operand formats and structures here the same between the two + /// forms. + fn generate_vex_or_evex_prefix( + &self, + f: &mut Formatter, + prefix_type: &str, + bits: &str, + is4: bool, + evex_scaling: Option, + unwrap_digit: impl Fn() -> Option, + ) -> ModRmStyle { + use dsl::OperandKind::{FixedReg, Imm, Mem, Reg, RegMem}; + let style = match self.operands_by_kind().as_slice() { [Reg(reg), Reg(vvvv), Reg(rm)] => { - assert!(!vex.is4); + assert!(!is4); fmtln!(f, "let reg = self.{reg}.enc();"); fmtln!(f, "let vvvv = self.{vvvv}.enc();"); fmtln!(f, "let rm = self.{rm}.encode_bx_regs();"); - fmtln!(f, "let vex = VexPrefix::three_op(reg, vvvv, rm, {bits});"); + fmtln!( + f, + "let prefix = {prefix_type}::three_op(reg, vvvv, rm, {bits});" + ); ModRmStyle::Reg { reg: ModRmReg::Reg(*reg), rm: *rm, @@ -240,75 +327,91 @@ impl dsl::Format { | [Reg(reg), Reg(vvvv), Mem(rm)] | [Reg(reg), Reg(vvvv), RegMem(rm), Imm(_) | FixedReg(_)] | [Reg(reg), RegMem(rm), Reg(vvvv)] => { - assert!(!vex.is4); + assert!(!is4); fmtln!(f, "let reg = self.{reg}.enc();"); fmtln!(f, "let vvvv = self.{vvvv}.enc();"); fmtln!(f, "let rm = self.{rm}.encode_bx_regs();"); - fmtln!(f, "let vex = VexPrefix::three_op(reg, vvvv, rm, {bits});"); + fmtln!( + f, + "let prefix = {prefix_type}::three_op(reg, vvvv, rm, {bits});" + ); ModRmStyle::RegMem { reg: ModRmReg::Reg(*reg), rm: *rm, + evex_scaling, } } - [Reg(reg), Reg(vvvv), RegMem(rm), Reg(is4)] => { - assert!(vex.is4); + [Reg(reg), Reg(vvvv), RegMem(rm), Reg(r_is4)] => { + assert!(is4); fmtln!(f, "let reg = self.{reg}.enc();"); fmtln!(f, "let vvvv = self.{vvvv}.enc();"); fmtln!(f, "let rm = self.{rm}.encode_bx_regs();"); - fmtln!(f, "let vex = VexPrefix::three_op(reg, vvvv, rm, {bits});"); + fmtln!( + f, + "let prefix = {prefix_type}::three_op(reg, vvvv, rm, {bits});" + ); ModRmStyle::RegMemIs4 { reg: ModRmReg::Reg(*reg), rm: *rm, - is4: *is4, + is4: *r_is4, + evex_scaling, } } [Reg(reg_or_vvvv), RegMem(rm)] | [RegMem(rm), Reg(reg_or_vvvv)] - | [Reg(reg_or_vvvv), RegMem(rm), Imm(_)] => match vex.unwrap_digit() { + | [Reg(reg_or_vvvv), RegMem(rm), Imm(_)] => match unwrap_digit() { Some(digit) => { - assert!(!vex.is4); + assert!(!is4); let vvvv = reg_or_vvvv; fmtln!(f, "let reg = {digit:#x};"); fmtln!(f, "let vvvv = self.{vvvv}.enc();"); fmtln!(f, "let rm = self.{rm}.encode_bx_regs();"); - fmtln!(f, "let vex = VexPrefix::three_op(reg, vvvv, rm, {bits});"); + fmtln!( + f, + "let prefix = {prefix_type}::three_op(reg, vvvv, rm, {bits});" + ); ModRmStyle::RegMem { reg: ModRmReg::Digit(digit), rm: *rm, + evex_scaling, } } None => { - assert!(!vex.is4); + assert!(!is4); let reg = reg_or_vvvv; fmtln!(f, "let reg = self.{reg}.enc();"); fmtln!(f, "let rm = self.{rm}.encode_bx_regs();"); - fmtln!(f, "let vex = VexPrefix::two_op(reg, rm, {bits});"); + fmtln!(f, "let prefix = {prefix_type}::two_op(reg, rm, {bits});"); ModRmStyle::RegMem { reg: ModRmReg::Reg(*reg), rm: *rm, + evex_scaling, } } }, [Reg(reg_or_vvvv), Reg(rm)] | [Reg(reg_or_vvvv), Reg(rm), Imm(_)] => { - match vex.unwrap_digit() { + match unwrap_digit() { Some(digit) => { - assert!(!vex.is4); + assert!(!is4); let vvvv = reg_or_vvvv; fmtln!(f, "let reg = {digit:#x};"); fmtln!(f, "let vvvv = self.{vvvv}.enc();"); fmtln!(f, "let rm = self.{rm}.encode_bx_regs();"); - fmtln!(f, "let vex = VexPrefix::three_op(reg, vvvv, rm, {bits});"); + fmtln!( + f, + "let prefix = {prefix_type}::three_op(reg, vvvv, rm, {bits});" + ); ModRmStyle::Reg { reg: ModRmReg::Digit(digit), rm: *rm, } } None => { - assert!(!vex.is4); + assert!(!is4); let reg = reg_or_vvvv; fmtln!(f, "let reg = self.{reg}.enc();"); fmtln!(f, "let rm = self.{rm}.encode_bx_regs();"); - fmtln!(f, "let vex = VexPrefix::two_op(reg, rm, {bits});"); + fmtln!(f, "let prefix = {prefix_type}::two_op(reg, rm, {bits});"); ModRmStyle::Reg { reg: ModRmReg::Reg(*reg), rm: *rm, @@ -317,49 +420,20 @@ impl dsl::Format { } } [Reg(reg), Mem(rm)] | [Mem(rm), Reg(reg)] | [RegMem(rm), Reg(reg), Imm(_)] => { - assert!(!vex.is4); + assert!(!is4); fmtln!(f, "let reg = self.{reg}.enc();"); fmtln!(f, "let rm = self.{rm}.encode_bx_regs();"); - fmtln!(f, "let vex = VexPrefix::two_op(reg, rm, {bits});"); + fmtln!(f, "let prefix = {prefix_type}::two_op(reg, rm, {bits});"); ModRmStyle::RegMem { reg: ModRmReg::Reg(*reg), rm: *rm, + evex_scaling, } } unknown => unimplemented!("unknown pattern: {unknown:?}"), }; - fmtln!(f, "vex.encode(buf);"); - style - } - - fn generate_evex_prefix(&self, f: &mut Formatter, evex: &dsl::Evex) -> ModRmStyle { - use dsl::OperandKind::{Reg, RegMem}; - - f.empty_line(); - f.comment("Emit EVEX prefix."); - let ll = evex.length.evex_bits(); - fmtln!(f, "let ll = {ll:#04b};"); - fmtln!(f, "let pp = {:#04b};", evex.pp.map_or(0b00, |pp| pp.bits())); - fmtln!(f, "let mmm = {:#07b};", evex.mmm.unwrap().bits()); - fmtln!(f, "let w = {};", evex.w.as_bool()); - fmtln!(f, "let bcast = false;"); - let bits = format!("ll, pp, mmm, w, bcast"); - - let style = match self.operands_by_kind().as_slice() { - [Reg(reg), Reg(vvvv), RegMem(rm)] => { - fmtln!(f, "let reg = self.{reg}.enc();"); - fmtln!(f, "let vvvv = self.{vvvv}.enc();"); - fmtln!(f, "let rm = self.{rm}.encode_bx_regs();"); - fmtln!(f, "let evex = EvexPrefix::new(reg, vvvv, rm, {bits});"); - ModRmStyle::RegMem { - reg: ModRmReg::Reg(*reg), - rm: *rm, - } - } - unknown => unimplemented!("unknown pattern: {unknown:?}"), - }; - fmtln!(f, "evex.encode(buf);"); + fmtln!(f, "prefix.encode(buf);"); style } @@ -382,14 +456,24 @@ impl dsl::Format { match modrm_style { ModRmStyle::None => {} - ModRmStyle::RegMem { reg, rm } | ModRmStyle::RegMemIs4 { reg, rm, is4: _ } => { + ModRmStyle::RegMem { + reg, + rm, + evex_scaling, + } + | ModRmStyle::RegMemIs4 { + reg, + rm, + is4: _, + evex_scaling, + } => { match reg { ModRmReg::Reg(reg) => fmtln!(f, "let reg = self.{reg}.enc();"), ModRmReg::Digit(digit) => fmtln!(f, "let reg = {digit:#x};"), } fmtln!( f, - "self.{rm}.encode_rex_suffixes(buf, reg, {bytes_at_end});" + "self.{rm}.encode_rex_suffixes(buf, reg, {bytes_at_end}, {evex_scaling:?});" ); } ModRmStyle::Reg { reg, rm } => { diff --git a/cranelift/assembler-x64/meta/src/instructions/abs.rs b/cranelift/assembler-x64/meta/src/instructions/abs.rs index b1745d6e1a..4394cdc385 100644 --- a/cranelift/assembler-x64/meta/src/instructions/abs.rs +++ b/cranelift/assembler-x64/meta/src/instructions/abs.rs @@ -1,14 +1,20 @@ -use crate::dsl::{Feature::*, Inst, Length::*, Location::*}; -use crate::dsl::{align, fmt, inst, r, rex, vex, w}; +use crate::dsl::{Feature::*, Inst, Length::*, Location::*, TupleType::*}; +use crate::dsl::{align, evex, fmt, inst, r, rex, vex, w}; #[rustfmt::skip] // Keeps instructions on a single line. pub fn list() -> Vec { vec![ inst("pabsb", fmt("A", [w(xmm1), r(align(xmm_m128))]), rex([0x66, 0x0F, 0x38, 0x1C]), _64b | compat | ssse3).alt(avx, "vpabsb_a"), inst("vpabsb", fmt("A", [w(xmm1), r(xmm_m128)]), vex(L128)._66()._0f38().op(0x1C), _64b | compat | avx), + // FIXME: uncomment once the avx512bw feature is bound + // inst("vpabsb", fmt("B", [w(xmm1), r(xmm_m128)]), evex(L128, FullMem)._66()._0f38().wig().op(0x1C).r(), _64b | compat | avx512vl | avx512bw), inst("pabsw", fmt("A", [w(xmm1), r(align(xmm_m128))]), rex([0x66, 0x0F, 0x38, 0x1D]), _64b | compat | ssse3).alt(avx, "vpabsw_a"), inst("vpabsw", fmt("A", [w(xmm1), r(xmm_m128)]), vex(L128)._66()._0f38().op(0x1D), _64b | compat | avx), + // FIXME: uncomment once the avx512bw feature is bound + // inst("vpabsw", fmt("B", [w(xmm1), r(xmm_m128)]), evex(L128, FullMem)._66()._0f38().wig().op(0x1D).r(), _64b | compat | avx512vl | avx512bw), inst("pabsd", fmt("A", [w(xmm1), r(align(xmm_m128))]), rex([0x66, 0x0F, 0x38, 0x1E]), _64b | compat | ssse3).alt(avx, "vpabsd_a"), inst("vpabsd", fmt("A", [w(xmm1), r(xmm_m128)]), vex(L128)._66()._0f38().op(0x1E), _64b | compat | avx), + inst("vpabsd", fmt("C", [w(xmm1), r(xmm_m128)]), evex(L128, Full)._66()._0f38().w0().op(0x1E).r(), _64b | compat | avx512vl | avx512f), + inst("vpabsq", fmt("C", [w(xmm1), r(xmm_m128)]), evex(L128, Full)._66()._0f38().w1().op(0x1F).r(), _64b | compat | avx512vl | avx512f), ] } diff --git a/cranelift/assembler-x64/meta/src/instructions/add.rs b/cranelift/assembler-x64/meta/src/instructions/add.rs index e65bfbcc4a..27c0995451 100644 --- a/cranelift/assembler-x64/meta/src/instructions/add.rs +++ b/cranelift/assembler-x64/meta/src/instructions/add.rs @@ -1,4 +1,4 @@ -use crate::dsl::{Customization::*, Feature::*, Inst, Length::*, Location::*}; +use crate::dsl::{Customization::*, Feature::*, Inst, Length::*, Location::*, TupleType::*}; use crate::dsl::{align, evex, fmt, inst, r, rex, rw, sxl, sxq, vex, w}; #[rustfmt::skip] // Keeps instructions on a single line. @@ -96,6 +96,6 @@ pub fn list() -> Vec { inst("vpaddusw", fmt("B", [w(xmm1), r(xmm2), r(xmm_m128)]), vex(L128)._66()._0f().op(0xDD).r(), _64b | compat | avx), inst("vphaddw", fmt("B", [w(xmm1), r(xmm2), r(xmm_m128)]), vex(L128)._66()._0f38().op(0x01).r(), _64b | compat | avx), inst("vphaddd", fmt("B", [w(xmm1), r(xmm2), r(xmm_m128)]), vex(L128)._66()._0f38().op(0x02).r(), _64b | compat | avx), - inst("vaddpd", fmt("C", [w(xmm1), r(xmm2), r(xmm_m128)]), evex(L128)._66()._0f().w1().op(0x58).r(), _64b | compat | avx512vl), + inst("vaddpd", fmt("C", [w(xmm1), r(xmm2), r(xmm_m128)]), evex(L128, Full)._66()._0f().w1().op(0x58).r(), _64b | compat | avx512vl), ] } diff --git a/cranelift/assembler-x64/meta/src/instructions/bitmanip.rs b/cranelift/assembler-x64/meta/src/instructions/bitmanip.rs index 82b7a25c4a..1b7b0b35a3 100644 --- a/cranelift/assembler-x64/meta/src/instructions/bitmanip.rs +++ b/cranelift/assembler-x64/meta/src/instructions/bitmanip.rs @@ -1,5 +1,5 @@ -use crate::dsl::{Eflags::*, Feature::*, Inst, Length::*, Location::*}; -use crate::dsl::{fmt, implicit, inst, r, rex, rw, vex, w}; +use crate::dsl::{Eflags::*, Feature::*, Inst, Length::*, Location::*, TupleType::*}; +use crate::dsl::{evex, fmt, implicit, inst, r, rex, rw, vex, w}; #[rustfmt::skip] // Keeps instructions on a single line. pub fn list() -> Vec { @@ -61,5 +61,11 @@ pub fn list() -> Vec { // BMI2 instructions inst("bzhil", fmt("RMV", [w(r32a), r(rm32), r(r32b)]), vex(LZ)._0f38().w0().op(0xF5), _64b | compat | bmi2), inst("bzhiq", fmt("RMV", [w(r64a), r(rm64), r(r64b)]), vex(LZ)._0f38().w1().op(0xF5), _64b | bmi2), + + inst("vpopcntb", fmt("A", [w(xmm1), r(xmm_m128)]), evex(L128, FullMem)._66()._0f38().w0().op(0x54).r(), _64b | compat | avx512vl | avx512bitalg), + inst("vpopcntw", fmt("A", [w(xmm1), r(xmm_m128)]), evex(L128, FullMem)._66()._0f38().w1().op(0x54).r(), _64b | compat | avx512vl | avx512bitalg), + // FIXME: uncomment when avx512vpopcntdq is bound in cranelift + // inst("vpopcntd", fmt("A", [w(xmm1), r(xmm_m128)]), evex(L128, Full)._66()._0f38().w0().op(0x55).r(), _64b | compat | avx512vl | avx512vpopcntdq), + // inst("vpopcntq", fmt("A", [w(xmm1), r(xmm_m128)]), evex(L128, Full)._66()._0f38().w1().op(0x55).r(), _64b | compat | avx512vl | avx512vpopcntdq), ] } diff --git a/cranelift/assembler-x64/meta/src/instructions/cvt.rs b/cranelift/assembler-x64/meta/src/instructions/cvt.rs index 8791aacca7..e700d22414 100644 --- a/cranelift/assembler-x64/meta/src/instructions/cvt.rs +++ b/cranelift/assembler-x64/meta/src/instructions/cvt.rs @@ -1,5 +1,5 @@ -use crate::dsl::{Customization::*, Feature::*, Inst, Length::*, Location::*}; -use crate::dsl::{align, fmt, inst, r, rex, rw, vex, w}; +use crate::dsl::{Customization::*, Feature::*, Inst, Length::*, Location::*, TupleType::*}; +use crate::dsl::{align, evex, fmt, inst, r, rex, rw, vex, w}; #[rustfmt::skip] // Keeps instructions on a single line. pub fn list() -> Vec { @@ -64,5 +64,7 @@ pub fn list() -> Vec { // * cvtps2pi // * cvttpd2pi // * cvttps2pi + + inst("vcvtudq2ps", fmt("A", [w(xmm1), r(xmm_m128)]), evex(L128, Full)._f2()._0f().w0().op(0x7A).r(), _64b | avx512vl | avx512f), ] } diff --git a/cranelift/assembler-x64/meta/src/instructions/lanes.rs b/cranelift/assembler-x64/meta/src/instructions/lanes.rs index 85e52d0187..d85f3c9b2c 100644 --- a/cranelift/assembler-x64/meta/src/instructions/lanes.rs +++ b/cranelift/assembler-x64/meta/src/instructions/lanes.rs @@ -1,5 +1,5 @@ -use crate::dsl::{Feature::*, Inst, Length::*, Location::*}; -use crate::dsl::{align, fmt, inst, r, rex, rw, vex, w}; +use crate::dsl::{Feature::*, Inst, Length::*, Location::*, TupleType::*}; +use crate::dsl::{align, evex, fmt, inst, r, rex, rw, vex, w}; #[rustfmt::skip] // Keeps instructions on a single line. pub fn list() -> Vec { @@ -86,5 +86,8 @@ pub fn list() -> Vec { inst("vpbroadcastw", fmt("A", [w(xmm1), r(xmm_m16)]), vex(L128)._66()._0f38().w0().op(0x79).r(), _64b | compat | avx2), inst("vpbroadcastd", fmt("A", [w(xmm1), r(xmm_m32)]), vex(L128)._66()._0f38().w0().op(0x58).r(), _64b | compat | avx2), inst("vpbroadcastq", fmt("A", [w(xmm1), r(xmm_m64)]), vex(L128)._66()._0f38().w0().op(0x59).r(), _64b | compat | avx2), + + // AVX-512 permutations + inst("vpermi2b", fmt("A", [rw(xmm1), r(xmm2), r(xmm_m128)]), evex(L128, FullMem)._66()._0f38().w0().op(0x75).r(), _64b | compat | avx512vl | avx512vbmi), ] } diff --git a/cranelift/assembler-x64/meta/src/instructions/mul.rs b/cranelift/assembler-x64/meta/src/instructions/mul.rs index 5d58be86ab..9f82eb3d06 100644 --- a/cranelift/assembler-x64/meta/src/instructions/mul.rs +++ b/cranelift/assembler-x64/meta/src/instructions/mul.rs @@ -1,5 +1,5 @@ -use crate::dsl::{Customization::*, Feature::*, Inst, Length::*, Location::*}; -use crate::dsl::{align, fmt, implicit, inst, r, rex, rw, sxl, sxq, sxw, vex, w}; +use crate::dsl::{Customization::*, Feature::*, Inst, Length::*, Location::*, TupleType::*}; +use crate::dsl::{align, evex, fmt, implicit, inst, r, rex, rw, sxl, sxq, sxw, vex, w}; #[rustfmt::skip] // Keeps instructions on a single line. pub fn list() -> Vec { @@ -55,5 +55,8 @@ pub fn list() -> Vec { inst("vpmulld", fmt("B", [w(xmm1), r(xmm2), r(xmm_m128)]), vex(L128)._66()._0f38().op(0x40), _64b | compat | avx), inst("vpmullw", fmt("B", [w(xmm1), r(xmm2), r(xmm_m128)]), vex(L128)._66()._0f().op(0xD5), _64b | compat | avx), inst("vpmuludq", fmt("B", [w(xmm1), r(xmm2), r(xmm_m128)]), vex(L128)._66()._0f().op(0xF4), _64b | compat | avx), + + inst("vpmulld", fmt("C", [w(xmm1), r(xmm2), r(xmm_m128)]), evex(L128, Full)._66()._0f38().w0().op(0x40).r(), _64b | compat | avx512vl | avx512f), + inst("vpmullq", fmt("C", [w(xmm1), r(xmm2), r(xmm_m128)]), evex(L128, Full)._66()._0f38().w1().op(0x40).r(), _64b | compat | avx512vl | avx512dq), ] } diff --git a/cranelift/assembler-x64/meta/src/instructions/shift.rs b/cranelift/assembler-x64/meta/src/instructions/shift.rs index 35b46a70cf..1d1c561711 100644 --- a/cranelift/assembler-x64/meta/src/instructions/shift.rs +++ b/cranelift/assembler-x64/meta/src/instructions/shift.rs @@ -1,5 +1,5 @@ -use crate::dsl::{Customization::*, Feature::*, Inst, Length::*, Location::*}; -use crate::dsl::{align, fmt, inst, r, rex, rw, vex, w}; +use crate::dsl::{Customization::*, Feature::*, Inst, Length::*, Location::*, TupleType::*}; +use crate::dsl::{align, evex, fmt, inst, r, rex, rw, vex, w}; #[rustfmt::skip] // Keeps instructions on a single line. pub fn list() -> Vec { @@ -98,6 +98,14 @@ pub fn list() -> Vec { inst("vpslld", fmt("D", [w(xmm1), r(xmm2), r(imm8)]), vex(L128)._66()._0f().op(0x72).digit(6).ib(), _64b | compat | avx), inst("vpsllq", fmt("C", [w(xmm1), r(xmm2), r(xmm_m128)]), vex(L128)._66()._0f().op(0xF3).r(), _64b | compat | avx), inst("vpsllq", fmt("D", [w(xmm1), r(xmm2), r(imm8)]), vex(L128)._66()._0f().op(0x73).digit(6).ib(), _64b | compat | avx), + // FIXME: uncomment once the avx512bw feature is bound + // inst("vpsllw", fmt("G", [w(xmm1), r(xmm2), r(xmm_m128)]), evex(L128, Mem128)._66()._0f().wig().op(0xF1).r(), _64b | compat | avx512vl | avx512bw), + // inst("vpsllw", fmt("E", [w(xmm1), r(xmm_m128), r(imm8)]), evex(L128, FullMem)._66()._0f().wig().op(0x71).digit(6).ib(), _64b | compat | avx512vl | avx512bw), + inst("vpslld", fmt("G", [w(xmm1), r(xmm2), r(xmm_m128)]), evex(L128, Mem128)._66()._0f().w0().op(0xF2).r(), _64b | compat | avx512vl | avx512f), + inst("vpslld", fmt("F", [w(xmm1), r(xmm_m128), r(imm8)]), evex(L128, Full)._66()._0f().w0().op(0x72).digit(6).ib(), _64b | compat | avx512vl | avx512f), + inst("vpsllq", fmt("G", [w(xmm1), r(xmm2), r(xmm_m128)]), evex(L128, Mem128)._66()._0f().w1().op(0xF3).r(), _64b | compat | avx512vl | avx512f), + inst("vpsllq", fmt("F", [w(xmm1), r(xmm_m128), r(imm8)]), evex(L128, Full)._66()._0f().w1().op(0x73).digit(6).ib(), _64b | compat | avx512vl | avx512f), + // Vector instructions (shift right). inst("psraw", fmt("A", [rw(xmm1), r(align(xmm_m128))]), rex([0x66, 0x0F, 0xE1]).r(), _64b | compat | sse2).alt(avx, "vpsraw_c"), inst("psraw", fmt("B", [rw(xmm1), r(imm8)]), rex([0x66, 0x0F, 0x71]).digit(4).ib(), _64b | compat | sse2).alt(avx, "vpsraw_d"), @@ -119,5 +127,19 @@ pub fn list() -> Vec { inst("vpsrld", fmt("D", [w(xmm1), r(xmm2), r(imm8)]), vex(L128)._66()._0f().op(0x72).digit(2).ib(), _64b | compat | avx), inst("vpsrlq", fmt("C", [w(xmm1), r(xmm2), r(xmm_m128)]), vex(L128)._66()._0f().op(0xD3).r(), _64b | compat | avx), inst("vpsrlq", fmt("D", [w(xmm1), r(xmm2), r(imm8)]), vex(L128)._66()._0f().op(0x73).digit(2).ib(), _64b | compat | avx), + // FIXME: uncomment once the avx512bw feature is bound + // inst("vpsraw", fmt("G", [w(xmm1), r(xmm2), r(xmm_m128)]), evex(L128, Mem128)._66()._0f().wig().op(0xE1).r(), _64b | compat | avx512vl | avx512bw), + // inst("vpsraw", fmt("E", [w(xmm1), r(xmm_m128), r(imm8)]), evex(L128, FullMem)._66()._0f().wig().op(0x71).digit(4).ib(), _64b | compat | avx512vl | avx512bw), + inst("vpsrad", fmt("G", [w(xmm1), r(xmm2), r(xmm_m128)]), evex(L128, Mem128)._66()._0f().w0().op(0xE2).r(), _64b | compat | avx512vl | avx512f), + inst("vpsrad", fmt("F", [w(xmm1), r(xmm_m128), r(imm8)]), evex(L128, Full)._66()._0f().w0().op(0x72).digit(4).ib(), _64b | compat | avx512vl | avx512f), + inst("vpsraq", fmt("G", [w(xmm1), r(xmm2), r(xmm_m128)]), evex(L128, Mem128)._66()._0f().w1().op(0xE2).r(), _64b | compat | avx512vl | avx512f), + inst("vpsraq", fmt("F", [w(xmm1), r(xmm_m128), r(imm8)]), evex(L128, Full)._66()._0f().w1().op(0x72).digit(4).ib(), _64b | compat | avx512vl | avx512f), + // FIXME: uncomment once the avx512bw feature is bound + // inst("vpsrlw", fmt("G", [w(xmm1), r(xmm2), r(xmm_m128)]), evex(L128, Mem128)._66()._0f().wig().op(0xD1).r(), _64b | compat | avx512vl | avx512bw), + // inst("vpsrlw", fmt("E", [w(xmm1), r(xmm_m128), r(imm8)]), evex(L128, FullMem)._66()._0f().wig().op(0x71).digit(2).ib(), _64b | compat | avx512vl | avx512bw), + inst("vpsrld", fmt("G", [w(xmm1), r(xmm2), r(xmm_m128)]), evex(L128, Mem128)._66()._0f().w0().op(0xD2).r(), _64b | compat | avx512vl | avx512f), + inst("vpsrld", fmt("F", [w(xmm1), r(xmm_m128), r(imm8)]), evex(L128, Full)._66()._0f().w0().op(0x72).digit(2).ib(), _64b | compat | avx512vl | avx512f), + inst("vpsrlq", fmt("G", [w(xmm1), r(xmm2), r(xmm_m128)]), evex(L128, Mem128)._66()._0f().w1().op(0xD3).r(), _64b | compat | avx512vl | avx512f), + inst("vpsrlq", fmt("F", [w(xmm1), r(xmm_m128), r(imm8)]), evex(L128, Full)._66()._0f().w1().op(0x73).digit(2).ib(), _64b | compat | avx512vl | avx512f), ] } diff --git a/cranelift/assembler-x64/src/evex.rs b/cranelift/assembler-x64/src/evex.rs index 7cb45493e7..bf89dd13bd 100644 --- a/cranelift/assembler-x64/src/evex.rs +++ b/cranelift/assembler-x64/src/evex.rs @@ -65,6 +65,33 @@ impl EvexPrefix { } } + /// Construct the [`EvexPrefix`] for an instruction. + pub fn two_op( + reg: u8, + (b, x): (Option, Option), + ll: u8, + pp: u8, + mmm: u8, + w: bool, + broadcast: bool, + ) -> Self { + EvexPrefix::new(reg, 0, (b, x), ll, pp, mmm, w, broadcast) + } + + /// Construct the [`EvexPrefix`] for an instruction. + pub fn three_op( + reg: u8, + vvvv: u8, + (b, x): (Option, Option), + ll: u8, + pp: u8, + mmm: u8, + w: bool, + broadcast: bool, + ) -> Self { + EvexPrefix::new(reg, vvvv, (b, x), ll, pp, mmm, w, broadcast) + } + pub(crate) fn encode(&self, sink: &mut impl CodeSink) { sink.put1(0x62); sink.put1(self.byte1); diff --git a/cranelift/assembler-x64/src/fuzz.rs b/cranelift/assembler-x64/src/fuzz.rs index 8ab5fa26da..250e08a43e 100644 --- a/cranelift/assembler-x64/src/fuzz.rs +++ b/cranelift/assembler-x64/src/fuzz.rs @@ -267,7 +267,7 @@ fn fix_up(dis: &str) -> std::borrow::Cow<'_, str> { /// Fuzz-specific registers. /// /// For the fuzzer, we do not need any fancy register types; see [`FuzzReg`]. -#[derive(Arbitrary, Debug)] +#[derive(Clone, Arbitrary, Debug)] pub struct FuzzRegs; impl Registers for FuzzRegs { @@ -298,6 +298,25 @@ impl AsReg for FuzzReg { } } +impl Arbitrary<'_> for AmodeOffset { + fn arbitrary(u: &mut Unstructured<'_>) -> Result { + // Custom implementation to try to generate some "interesting" offsets. + // For example choose either an arbitrary 8-bit or 32-bit number as the + // base, and then optionally shift that number to the left to create + // multiples of constants. This can help stress some of the more + // interesting encodings in EVEX instructions for example. + let base = if u.arbitrary()? { + i32::from(u.arbitrary::()?) + } else { + u.arbitrary::()? + }; + Ok(match u.int_in_range(0..=5)? { + 0 => AmodeOffset::ZERO, + n => AmodeOffset::new(base << (n - 1)), + }) + } +} + impl Arbitrary<'_> for AmodeOffsetPlusKnownOffset { fn arbitrary(u: &mut Unstructured<'_>) -> Result { // For now, we don't generate offsets (TODO). diff --git a/cranelift/assembler-x64/src/mem.rs b/cranelift/assembler-x64/src/mem.rs index 40c051de2a..19cf06707e 100644 --- a/cranelift/assembler-x64/src/mem.rs +++ b/cranelift/assembler-x64/src/mem.rs @@ -55,8 +55,9 @@ impl Amode { sink: &mut impl CodeSink, enc_reg: u8, bytes_at_end: u8, + evex_scaling: Option, ) { - emit_modrm_sib_disp(sink, enc_reg, self, bytes_at_end, None); + emit_modrm_sib_disp(sink, enc_reg, self, bytes_at_end, evex_scaling); } /// Return the registers for encoding the `b` and `x` bits (e.g., in a VEX @@ -76,7 +77,6 @@ impl Amode { /// A 32-bit immediate for address offsets. #[derive(Clone, Copy, Debug, PartialEq)] -#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))] pub struct AmodeOffset(i32); impl AmodeOffset { @@ -286,13 +286,14 @@ impl GprMem { sink: &mut impl CodeSink, enc_reg: u8, bytes_at_end: u8, + evex_scaling: Option, ) { match self { GprMem::Gpr(gpr) => { sink.put1(encode_modrm(0b11, enc_reg & 0b111, gpr.enc() & 0b111)); } GprMem::Mem(amode) => { - amode.encode_rex_suffixes(sink, enc_reg, bytes_at_end); + amode.encode_rex_suffixes(sink, enc_reg, bytes_at_end, evex_scaling); } } } @@ -354,13 +355,14 @@ impl XmmMem { sink: &mut impl CodeSink, enc_reg: u8, bytes_at_end: u8, + evex_scaling: Option, ) { match self { XmmMem::Xmm(xmm) => { sink.put1(encode_modrm(0b11, enc_reg & 0b111, xmm.enc() & 0b111)); } XmmMem::Mem(amode) => { - amode.encode_rex_suffixes(sink, enc_reg, bytes_at_end); + amode.encode_rex_suffixes(sink, enc_reg, bytes_at_end, evex_scaling); } } } diff --git a/cranelift/codegen/src/inline.rs b/cranelift/codegen/src/inline.rs index 8bff93febf..321af3fa7e 100644 --- a/cranelift/codegen/src/inline.rs +++ b/cranelift/codegen/src/inline.rs @@ -20,7 +20,7 @@ //! Cranelift the body of the callee that is to be inlined. use crate::cursor::{Cursor as _, FuncCursor}; -use crate::ir::{self, InstBuilder as _}; +use crate::ir::{self, ExceptionTableData, ExceptionTableItem, InstBuilder as _}; use crate::result::CodegenResult; use crate::trace; use crate::traversals::Dfs; @@ -73,7 +73,7 @@ pub trait Inline { /// Failure to uphold these invariants may result in panics during /// compilation or incorrect runtime behavior in the generated code. fn inline( - &self, + &mut self, caller: &ir::Function, call_inst: ir::Inst, call_opcode: ir::Opcode, @@ -82,12 +82,12 @@ pub trait Inline { ) -> InlineCommand<'_>; } -impl<'a, T> Inline for &'a T +impl<'a, T> Inline for &'a mut T where T: Inline, { fn inline( - &self, + &mut self, caller: &ir::Function, inst: ir::Inst, opcode: ir::Opcode, @@ -102,7 +102,12 @@ where /// instruction, and inline the callee when directed to do so. /// /// Returns whether any call was inlined. -pub(crate) fn do_inlining(func: &mut ir::Function, inliner: impl Inline) -> CodegenResult { +pub(crate) fn do_inlining( + func: &mut ir::Function, + mut inliner: impl Inline, +) -> CodegenResult { + trace!("function {} before inlining: {}", func.name, func); + let mut inlined_any = false; let mut allocs = InliningAllocs::default(); @@ -175,6 +180,12 @@ pub(crate) fn do_inlining(func: &mut ir::Function, inliner: impl Inline) -> Code } } + if inlined_any { + trace!("function {} after inlining: {}", func.name, func); + } else { + trace!("function {} did not have any callees inlined", func.name); + } + Ok(inlined_any) } @@ -202,15 +213,6 @@ struct InliningAllocs { /// do not require set-membership testing, so a hash set is not a good /// choice either. calls_needing_exception_table_fixup: Vec, - - /// The set of existing tags already caught by an inlined `try_call` - /// instruction's exception table, for filtering out duplicate tags when - /// merging exception tables. - /// - /// Note: this is a `HashSet`, and not an `EntitySet`, because tags indices - /// are completely arbitrary and there is no guarantee that they start from - /// zero or are contiguous. - existing_exception_tags: crate::HashSet>, } impl InliningAllocs { @@ -219,7 +221,6 @@ impl InliningAllocs { values, constants, calls_needing_exception_table_fixup, - existing_exception_tags, } = self; values.clear(); @@ -232,11 +233,6 @@ impl InliningAllocs { // `calls_needing_exception_table_fixup` because it is a sparse set and // we don't know how large it needs to be ahead of time. calls_needing_exception_table_fixup.clear(); - - // Note: We do not reserve capacity for `existing_exception_tags` - // because it is a sparse set and we don't know how large it needs to be - // ahead of time. - existing_exception_tags.clear(); } fn set_inlined_value( @@ -414,6 +410,18 @@ fn inline_one( } } + // We copied *all* callee blocks into the caller's layout, but only copied + // the callee instructions in *reachable* callee blocks into the caller's + // associated blocks. Therefore, any *unreachable* blocks are empty in the + // caller, which is invalid CLIF because all blocks must end in a + // terminator, so do a quick pass over the inlined blocks and remove any + // empty blocks from the caller's layout. + for block in entity_map.iter_inlined_blocks(func) { + if func.layout.first_inst(block).is_none() { + func.layout.remove_block(block); + } + } + // Final step: fixup the exception tables of any inlined calls when we are // inlining a `try_call` site. // @@ -604,31 +612,24 @@ fn fixup_inlined_call_exception_tables( exception, .. } => { - // Gather the set of tags that this instruction's exception - // table already has entries for. - allocs.existing_exception_tags.clear(); - allocs.existing_exception_tags.extend( + // Construct a new exception table that consists of + // the inlined instruction's exception table match + // sequence, with the inlining site's exception table + // appended. This will ensure that the first-match + // semantics emulates the original behavior of + // matching in the inner frame first. + let sig = func.dfg.exception_tables[exception].signature(); + let normal_return = *func.dfg.exception_tables[exception].normal_return(); + let exception_data = ExceptionTableData::new( + sig, + normal_return, func.dfg.exception_tables[exception] - .catches() - .map(|(c, _)| c), - ); - - // Add only the catch edges from our original `try_call`'s - // exception table that are not already handled by this - // instruction. - for i in 0..func.dfg.exception_tables[call_exception_table].len_catches() { - let exception_tables = &mut func.stencil.dfg.exception_tables; - let value_lists = &mut func.stencil.dfg.value_lists; - - let (tag, block_call) = - exception_tables[call_exception_table].get_catch(i).unwrap(); - if allocs.existing_exception_tags.contains(&tag) { - continue; - } + .items() + .chain(func.dfg.exception_tables[call_exception_table].items()), + ) + .deep_clone(&mut func.dfg.value_lists); - let block_call = block_call.deep_clone(value_lists); - exception_tables[exception].push_catch(tag, block_call); - } + func.dfg.exception_tables[exception] = exception_data; } otherwise => unreachable!("unknown non-return call instruction: {otherwise:?}"), @@ -817,8 +818,18 @@ impl<'a> ir::instructions::InstructionMapper for InliningInstRemapper<'a> { let inlined_sig_ref = self.map_sig_ref(exception_table.signature()); let inlined_normal_return = self.map_block_call(*exception_table.normal_return()); let inlined_table = exception_table - .catches() - .map(|(tag, callee_block_call)| (tag, self.map_block_call(*callee_block_call))) + .items() + .map(|item| match item { + ExceptionTableItem::Tag(tag, block_call) => { + ExceptionTableItem::Tag(tag, self.map_block_call(block_call)) + } + ExceptionTableItem::Default(block_call) => { + ExceptionTableItem::Default(self.map_block_call(block_call)) + } + ExceptionTableItem::Context(value) => { + ExceptionTableItem::Context(self.map_value(value)) + } + }) .collect::>(); self.func .dfg @@ -1117,6 +1128,17 @@ impl EntityMap { ir::Block::from_u32(offset + callee_block.as_u32()) } + fn iter_inlined_blocks(&self, func: &ir::Function) -> impl Iterator + use<> { + let start = self.block_offset.expect( + "must create inlined `ir::Block`s before calling `EntityMap::iter_inlined_blocks`", + ); + + let end = func.dfg.blocks.len(); + let end = u32::try_from(end).unwrap(); + + (start..end).map(|i| ir::Block::from_u32(i)) + } + fn inlined_global_value(&self, callee_global_value: ir::GlobalValue) -> ir::GlobalValue { let offset = self .global_value_offset diff --git a/cranelift/codegen/src/ir/dfg.rs b/cranelift/codegen/src/ir/dfg.rs index 63756fb412..3dd2ad4211 100644 --- a/cranelift/codegen/src/ir/dfg.rs +++ b/cranelift/codegen/src/ir/dfg.rs @@ -333,6 +333,16 @@ impl<'a> Iterator for Values<'a> { .find(|kv| valid_valuedata(*kv.1)) .map(|kv| kv.0) } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl ExactSizeIterator for Values<'_> { + fn len(&self) -> usize { + self.inner.len() + } } /// Handling values. @@ -888,16 +898,25 @@ impl DataFlowGraph { &'dfg self, inst: Inst, ) -> impl DoubleEndedIterator + 'dfg { - self.inst_args(inst).iter().copied().chain( - self.insts[inst] - .branch_destination(&self.jump_tables, &self.exception_tables) - .into_iter() - .flat_map(|branch| { - branch - .args(&self.value_lists) - .filter_map(|arg| arg.as_value()) - }), - ) + self.inst_args(inst) + .iter() + .copied() + .chain( + self.insts[inst] + .branch_destination(&self.jump_tables, &self.exception_tables) + .into_iter() + .flat_map(|branch| { + branch + .args(&self.value_lists) + .filter_map(|arg| arg.as_value()) + }), + ) + .chain( + self.insts[inst] + .exception_table() + .into_iter() + .flat_map(|et| self.exception_tables[et].contexts()), + ) } /// Map a function over the values of the instruction. diff --git a/cranelift/codegen/src/ir/exception_table.rs b/cranelift/codegen/src/ir/exception_table.rs index 1916a6e36c..4130f23792 100644 --- a/cranelift/codegen/src/ir/exception_table.rs +++ b/cranelift/codegen/src/ir/exception_table.rs @@ -16,35 +16,51 @@ //! small, the exception table also contains the signature ID of the //! called function. -use crate::ir::BlockCall; use crate::ir::entities::{ExceptionTag, SigRef}; use crate::ir::instructions::ValueListPool; +use crate::ir::{BlockCall, Value}; use alloc::vec::Vec; use core::fmt::{self, Display, Formatter}; -use cranelift_entity::packed_option::PackedOption; #[cfg(feature = "enable-serde")] use serde_derive::{Deserialize, Serialize}; /// Contents of an exception table. /// -/// The "no exception" target for is stored as the last element of the -/// underlying vector. It can be accessed through the `normal_return` -/// and `normal_return_mut` functions. Exceptional catch clauses may -/// be iterated using the `catches` and `catches_mut` functions. All -/// targets may be iterated over using the `all_targets` and -/// `all_targets_mut` functions. +/// An exception table consists of a "no exception" ("normal") +/// destination block-call, and a series of exceptional destination +/// block-calls associated with tags. +/// +/// The exceptional tags can also be interspersed with "dynamic +/// context" entries, which result in a particular value being stored +/// in the stack frame and accessible at an offset given in the +/// compiled exception-table metadata. This is needed for some kinds +/// of tag-matching where different dynamic instances of tags may +/// exist (e.g., in the WebAssembly exception-handling proposal). +/// +/// The sequence of targets is semantically a list of +/// context-or-tagged-blockcall; e.g., `[context v0, tag1: block1(v1, +/// v2), context v2, tag2: block2(), tag3: block3()]`. +/// +/// The "no exception" target can be accessed through the +/// `normal_return` and `normal_return_mut` functions. Exceptional +/// catch clauses may be iterated using the `catches` and +/// `catches_mut` functions. All targets may be iterated over using +/// the `all_targets` and `all_targets_mut` functions. #[derive(Debug, Clone, PartialEq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct ExceptionTableData { /// All BlockCalls packed together. This is necessary because the /// rest of the compiler expects to be able to grab a slice of /// branch targets for any branch instruction. The last BlockCall - /// is the normal-return destination, and the rest correspond to - /// the tags in `tags` below. Thus, we have the invariant that - /// `targets.len() == tags.len() + 1`. + /// is the normal-return destination, and the rest are referred to + /// by index by the `items` below. targets: Vec, - /// Tags corresponding to targets other than the first one. + /// Exception-table items. + /// + /// This internal representation for items is like + /// `ExceptionTableItem` except that it has indices that refer to + /// `targets` above. /// /// A tag value of `None` indicates a catch-all handler. The /// catch-all handler matches only if no other handler matches, @@ -53,13 +69,44 @@ pub struct ExceptionTableData { /// `tags[i]` corresponds to `targets[i]`. Note that there will be /// one more `targets` element than `tags` because the last /// element in `targets` is the normal-return path. - tags: Vec>, + items: Vec, /// The signature of the function whose invocation is associated /// with this handler table. sig: SigRef, } +/// A single item in the match-list of an exception table. +#[derive(Clone, Debug)] +pub enum ExceptionTableItem { + /// A tag match, taking the specified block-call destination if + /// the tag matches the one in the thrown exception. (The match + /// predicate is up to the runtime; Cranelift only emits metadata + /// containing this tag.) + Tag(ExceptionTag, BlockCall), + /// A default match, always taking the specified block-call + /// destination. + Default(BlockCall), + /// A dynamic context update, applying to all tags until the next + /// update. (Cranelift does not interpret this context, but only + /// provides information to the runtime regarding where to find + /// it.) + Context(Value), +} + +/// Our internal representation of exception-table items. +/// +/// This is a version of `ExceptionTableItem` with block-calls +/// out-of-lined so that we can provide the slice externally. Each +/// block-call is referenced via an index. +#[derive(Clone, Debug, PartialEq, Hash)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +enum InternalExceptionTableItem { + Tag(ExceptionTag, u32), + Default(u32), + Context(Value), +} + impl ExceptionTableData { /// Create new exception-table data. /// @@ -87,17 +134,33 @@ impl ExceptionTableData { pub fn new( sig: SigRef, normal_return: BlockCall, - tags_and_targets: impl IntoIterator, BlockCall)>, + matches: impl IntoIterator, ) -> Self { let mut targets = vec![]; - let mut tags = vec![]; - for (tag, target) in tags_and_targets { - tags.push(tag.into()); - targets.push(target); + let mut items = vec![]; + for item in matches { + let target_idx = u32::try_from(targets.len()).unwrap(); + match item { + ExceptionTableItem::Tag(tag, target) => { + items.push(InternalExceptionTableItem::Tag(tag, target_idx)); + targets.push(target); + } + ExceptionTableItem::Default(target) => { + items.push(InternalExceptionTableItem::Default(target_idx)); + targets.push(target); + } + ExceptionTableItem::Context(ctx) => { + items.push(InternalExceptionTableItem::Context(ctx)); + } + } } targets.push(normal_return); - ExceptionTableData { targets, tags, sig } + ExceptionTableData { + targets, + items, + sig, + } } /// Return a value that can display the contents of this exception @@ -110,7 +173,7 @@ impl ExceptionTableData { pub fn deep_clone(&self, pool: &mut ValueListPool) -> Self { Self { targets: self.targets.iter().map(|b| b.deep_clone(pool)).collect(), - tags: self.tags.clone(), + items: self.items.clone(), sig: self.sig, } } @@ -125,40 +188,19 @@ impl ExceptionTableData { self.targets.last_mut().unwrap() } - /// Get the targets for exceptional return cases, together with - /// their tags. - pub fn catches(&self) -> impl Iterator, &BlockCall)> + '_ { - self.tags - .iter() - .map(|tag| tag.expand()) - // Skips the last entry of `targets` (the normal return) - // because `tags` is one element shorter. - .zip(self.targets.iter()) - } - - /// Get the targets for exceptional return cases, together with - /// their tags. - pub fn catches_mut( - &mut self, - ) -> impl Iterator, &mut BlockCall)> + '_ { - self.tags - .iter() - .map(|tag| tag.expand()) - // Skips the last entry of `targets` (the normal return) - // because `tags` is one element shorter. - .zip(self.targets.iter_mut()) - } - - /// The number of catch edges in this exception table. - pub fn len_catches(&self) -> usize { - self.tags.len() - } - - /// Get the `index`th catch edge from this table. - pub fn get_catch(&self, index: usize) -> Option<(Option, &BlockCall)> { - let tag = self.tags.get(index)?.expand(); - let target = &self.targets[index]; - Some((tag, target)) + /// Get the exception-catch items: dynamic context updates for + /// interpreting tags, tag-associated targets, and catch-all + /// targets. + pub fn items(&self) -> impl Iterator + '_ { + self.items.iter().map(|item| match item { + InternalExceptionTableItem::Tag(tag, target_idx) => { + ExceptionTableItem::Tag(*tag, self.targets[usize::try_from(*target_idx).unwrap()]) + } + InternalExceptionTableItem::Default(target_idx) => { + ExceptionTableItem::Default(self.targets[usize::try_from(*target_idx).unwrap()]) + } + InternalExceptionTableItem::Context(ctx) => ExceptionTableItem::Context(*ctx), + }) } /// Get all branch targets. @@ -182,29 +224,27 @@ impl ExceptionTableData { &mut self.sig } + /// Get an iterator over context values. + pub(crate) fn contexts(&self) -> impl DoubleEndedIterator { + self.items.iter().filter_map(|item| match item { + InternalExceptionTableItem::Context(ctx) => Some(*ctx), + _ => None, + }) + } + + /// Get a mutable iterator over context values. + pub(crate) fn contexts_mut(&mut self) -> impl DoubleEndedIterator { + self.items.iter_mut().filter_map(|item| match item { + InternalExceptionTableItem::Context(ctx) => Some(ctx), + _ => None, + }) + } + /// Clears all entries in this exception table, but leaves the function signature. pub fn clear(&mut self) { - self.tags.clear(); + self.items.clear(); self.targets.clear(); } - - /// Push a catch target onto this exception table. - /// - /// # Panics - /// - /// Panics if this exception table has been cleared. - pub fn push_catch(&mut self, tag: Option, block_call: BlockCall) { - assert_eq!( - self.tags.len() + 1, - self.targets.len(), - "cannot push onto an exception table that has been cleared" - ); - - self.tags.push(tag.into()); - - let target_index = self.targets.len() - 1; - self.targets.insert(target_index, block_call); - } } /// A wrapper for the context required to display a @@ -223,17 +263,23 @@ impl<'a> Display for DisplayExceptionTable<'a> { self.table.normal_return().display(self.pool) )?; let mut first = true; - for (tag, block_call) in self.table.catches() { + for item in self.table.items() { if first { write!(fmt, " ")?; first = false; } else { write!(fmt, ", ")?; } - if let Some(tag) = tag { - write!(fmt, "{}: {}", tag, block_call.display(self.pool))?; - } else { - write!(fmt, "default: {}", block_call.display(self.pool))?; + match item { + ExceptionTableItem::Tag(tag, block_call) => { + write!(fmt, "{}: {}", tag, block_call.display(self.pool))?; + } + ExceptionTableItem::Default(block_call) => { + write!(fmt, "default: {}", block_call.display(self.pool))?; + } + ExceptionTableItem::Context(ctx) => { + write!(fmt, "context {ctx}")?; + } } } let space = if first { "" } else { " " }; diff --git a/cranelift/codegen/src/ir/instructions.rs b/cranelift/codegen/src/ir/instructions.rs index b36912d958..c10cc33750 100644 --- a/cranelift/codegen/src/ir/instructions.rs +++ b/cranelift/codegen/src/ir/instructions.rs @@ -478,13 +478,22 @@ impl InstructionData { exception_tables: &mut ir::ExceptionTables, mut f: impl FnMut(Value) -> Value, ) { + // Map all normal operator args. for arg in self.arguments_mut(pool) { *arg = f(*arg); } + // Map all BlockCall args. for block in self.branch_destination_mut(jump_tables, exception_tables) { block.update_args(pool, |arg| arg.map_value(|val| f(val))); } + + // Map all context items. + if let Some(et) = self.exception_table() { + for ctx in exception_tables[et].contexts_mut() { + *ctx = f(*ctx); + } + } } /// If this is a trapping instruction, get its trap code. Otherwise, return diff --git a/cranelift/codegen/src/ir/mod.rs b/cranelift/codegen/src/ir/mod.rs index 662513380e..4519d5d74e 100644 --- a/cranelift/codegen/src/ir/mod.rs +++ b/cranelift/codegen/src/ir/mod.rs @@ -43,7 +43,7 @@ pub use crate::ir::entities::{ GlobalValue, Immediate, Inst, JumpTable, MemoryType, SigRef, StackSlot, UserExternalNameRef, Value, }; -pub use crate::ir::exception_table::ExceptionTableData; +pub use crate::ir::exception_table::{ExceptionTableData, ExceptionTableItem}; pub use crate::ir::extfunc::{ AbiParam, ArgumentExtension, ArgumentPurpose, ExtFuncData, Signature, }; diff --git a/cranelift/codegen/src/isa/aarch64/abi.rs b/cranelift/codegen/src/isa/aarch64/abi.rs index be97d2e30f..ebfcd50f53 100644 --- a/cranelift/codegen/src/isa/aarch64/abi.rs +++ b/cranelift/codegen/src/isa/aarch64/abi.rs @@ -1158,6 +1158,7 @@ impl ABIMachineSpec for AArch64MachineDeps { // Return FrameLayout structure. FrameLayout { + word_bytes: 8, incoming_args_size, tail_args_size, setup_area_size, diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs index 7774a55bac..f09637bef6 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs @@ -2950,9 +2950,9 @@ impl MachInstEmit for Inst { } if let Some(try_call) = info.try_call_info.as_ref() { - sink.add_call_site(&try_call.exception_dests); + sink.add_try_call_site(try_call.exception_handlers(&state.frame_layout)); } else { - sink.add_call_site(&[]); + sink.add_call_site(); } if info.callee_pop_size > 0 { @@ -2994,9 +2994,9 @@ impl MachInstEmit for Inst { } if let Some(try_call) = info.try_call_info.as_ref() { - sink.add_call_site(&try_call.exception_dests); + sink.add_try_call_site(try_call.exception_handlers(&state.frame_layout)); } else { - sink.add_call_site(&[]); + sink.add_call_site(); } if info.callee_pop_size > 0 { @@ -3035,7 +3035,7 @@ impl MachInstEmit for Inst { // for the target, but rather a function relocation. sink.add_reloc(Reloc::Arm64Call, &info.dest, 0); sink.put4(enc_jump26(0b000101, 0)); - sink.add_call_site(&[]); + sink.add_call_site(); // `emit_return_call_common_sequence` emits an island if // necessary, so we can safely disable the worst-case-size check @@ -3050,7 +3050,7 @@ impl MachInstEmit for Inst { targets: vec![], } .emit(sink, emit_info, state); - sink.add_call_site(&[]); + sink.add_call_site(); // `emit_return_call_common_sequence` emits an island if // necessary, so we can safely disable the worst-case-size check diff --git a/cranelift/codegen/src/isa/aarch64/inst/mod.rs b/cranelift/codegen/src/isa/aarch64/inst/mod.rs index 18dc0b81cf..4e7ef989c7 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs @@ -813,6 +813,9 @@ fn aarch64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { } } collector.reg_clobbers(info.clobbers); + if let Some(try_call_info) = &mut info.try_call_info { + try_call_info.collect_operands(collector); + } } Inst::CallInd { info, .. } => { let CallInfo { @@ -829,6 +832,9 @@ fn aarch64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { } } collector.reg_clobbers(info.clobbers); + if let Some(try_call_info) = &mut info.try_call_info { + try_call_info.collect_operands(collector); + } } Inst::ReturnCall { info } => { for CallArgPair { vreg, preg } in &mut info.uses { @@ -1181,13 +1187,11 @@ fn mem_finalize_for_show(mem: &AMode, access_ty: Type, state: &EmitState) -> (St } fn pretty_print_try_call(info: &TryCallInfo) -> String { - let dests = info - .exception_dests - .iter() - .map(|(tag, label)| format!("{tag:?}: {label:?}")) - .collect::>() - .join(", "); - format!("; b {:?}; catch [{dests}]", info.continuation) + format!( + "; b {:?}; catch [{}]", + info.continuation, + info.pretty_print_dests() + ) } impl Inst { diff --git a/cranelift/codegen/src/isa/pulley_shared/abi.rs b/cranelift/codegen/src/isa/pulley_shared/abi.rs index 86ca847221..9836307c9a 100644 --- a/cranelift/codegen/src/isa/pulley_shared/abi.rs +++ b/cranelift/codegen/src/isa/pulley_shared/abi.rs @@ -528,6 +528,7 @@ where }; FrameLayout { + word_bytes: u32::from(P::pointer_width().bytes()), incoming_args_size, tail_args_size, setup_area_size: setup_area_size.into(), diff --git a/cranelift/codegen/src/isa/pulley_shared/inst/emit.rs b/cranelift/codegen/src/isa/pulley_shared/inst/emit.rs index 0660e8a081..f8ed84657b 100644 --- a/cranelift/codegen/src/isa/pulley_shared/inst/emit.rs +++ b/cranelift/codegen/src/isa/pulley_shared/inst/emit.rs @@ -184,9 +184,9 @@ fn pulley_emit

( } if let Some(try_call) = info.try_call_info.as_ref() { - sink.add_call_site(&try_call.exception_dests); + sink.add_try_call_site(try_call.exception_handlers(&state.frame_layout)); } else { - sink.add_call_site(&[]); + sink.add_call_site(); } let adjust = -i32::try_from(info.callee_pop_size).unwrap(); @@ -224,9 +224,9 @@ fn pulley_emit

( } if let Some(try_call) = info.try_call_info.as_ref() { - sink.add_call_site(&try_call.exception_dests); + sink.add_try_call_site(try_call.exception_handlers(&state.frame_layout)); } else { - sink.add_call_site(&[]); + sink.add_call_site(); } let adjust = -i32::try_from(info.callee_pop_size).unwrap(); @@ -288,7 +288,12 @@ fn pulley_emit

( let offset = sink.cur_offset(); sink.push_user_stack_map(state, offset, s); } - sink.add_call_site(&[]); + + if let Some(try_call) = info.try_call_info.as_ref() { + sink.add_try_call_site(try_call.exception_handlers(&state.frame_layout)); + } else { + sink.add_call_site(); + } // If a callee pop is happening here that means that something has // messed up, these are expected to be "very simple" signatures. diff --git a/cranelift/codegen/src/isa/pulley_shared/inst/mod.rs b/cranelift/codegen/src/isa/pulley_shared/inst/mod.rs index 8d874b02fa..58c4c24cc2 100644 --- a/cranelift/codegen/src/isa/pulley_shared/inst/mod.rs +++ b/cranelift/codegen/src/isa/pulley_shared/inst/mod.rs @@ -10,7 +10,6 @@ use crate::isa::pulley_shared::abi::PulleyMachineDeps; use crate::{CodegenError, CodegenResult, settings}; use crate::{machinst::*, trace}; use alloc::string::{String, ToString}; -use alloc::vec::Vec; use regalloc2::RegClass; use smallvec::SmallVec; @@ -155,7 +154,12 @@ fn pulley_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { Inst::Call { info } => { let CallInfo { - uses, defs, dest, .. + uses, + defs, + dest, + try_call_info, + clobbers, + .. } = &mut **info; // Pulley supports having the first few integer arguments in any @@ -176,10 +180,19 @@ fn pulley_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { RetLocation::Stack(..) => collector.any_def(vreg), } } - collector.reg_clobbers(info.clobbers); + collector.reg_clobbers(*clobbers); + if let Some(try_call_info) = try_call_info { + try_call_info.collect_operands(collector); + } } Inst::IndirectCallHost { info } => { - let CallInfo { uses, defs, .. } = &mut **info; + let CallInfo { + uses, + defs, + try_call_info, + clobbers, + .. + } = &mut **info; for CallArgPair { vreg, preg } in uses { collector.reg_fixed_use(vreg, *preg); } @@ -189,11 +202,20 @@ fn pulley_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { RetLocation::Stack(..) => collector.any_def(vreg), } } - collector.reg_clobbers(info.clobbers); + collector.reg_clobbers(*clobbers); + if let Some(try_call_info) = try_call_info { + try_call_info.collect_operands(collector); + } } Inst::IndirectCall { info } => { collector.reg_use(&mut info.dest); - let CallInfo { uses, defs, .. } = &mut **info; + let CallInfo { + uses, + defs, + try_call_info, + clobbers, + .. + } = &mut **info; for CallArgPair { vreg, preg } in uses { collector.reg_fixed_use(vreg, *preg); } @@ -203,7 +225,10 @@ fn pulley_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { RetLocation::Stack(..) => collector.any_def(vreg), } } - collector.reg_clobbers(info.clobbers); + collector.reg_clobbers(*clobbers); + if let Some(try_call_info) = try_call_info { + try_call_info.collect_operands(collector); + } } Inst::ReturnCall { info } => { for CallArgPair { vreg, preg } in &mut info.uses { @@ -458,6 +483,9 @@ where Inst::ReturnCall { .. } | Inst::ReturnIndirectCall { .. } => MachTerminator::RetCall, Inst::Call { info } if info.try_call_info.is_some() => MachTerminator::Branch, Inst::IndirectCall { info } if info.try_call_info.is_some() => MachTerminator::Branch, + Inst::IndirectCallHost { info } if info.try_call_info.is_some() => { + MachTerminator::Branch + } _ => MachTerminator::None, } } @@ -588,13 +616,11 @@ pub fn reg_name(reg: Reg) -> String { } fn pretty_print_try_call(info: &TryCallInfo) -> String { - let dests = info - .exception_dests - .iter() - .map(|(tag, label)| format!("{tag:?}: {label:?}")) - .collect::>() - .join(", "); - format!("; jump {:?}; catch [{dests}]", info.continuation) + format!( + "; jump {:?}; catch [{}]", + info.continuation, + info.pretty_print_dests() + ) } impl Inst { @@ -677,7 +703,12 @@ impl Inst { } Inst::IndirectCallHost { info } => { - format!("indirect_call_host {info:?}") + let try_call = info + .try_call_info + .as_ref() + .map(|tci| pretty_print_try_call(tci)) + .unwrap_or_default(); + format!("indirect_call_host {info:?}{try_call}") } Inst::Jump { label } => format!("jump {}", label.to_string()), diff --git a/cranelift/codegen/src/isa/riscv64/abi.rs b/cranelift/codegen/src/isa/riscv64/abi.rs index 5caa4a9a6b..98823d5715 100644 --- a/cranelift/codegen/src/isa/riscv64/abi.rs +++ b/cranelift/codegen/src/isa/riscv64/abi.rs @@ -662,6 +662,7 @@ impl ABIMachineSpec for Riscv64MachineDeps { // Return FrameLayout structure. FrameLayout { + word_bytes: 8, incoming_args_size, tail_args_size, setup_area_size, diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit.rs b/cranelift/codegen/src/isa/riscv64/inst/emit.rs index 2273d6dab9..099a44a8bf 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/emit.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/emit.rs @@ -1201,9 +1201,9 @@ impl Inst { } if let Some(try_call) = info.try_call_info.as_ref() { - sink.add_call_site(&try_call.exception_dests); + sink.add_try_call_site(try_call.exception_handlers(&state.frame_layout)); } else { - sink.add_call_site(&[]); + sink.add_call_site(); } let callee_pop_size = i32::try_from(info.callee_pop_size).unwrap(); @@ -1245,9 +1245,9 @@ impl Inst { } if let Some(try_call) = info.try_call_info.as_ref() { - sink.add_call_site(&try_call.exception_dests); + sink.add_try_call_site(try_call.exception_handlers(&state.frame_layout)); } else { - sink.add_call_site(&[]); + sink.add_call_site(); } let callee_pop_size = i32::try_from(info.callee_pop_size).unwrap(); @@ -1279,7 +1279,7 @@ impl Inst { &Inst::ReturnCall { ref info } => { emit_return_call_common_sequence(sink, emit_info, state, info); - sink.add_call_site(&[]); + sink.add_call_site(); sink.add_reloc(Reloc::RiscvCallPlt, &info.dest, 0); Inst::construct_auipc_and_jalr(None, writable_spilltmp_reg(), 0) .into_iter() diff --git a/cranelift/codegen/src/isa/riscv64/inst/mod.rs b/cranelift/codegen/src/isa/riscv64/inst/mod.rs index f6687a2127..6b74ed2af7 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/mod.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/mod.rs @@ -342,6 +342,9 @@ fn riscv64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { } } collector.reg_clobbers(info.clobbers); + if let Some(try_call_info) = &mut info.try_call_info { + try_call_info.collect_operands(collector); + } } Inst::CallInd { info } => { let CallInfo { @@ -358,6 +361,9 @@ fn riscv64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { } } collector.reg_clobbers(info.clobbers); + if let Some(try_call_info) = &mut info.try_call_info { + try_call_info.collect_operands(collector); + } } Inst::ReturnCall { info } => { for CallArgPair { vreg, preg } in &mut info.uses { @@ -879,13 +885,11 @@ pub fn reg_name(reg: Reg) -> String { } fn pretty_print_try_call(info: &TryCallInfo) -> String { - let dests = info - .exception_dests - .iter() - .map(|(tag, label)| format!("{tag:?}: {label:?}")) - .collect::>() - .join(", "); - format!("; j {:?}; catch [{dests}]", info.continuation) + format!( + "; j {:?}; catch [{}]", + info.continuation, + info.pretty_print_dests() + ) } impl Inst { diff --git a/cranelift/codegen/src/isa/s390x/abi.rs b/cranelift/codegen/src/isa/s390x/abi.rs index 019c9818d5..d34a485237 100644 --- a/cranelift/codegen/src/isa/s390x/abi.rs +++ b/cranelift/codegen/src/isa/s390x/abi.rs @@ -964,6 +964,7 @@ impl ABIMachineSpec for S390xMachineDeps { // Return FrameLayout structure. FrameLayout { + word_bytes: 8, incoming_args_size, // We already accounted for tail-call arguments above, so reset // this value to its default. diff --git a/cranelift/codegen/src/isa/s390x/inst/emit.rs b/cranelift/codegen/src/isa/s390x/inst/emit.rs index d817613443..0d614a1090 100644 --- a/cranelift/codegen/src/isa/s390x/inst/emit.rs +++ b/cranelift/codegen/src/isa/s390x/inst/emit.rs @@ -3317,9 +3317,9 @@ impl Inst { put(sink, enc); if let Some(try_call) = info.try_call_info.as_ref() { - sink.add_call_site(&try_call.exception_dests); + sink.add_try_call_site(try_call.exception_handlers(&state.frame_layout)); } else { - sink.add_call_site(&[]); + sink.add_call_site(); } state.nominal_sp_offset -= info.callee_pop_size; @@ -3363,7 +3363,7 @@ impl Inst { } }; put(sink, enc); - sink.add_call_site(&[]); + sink.add_call_site(); } &Inst::ElfTlsGetOffset { ref symbol, .. } => { let opcode = 0xc05; // BRASL @@ -3380,7 +3380,7 @@ impl Inst { } put(sink, &enc_ril_b(opcode, gpr(14), 0)); - sink.add_call_site(&[]); + sink.add_call_site(); } &Inst::Args { .. } => {} &Inst::Rets { .. } => {} diff --git a/cranelift/codegen/src/isa/s390x/inst/mod.rs b/cranelift/codegen/src/isa/s390x/inst/mod.rs index dd4681aae5..31fb0aed91 100644 --- a/cranelift/codegen/src/isa/s390x/inst/mod.rs +++ b/cranelift/codegen/src/isa/s390x/inst/mod.rs @@ -901,6 +901,7 @@ fn s390x_get_operands(inst: &mut Inst, collector: &mut DenyReuseVisitor { let ReturnCallInfo { dest, uses, .. } = &mut **info; @@ -3184,13 +3188,11 @@ impl Inst { } state.outgoing_sp_offset = 0; let try_call = if let Some(try_call_info) = &info.try_call_info { - let dests = try_call_info - .exception_dests - .iter() - .map(|(tag, label)| format!("{tag:?}: {label:?}")) - .collect::>() - .join(", "); - format!("; jg {:?}; catch [{dests}]", try_call_info.continuation) + format!( + "; jg {:?}; catch [{}]", + try_call_info.continuation, + try_call_info.pretty_print_dests() + ) } else { "".to_string() }; diff --git a/cranelift/codegen/src/isa/x64/abi.rs b/cranelift/codegen/src/isa/x64/abi.rs index 3f06ece87b..aae05c9709 100644 --- a/cranelift/codegen/src/isa/x64/abi.rs +++ b/cranelift/codegen/src/isa/x64/abi.rs @@ -938,6 +938,7 @@ impl ABIMachineSpec for X64ABIMachineSpec { // Return FrameLayout structure. FrameLayout { + word_bytes: 8, incoming_args_size, tail_args_size: align_to(tail_args_size, 16), setup_area_size, diff --git a/cranelift/codegen/src/isa/x64/encoding/evex.rs b/cranelift/codegen/src/isa/x64/encoding/evex.rs deleted file mode 100644 index 5a491c7d2d..0000000000 --- a/cranelift/codegen/src/isa/x64/encoding/evex.rs +++ /dev/null @@ -1,752 +0,0 @@ -//! Encodes EVEX instructions. These instructions are those added by the AVX-512 extensions. The -//! EVEX encoding requires a 4-byte prefix: -//! -//! Byte 0: 0x62 -//! ┌───┬───┬───┬───┬───┬───┬───┬───┐ -//! Byte 1: │ R │ X │ B │ R'│ 0 │ 0 │ m │ m │ -//! ├───┼───┼───┼───┼───┼───┼───┼───┤ -//! Byte 2: │ W │ v │ v │ v │ v │ 1 │ p │ p │ -//! ├───┼───┼───┼───┼───┼───┼───┼───┤ -//! Byte 3: │ z │ L'│ L │ b │ V'│ a │ a │ a │ -//! └───┴───┴───┴───┴───┴───┴───┴───┘ -//! -//! The prefix is then followed by the opcode byte, the ModR/M byte, and other optional suffixes -//! (e.g. SIB byte, displacements, immediates) based on the instruction (see section 2.6, Intel -//! Software Development Manual, volume 2A). - -use super::rex::{self, LegacyPrefixes, OpcodeMap}; -use crate::MachBuffer; -use crate::isa::x64::args::{Amode, Avx512TupleType}; -use crate::isa::x64::inst::Inst; -use core::ops::RangeInclusive; - -/// Constructs an EVEX-encoded instruction using a builder pattern. This approach makes it visually -/// easier to transform something the manual's syntax, `EVEX.256.66.0F38.W1 1F /r` to code: -/// `EvexInstruction::new().length(...).prefix(...).map(...).w(true).opcode(0x1F).reg(...).rm(...)`. -pub struct EvexInstruction { - bits: u32, - opcode: u8, - reg: Register, - rm: RegisterOrAmode, - tuple_type: Option, - imm: Option, -} - -/// Because some of the bit flags in the EVEX prefix are reversed and users of `EvexInstruction` may -/// choose to skip setting fields, here we set some sane defaults. Note that: -/// - the first byte is always `0x62` but you will notice it at the end of the default `bits` value -/// implemented--remember the little-endian order -/// - some bits are always set to certain values: bits 10-11 to 0, bit 18 to 1 -/// - the other bits set correspond to reversed bits: R, X, B, R' (byte 1), vvvv (byte 2), V' (byte -/// 3). -/// -/// See the `default_emission` test for what these defaults are equivalent to (e.g. using RAX, -/// unsetting the W bit, etc.) -impl Default for EvexInstruction { - fn default() -> Self { - Self { - bits: 0x08_7C_F0_62, - opcode: 0, - reg: Register::default(), - rm: RegisterOrAmode::Register(Register::default()), - tuple_type: None, - imm: None, - } - } -} - -#[expect( - non_upper_case_globals, - reason = "This makes it easier to match the bit range names to the manual's names" -)] -impl EvexInstruction { - /// Construct a default EVEX instruction. - pub fn new() -> Self { - Self::default() - } - - /// Set the length of the instruction . Note that there are sets of instructions (i.e. rounding, - /// memory broadcast) that modify the same underlying bits--at some point (TODO) we can add a - /// way to set those context bits and verify that both are not used (e.g. rounding AND length). - /// For now, this method is very convenient. - #[inline(always)] - pub fn length(mut self, length: EvexVectorLength) -> Self { - self.write(Self::LL, EvexContext::Other { length }.bits() as u32); - self - } - - /// Set the legacy prefix byte of the instruction: None | 66 | F0 | F2 | F3. EVEX instructions - /// pack these into the prefix, not as separate bytes. - #[inline(always)] - pub fn prefix(mut self, prefix: LegacyPrefixes) -> Self { - self.write(Self::pp, prefix.bits() as u32); - self - } - - /// Set the opcode map byte of the instruction: None | 0F | 0F38 | 0F3A. EVEX instructions pack - /// these into the prefix, not as separate bytes. - #[inline(always)] - pub fn map(mut self, map: OpcodeMap) -> Self { - self.write(Self::mm, map.bits() as u32); - self - } - - /// Set the W bit, typically used to indicate an instruction using 64 bits of an operand (e.g. - /// 64 bit lanes). EVEX packs this bit in the EVEX prefix; previous encodings used the REX - /// prefix. - #[inline(always)] - pub fn w(mut self, w: bool) -> Self { - self.write(Self::W, w as u32); - self - } - - /// Set the instruction opcode byte. - #[inline(always)] - pub fn opcode(mut self, opcode: u8) -> Self { - self.opcode = opcode; - self - } - - /// Set the "tuple type" which is used for 8-bit scaling when a memory - /// operand is used. - #[inline(always)] - pub fn tuple_type(mut self, tt: Avx512TupleType) -> Self { - self.tuple_type = Some(tt); - self - } - - /// Set the register to use for the `reg` bits; many instructions use this as the write operand. - /// Setting this affects both the ModRM byte (`reg` section) and the EVEX prefix (the extension - /// bits for register encodings > 8). - #[inline(always)] - pub fn reg(mut self, reg: impl Into) -> Self { - self.reg = reg.into(); - let r = !(self.reg.0 >> 3) & 1; - let r_ = !(self.reg.0 >> 4) & 1; - self.write(Self::R, r as u32); - self.write(Self::R_, r_ as u32); - self - } - - /// Set the mask to use. See section 2.6 in the Intel Software Developer's Manual, volume 2A for - /// more details. - #[inline(always)] - #[cfg_attr(not(test), expect(dead_code, reason = "here for future use"))] - pub fn mask(mut self, mask: EvexMasking) -> Self { - self.write(Self::aaa, mask.aaa_bits() as u32); - self.write(Self::z, mask.z_bit() as u32); - self - } - - /// Set the `vvvvv` register; some instructions allow using this as a second, non-destructive - /// source register in 3-operand instructions (e.g. 2 read, 1 write). - #[inline(always)] - pub fn vvvvv(mut self, reg: impl Into) -> Self { - let reg = reg.into(); - self.write(Self::vvvv, !(reg.0 as u32) & 0b1111); - self.write(Self::V_, !(reg.0 as u32 >> 4) & 0b1); - self - } - - /// Set the register to use for the `rm` bits; many instructions use this - /// as the "read from register/memory" operand. Setting this affects both - /// the ModRM byte (`rm` section) and the EVEX prefix (the extension bits - /// for register encodings > 8). - #[inline(always)] - pub fn rm(mut self, reg: impl Into) -> Self { - // NB: See Table 2-31. 32-Register Support in 64-bit Mode Using EVEX - // with Embedded REX Bits - self.rm = reg.into(); - let x = match &self.rm { - RegisterOrAmode::Register(r) => r.0 >> 4, - RegisterOrAmode::Amode(Amode::ImmRegRegShift { index, .. }) => { - index.to_real_reg().unwrap().hw_enc() >> 3 - } - - // These two modes technically don't use the X bit, so leave it at - // 0. - RegisterOrAmode::Amode(Amode::ImmReg { .. }) => 0, - RegisterOrAmode::Amode(Amode::RipRelative { .. }) => 0, - }; - // The X bit is stored in an inverted format, so invert it here. - self.write(Self::X, u32::from(!x & 1)); - - let b = match &self.rm { - RegisterOrAmode::Register(r) => r.0 >> 3, - RegisterOrAmode::Amode(Amode::ImmReg { base, .. }) => { - base.to_real_reg().unwrap().hw_enc() >> 3 - } - RegisterOrAmode::Amode(Amode::ImmRegRegShift { base, .. }) => { - base.to_real_reg().unwrap().hw_enc() >> 3 - } - // The 4th bit of %rip is 0 - RegisterOrAmode::Amode(Amode::RipRelative { .. }) => 0, - }; - // The B bit is stored in an inverted format, so invert it here. - self.write(Self::B, u32::from(!b & 1)); - self - } - - /// Set the imm byte. - #[inline(always)] - pub fn imm(mut self, imm: u8) -> Self { - self.imm = Some(imm); - self - } - - /// Emit the EVEX-encoded instruction to the code sink: - /// - /// - the 4-byte EVEX prefix; - /// - the opcode byte; - /// - the ModR/M byte - /// - SIB bytes, if necessary - /// - an optional immediate, if necessary (not currently implemented) - pub fn encode(&self, sink: &mut MachBuffer) { - if let RegisterOrAmode::Amode(amode) = &self.rm { - if let Some(trap_code) = amode.get_flags().trap_code() { - sink.add_trap(trap_code); - } - } - sink.put4(self.bits); - sink.put1(self.opcode); - - match &self.rm { - RegisterOrAmode::Register(reg) => { - let rm: u8 = (*reg).into(); - sink.put1(rex::encode_modrm(3, self.reg.0 & 7, rm & 7)); - } - RegisterOrAmode::Amode(amode) => { - let scaling = self.scaling_for_8bit_disp(); - - let bytes_at_end = if self.imm.is_some() { 1 } else { 0 }; - rex::emit_modrm_sib_disp(sink, self.reg.0 & 7, amode, bytes_at_end, Some(scaling)); - } - } - if let Some(imm) = self.imm { - sink.put1(imm); - } - } - - // In order to simplify the encoding of the various bit ranges in the prefix, we specify those - // ranges according to the table below (extracted from the Intel Software Development Manual, - // volume 2A). Remember that, because we pack the 4-byte prefix into a little-endian `u32`, this - // chart should be read from right-to-left, top-to-bottom. Note also that we start ranges at bit - // 8, leaving bits 0-7 for the mandatory `0x62`. - // ┌───┬───┬───┬───┬───┬───┬───┬───┐ - // Byte 1: │ R │ X │ B │ R'│ 0 │ 0 │ m │ m │ - // ├───┼───┼───┼───┼───┼───┼───┼───┤ - // Byte 2: │ W │ v │ v │ v │ v │ 1 │ p │ p │ - // ├───┼───┼───┼───┼───┼───┼───┼───┤ - // Byte 3: │ z │ L'│ L │ b │ V'│ a │ a │ a │ - // └───┴───┴───┴───┴───┴───┴───┴───┘ - - // Byte 1: - const mm: RangeInclusive = 8..=9; - const R_: RangeInclusive = 12..=12; - const B: RangeInclusive = 13..=13; - const X: RangeInclusive = 14..=14; - const R: RangeInclusive = 15..=15; - - // Byte 2: - const pp: RangeInclusive = 16..=17; - const vvvv: RangeInclusive = 19..=22; - const W: RangeInclusive = 23..=23; - - // Byte 3: - const aaa: RangeInclusive = 24..=26; - const V_: RangeInclusive = 27..=27; - const b: RangeInclusive = 28..=28; - const LL: RangeInclusive = 29..=30; - const z: RangeInclusive = 31..=31; - - // A convenience method for writing the `value` bits to the given range in `self.bits`. - #[inline] - fn write(&mut self, range: RangeInclusive, value: u32) { - assert!(ExactSizeIterator::len(&range) > 0); - let size = range.end() - range.start() + 1; // Calculate the number of bits in the range. - let mask: u32 = (1 << size) - 1; // Generate a bit mask. - debug_assert!( - value <= mask, - "The written value should have fewer than {size} bits." - ); - let mask_complement = !(mask << *range.start()); // Create the bitwise complement for the clear mask. - self.bits &= mask_complement; // Clear the bits in `range`; otherwise the OR below may allow previously-set bits to slip through. - let value = value << *range.start(); // Place the value in the correct location (assumes `value <= mask`). - self.bits |= value; // Modify the bits in `range`. - } - - /// A convenience method for reading given range of bits in `self.bits` - /// shifted to the LSB of the returned value.. - #[inline] - fn read(&self, range: RangeInclusive) -> u32 { - (self.bits >> range.start()) & ((1 << range.len()) - 1) - } - - fn scaling_for_8bit_disp(&self) -> i8 { - use Avx512TupleType::*; - - let vector_size_scaling = || match self.read(Self::LL) { - 0b00 => 16, - 0b01 => 32, - 0b10 => 64, - _ => unreachable!(), - }; - - match self.tuple_type { - Some(Full) => { - if self.read(Self::b) == 1 { - if self.read(Self::W) == 0 { 4 } else { 8 } - } else { - vector_size_scaling() - } - } - Some(FullMem) => vector_size_scaling(), - Some(Mem128) => 16, - None => panic!("tuple type was not set"), - } - } -} - -/// Describe the register index to use. This wrapper is a type-safe way to pass -/// around the registers defined in `inst/regs.rs`. -#[derive(Debug, Copy, Clone, Default)] -pub struct Register(u8); -impl From for Register { - fn from(reg: u8) -> Self { - debug_assert!(reg < 16); - Self(reg) - } -} -impl From for u8 { - fn from(reg: Register) -> u8 { - reg.0 - } -} - -#[derive(Debug, Clone)] -pub enum RegisterOrAmode { - Register(Register), - Amode(Amode), -} - -impl From for RegisterOrAmode { - fn from(reg: u8) -> Self { - RegisterOrAmode::Register(reg.into()) - } -} - -impl From for RegisterOrAmode { - fn from(amode: Amode) -> Self { - RegisterOrAmode::Amode(amode) - } -} - -/// Defines the EVEX context for the `L'`, `L`, and `b` bits (bits 6:4 of EVEX P2 byte). Table 2-36 in -/// section 2.6.10 (Intel Software Development Manual, volume 2A) describes how these bits can be -/// used together for certain classes of instructions; i.e., special care should be taken to ensure -/// that instructions use an applicable correct `EvexContext`. Table 2-39 contains cases where -/// opcodes can result in an #UD. -pub enum EvexContext { - #[expect(dead_code, reason = "here for future use")] - RoundingRegToRegFP { - rc: EvexRoundingControl, - }, - #[expect(dead_code, reason = "here for future use")] - NoRoundingFP { - sae: bool, - length: EvexVectorLength, - }, - #[expect(dead_code, reason = "here for future use")] - MemoryOp { - broadcast: bool, - length: EvexVectorLength, - }, - Other { - length: EvexVectorLength, - }, -} - -impl Default for EvexContext { - fn default() -> Self { - Self::Other { - length: EvexVectorLength::default(), - } - } -} - -impl EvexContext { - /// Encode the `L'`, `L`, and `b` bits (bits 6:4 of EVEX P2 byte) for merging with the P2 byte. - pub fn bits(&self) -> u8 { - match self { - Self::RoundingRegToRegFP { rc } => 0b001 | rc.bits() << 1, - Self::NoRoundingFP { sae, length } => (*sae as u8) | length.bits() << 1, - Self::MemoryOp { broadcast, length } => (*broadcast as u8) | length.bits() << 1, - Self::Other { length } => length.bits() << 1, - } - } -} - -/// The EVEX format allows choosing a vector length in the `L'` and `L` bits; see `EvexContext`. -pub enum EvexVectorLength { - V128, - #[expect(dead_code, reason = "here for future cranelift use")] - V256, - #[expect(dead_code, reason = "here for future cranelift use")] - V512, -} - -impl EvexVectorLength { - /// Encode the `L'` and `L` bits for merging with the P2 byte. - fn bits(&self) -> u8 { - match self { - Self::V128 => 0b00, - Self::V256 => 0b01, - Self::V512 => 0b10, - // 0b11 is reserved (#UD). - } - } -} - -impl Default for EvexVectorLength { - fn default() -> Self { - Self::V128 - } -} - -/// The EVEX format allows defining rounding control in the `L'` and `L` bits; see `EvexContext`. -#[expect(dead_code, reason = "here for future use")] -pub enum EvexRoundingControl { - RNE, - RD, - RU, - RZ, -} - -impl EvexRoundingControl { - /// Encode the `L'` and `L` bits for merging with the P2 byte. - fn bits(&self) -> u8 { - match self { - Self::RNE => 0b00, - Self::RD => 0b01, - Self::RU => 0b10, - Self::RZ => 0b11, - } - } -} - -/// Defines the EVEX masking behavior; masking support is described in section 2.6.4 of the Intel -/// Software Development Manual, volume 2A. -pub enum EvexMasking { - None, - #[expect(dead_code, reason = "here for future use")] - Merging { - k: u8, - }, - #[expect(dead_code, reason = "here for future use")] - Zeroing { - k: u8, - }, -} - -impl Default for EvexMasking { - fn default() -> Self { - EvexMasking::None - } -} - -impl EvexMasking { - /// Encode the `z` bit for merging with the P2 byte. - pub fn z_bit(&self) -> u8 { - match self { - Self::None | Self::Merging { .. } => 0, - Self::Zeroing { .. } => 1, - } - } - - /// Encode the `aaa` bits for merging with the P2 byte. - pub fn aaa_bits(&self) -> u8 { - match self { - Self::None => 0b000, - Self::Merging { k } | Self::Zeroing { k } => { - debug_assert!(*k <= 7); - *k - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::ir::MemFlags; - use crate::isa::x64::args::Gpr; - use crate::isa::x64::inst::regs; - use std::vec::Vec; - - // As a sanity test, we verify that the output of `xed-asmparse-main 'vpabsq xmm0{k0}, - // xmm1'` matches this EVEX encoding machinery. - #[test] - fn vpabsq() { - let mut tmp = MachBuffer::::new(); - let tests: &[(crate::Reg, RegisterOrAmode, Vec)] = &[ - // vpabsq %xmm1, %xmm0 - ( - regs::xmm0(), - regs::xmm1().to_real_reg().unwrap().hw_enc().into(), - vec![0x62, 0xf2, 0xfd, 0x08, 0x1f, 0xc1], - ), - // vpabsq %xmm8, %xmm10 - ( - regs::xmm10(), - regs::xmm8().to_real_reg().unwrap().hw_enc().into(), - vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0xd0], - ), - // vpabsq %xmm15, %xmm3 - ( - regs::xmm3(), - regs::xmm15().to_real_reg().unwrap().hw_enc().into(), - vec![0x62, 0xd2, 0xfd, 0x08, 0x1f, 0xdf], - ), - // vpabsq (%rsi), %xmm12 - ( - regs::xmm12(), - Amode::ImmReg { - simm32: 0, - base: regs::rsi(), - flags: MemFlags::trusted(), - } - .into(), - vec![0x62, 0x72, 0xfd, 0x08, 0x1f, 0x26], - ), - // vpabsq 8(%r15), %xmm14 - ( - regs::xmm14(), - Amode::ImmReg { - simm32: 8, - base: regs::r15(), - flags: MemFlags::trusted(), - } - .into(), - vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0xb7, 0x08, 0x00, 0x00, 0x00], - ), - // vpabsq 16(%r15), %xmm14 - ( - regs::xmm14(), - Amode::ImmReg { - simm32: 16, - base: regs::r15(), - flags: MemFlags::trusted(), - } - .into(), - vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0x77, 0x01], - ), - // vpabsq 17(%rax), %xmm3 - ( - regs::xmm3(), - Amode::ImmReg { - simm32: 17, - base: regs::rax(), - flags: MemFlags::trusted(), - } - .into(), - vec![0x62, 0xf2, 0xfd, 0x08, 0x1f, 0x98, 0x11, 0x00, 0x00, 0x00], - ), - // vpabsq (%rbx, %rsi, 8), %xmm9 - ( - regs::xmm9(), - Amode::ImmRegRegShift { - simm32: 0, - base: Gpr::unwrap_new(regs::rbx()), - index: Gpr::unwrap_new(regs::rsi()), - shift: 3, - flags: MemFlags::trusted(), - } - .into(), - vec![0x62, 0x72, 0xfd, 0x08, 0x1f, 0x0c, 0xf3], - ), - // vpabsq 1(%r11, %rdi, 4), %xmm13 - ( - regs::xmm13(), - Amode::ImmRegRegShift { - simm32: 1, - base: Gpr::unwrap_new(regs::r11()), - index: Gpr::unwrap_new(regs::rdi()), - shift: 2, - flags: MemFlags::trusted(), - } - .into(), - vec![ - 0x62, 0x52, 0xfd, 0x08, 0x1f, 0xac, 0xbb, 0x01, 0x00, 0x00, 0x00, - ], - ), - // vpabsq 128(%rsp, %r10, 2), %xmm5 - ( - regs::xmm5(), - Amode::ImmRegRegShift { - simm32: 128, - base: Gpr::unwrap_new(regs::rsp()), - index: Gpr::unwrap_new(regs::r10()), - shift: 1, - flags: MemFlags::trusted(), - } - .into(), - vec![0x62, 0xb2, 0xfd, 0x08, 0x1f, 0x6c, 0x54, 0x08], - ), - // vpabsq 112(%rbp, %r13, 1), %xmm6 - ( - regs::xmm6(), - Amode::ImmRegRegShift { - simm32: 112, - base: Gpr::unwrap_new(regs::rbp()), - index: Gpr::unwrap_new(regs::r13()), - shift: 0, - flags: MemFlags::trusted(), - } - .into(), - vec![0x62, 0xb2, 0xfd, 0x08, 0x1f, 0x74, 0x2d, 0x07], - ), - // vpabsq (%rbp, %r13, 1), %xmm7 - ( - regs::xmm7(), - Amode::ImmRegRegShift { - simm32: 0, - base: Gpr::unwrap_new(regs::rbp()), - index: Gpr::unwrap_new(regs::r13()), - shift: 0, - flags: MemFlags::trusted(), - } - .into(), - vec![0x62, 0xb2, 0xfd, 0x08, 0x1f, 0x7c, 0x2d, 0x00], - ), - // vpabsq 2032(%r12), %xmm8 - ( - regs::xmm8(), - Amode::ImmReg { - simm32: 2032, - base: regs::r12(), - flags: MemFlags::trusted(), - } - .into(), - vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0x44, 0x24, 0x7f], - ), - // vpabsq 2048(%r13), %xmm9 - ( - regs::xmm9(), - Amode::ImmReg { - simm32: 2048, - base: regs::r13(), - flags: MemFlags::trusted(), - } - .into(), - vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0x8d, 0x00, 0x08, 0x00, 0x00], - ), - // vpabsq -16(%r14), %xmm10 - ( - regs::xmm10(), - Amode::ImmReg { - simm32: -16, - base: regs::r14(), - flags: MemFlags::trusted(), - } - .into(), - vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0x56, 0xff], - ), - // vpabsq -5(%r15), %xmm11 - ( - regs::xmm11(), - Amode::ImmReg { - simm32: -5, - base: regs::r15(), - flags: MemFlags::trusted(), - } - .into(), - vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0x9f, 0xfb, 0xff, 0xff, 0xff], - ), - // vpabsq -2048(%rdx), %xmm12 - ( - regs::xmm12(), - Amode::ImmReg { - simm32: -2048, - base: regs::rdx(), - flags: MemFlags::trusted(), - } - .into(), - vec![0x62, 0x72, 0xfd, 0x08, 0x1f, 0x62, 0x80], - ), - // vpabsq -2064(%rsi), %xmm13 - ( - regs::xmm13(), - Amode::ImmReg { - simm32: -2064, - base: regs::rsi(), - flags: MemFlags::trusted(), - } - .into(), - vec![0x62, 0x72, 0xfd, 0x08, 0x1f, 0xae, 0xf0, 0xf7, 0xff, 0xff], - ), - // a: vpabsq a(%rip), %xmm14 - ( - regs::xmm14(), - Amode::RipRelative { - target: tmp.get_label(), - } - .into(), - vec![0x62, 0x72, 0xfd, 0x08, 0x1f, 0x35, 0xf6, 0xff, 0xff, 0xff], - ), - ]; - - for (dst, src, encoding) in tests { - let mut sink = MachBuffer::new(); - let label = sink.get_label(); - sink.bind_label(label, &mut Default::default()); - EvexInstruction::new() - .prefix(LegacyPrefixes::_66) - .map(OpcodeMap::_0F38) - .w(true) - .opcode(0x1F) - .reg(dst.to_real_reg().unwrap().hw_enc()) - .rm(src.clone()) - .length(EvexVectorLength::V128) - .tuple_type(Avx512TupleType::Full) - .encode(&mut sink); - let bytes0 = sink - .finish(&Default::default(), &mut Default::default()) - .data; - assert_eq!( - bytes0.as_slice(), - encoding.as_slice(), - "dst={dst:?} src={src:?}" - ); - } - } - - /// Verify that the defaults are equivalent to an instruction with a `0x00` opcode using the - /// "0" register (i.e. `rax`), with sane defaults for the various configurable parameters. This - /// test is more interesting than it may appear because some of the parameters have flipped-bit - /// representations (e.g. `vvvvv`) so emitting 0s as a default will not work. - #[test] - fn default_emission() { - let mut sink = MachBuffer::new(); - EvexInstruction::new().encode(&mut sink); - let bytes0 = sink - .finish(&Default::default(), &mut Default::default()) - .data; - - let mut sink = MachBuffer::new(); - EvexInstruction::new() - .length(EvexVectorLength::V128) - .prefix(LegacyPrefixes::None) - .map(OpcodeMap::None) - .w(false) - .opcode(0x00) - .reg(regs::rax().to_real_reg().unwrap().hw_enc()) - .rm(regs::rax().to_real_reg().unwrap().hw_enc()) - .mask(EvexMasking::None) - .encode(&mut sink); - let bytes1 = sink - .finish(&Default::default(), &mut Default::default()) - .data; - - assert_eq!(bytes0, bytes1); - } -} diff --git a/cranelift/codegen/src/isa/x64/encoding/mod.rs b/cranelift/codegen/src/isa/x64/encoding/mod.rs deleted file mode 100644 index cbfcb2da07..0000000000 --- a/cranelift/codegen/src/isa/x64/encoding/mod.rs +++ /dev/null @@ -1,37 +0,0 @@ -//! Contains the encoding machinery for the various x64 instruction formats. -use crate::{isa::x64, machinst::MachBuffer}; -use std::vec::Vec; - -pub mod evex; -pub mod rex; - -/// The encoding formats in this module all require a way of placing bytes into -/// a buffer. -pub trait ByteSink { - /// Add 1 byte to the code section. - fn put1(&mut self, _: u8); - - /// Add 4 bytes to the code section. - fn put4(&mut self, _: u32); -} - -impl ByteSink for MachBuffer { - fn put1(&mut self, value: u8) { - self.put1(value) - } - - fn put4(&mut self, value: u32) { - self.put4(value) - } -} - -/// Provide a convenient implementation for testing. -impl ByteSink for Vec { - fn put1(&mut self, v: u8) { - self.extend_from_slice(&[v]) - } - - fn put4(&mut self, v: u32) { - self.extend_from_slice(&v.to_le_bytes()) - } -} diff --git a/cranelift/codegen/src/isa/x64/encoding/rex.rs b/cranelift/codegen/src/isa/x64/encoding/rex.rs deleted file mode 100644 index 04315204d4..0000000000 --- a/cranelift/codegen/src/isa/x64/encoding/rex.rs +++ /dev/null @@ -1,274 +0,0 @@ -//! Encodes instructions in the standard x86 encoding mode. This is called -//! IA-32E mode in the Intel manuals but corresponds to the addition of the -//! REX-prefix format (hence the name of this module) that allowed encoding -//! instructions in both compatibility mode (32-bit instructions running on a -//! 64-bit OS) and in 64-bit mode (using the full 64-bit address space). -//! -//! For all of the routines that take both a memory-or-reg operand (sometimes -//! called "E" in the Intel documentation, see the Intel Developer's manual, -//! vol. 2, section A.2) and a reg-only operand ("G" in Intel-ese), the order is -//! always G first, then E. The term "enc" in the following means "hardware -//! register encoding number". - -use super::ByteSink; -use crate::isa::x64::inst::args::Amode; -use crate::isa::x64::inst::{Inst, LabelUse, regs}; -use crate::machinst::{MachBuffer, Reg, RegClass}; - -pub(crate) fn low8_will_sign_extend_to_32(x: u32) -> bool { - let xs = x as i32; - xs == ((xs << 24) >> 24) -} - -/// Encode the ModR/M byte. -#[inline(always)] -pub fn encode_modrm(m0d: u8, enc_reg_g: u8, rm_e: u8) -> u8 { - debug_assert!(m0d < 4); - debug_assert!(enc_reg_g < 8); - debug_assert!(rm_e < 8); - ((m0d & 3) << 6) | ((enc_reg_g & 7) << 3) | (rm_e & 7) -} - -#[inline(always)] -pub(crate) fn encode_sib(shift: u8, enc_index: u8, enc_base: u8) -> u8 { - debug_assert!(shift < 4); - debug_assert!(enc_index < 8); - debug_assert!(enc_base < 8); - ((shift & 3) << 6) | ((enc_index & 7) << 3) | (enc_base & 7) -} - -/// Get the encoding number of a GPR. -#[inline(always)] -pub(crate) fn int_reg_enc(reg: impl Into) -> u8 { - let reg = reg.into(); - debug_assert!(reg.is_real(), "reg = {reg:?}"); - debug_assert_eq!(reg.class(), RegClass::Int); - reg.to_real_reg().unwrap().hw_enc() -} - -/// Allows using the same opcode byte in different "opcode maps" to allow for more instruction -/// encodings. See appendix A in the Intel Software Developer's Manual, volume 2A, for more details. -#[derive(PartialEq)] -pub enum OpcodeMap { - None, - _0F, - _0F38, - _0F3A, -} - -impl OpcodeMap { - /// Normally the opcode map is specified as bytes in the instruction, but some x64 encoding - /// formats pack this information as bits in a prefix (e.g. VEX / EVEX). - pub(crate) fn bits(&self) -> u8 { - match self { - OpcodeMap::None => 0b00, - OpcodeMap::_0F => 0b01, - OpcodeMap::_0F38 => 0b10, - OpcodeMap::_0F3A => 0b11, - } - } -} - -impl Default for OpcodeMap { - fn default() -> Self { - Self::None - } -} - -/// We may need to include one or more legacy prefix bytes before the REX prefix. This enum -/// covers only the small set of possibilities that we actually need. -#[derive(PartialEq)] -pub enum LegacyPrefixes { - /// No prefix bytes. - None, - /// Operand Size Override -- here, denoting "16-bit operation". - _66, - /// The Lock prefix. - _F0, - /// Operand size override and Lock. - _66F0, - /// REPNE, but no specific meaning here -- is just an opcode extension. - _F2, - /// REP/REPE, but no specific meaning here -- is just an opcode extension. - _F3, - /// Operand size override and same effect as F3. - _66F3, -} - -impl LegacyPrefixes { - /// Emit the legacy prefix as bits (e.g. for EVEX instructions). - #[inline(always)] - pub(crate) fn bits(&self) -> u8 { - match self { - Self::None => 0b00, - Self::_66 => 0b01, - Self::_F3 => 0b10, - Self::_F2 => 0b11, - _ => panic!( - "VEX and EVEX bits can only be extracted from single prefixes: None, 66, F3, F2" - ), - } - } -} - -impl Default for LegacyPrefixes { - fn default() -> Self { - Self::None - } -} - -pub(crate) fn emit_modrm_sib_disp( - sink: &mut MachBuffer, - enc_g: u8, - mem_e: &Amode, - bytes_at_end: u8, - evex_scaling: Option, -) { - match *mem_e { - Amode::ImmReg { simm32, base, .. } => { - let enc_e = int_reg_enc(base); - let mut imm = Imm::new(simm32, evex_scaling); - - // Most base registers allow for a single ModRM byte plus an - // optional immediate. If rsp is the base register, however, then a - // SIB byte must be used. - let enc_e_low3 = enc_e & 7; - if enc_e_low3 != regs::ENC_RSP { - // If the base register is rbp and there's no offset then force - // a 1-byte zero offset since otherwise the encoding would be - // invalid. - if enc_e_low3 == regs::ENC_RBP { - imm.force_immediate(); - } - sink.put1(encode_modrm(imm.m0d(), enc_g & 7, enc_e & 7)); - imm.emit(sink); - } else { - // Displacement from RSP is encoded with a SIB byte where - // the index and base are both encoded as RSP's encoding of - // 0b100. This special encoding means that the index register - // isn't used and the base is 0b100 with or without a - // REX-encoded 4th bit (e.g. rsp or r12) - sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100)); - sink.put1(0b00_100_100); - imm.emit(sink); - } - } - - Amode::ImmRegRegShift { - simm32, - base: reg_base, - index: reg_index, - shift, - .. - } => { - let enc_base = int_reg_enc(*reg_base); - let enc_index = int_reg_enc(*reg_index); - - // Encoding of ModRM/SIB bytes don't allow the index register to - // ever be rsp. Note, though, that the encoding of r12, whose three - // lower bits match the encoding of rsp, is explicitly allowed with - // REX bytes so only rsp is disallowed. - assert!(enc_index != regs::ENC_RSP); - - // If the offset is zero then there is no immediate. Note, though, - // that if the base register's lower three bits are `101` then an - // offset must be present. This is a special case in the encoding of - // the SIB byte and requires an explicit displacement with rbp/r13. - let mut imm = Imm::new(simm32, evex_scaling); - if enc_base & 7 == regs::ENC_RBP { - imm.force_immediate(); - } - - // With the above determined encode the ModRM byte, then the SIB - // byte, then any immediate as necessary. - sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100)); - sink.put1(encode_sib(shift, enc_index & 7, enc_base & 7)); - imm.emit(sink); - } - - Amode::RipRelative { ref target } => { - // RIP-relative is mod=00, rm=101. - sink.put1(encode_modrm(0b00, enc_g & 7, 0b101)); - - let offset = sink.cur_offset(); - sink.use_label_at_offset(offset, *target, LabelUse::JmpRel32); - // N.B.: some instructions (XmmRmRImm format for example) - // have bytes *after* the RIP-relative offset. The - // addressed location is relative to the end of the - // instruction, but the relocation is nominally relative - // to the end of the u32 field. So, to compensate for - // this, we emit a negative extra offset in the u32 field - // initially, and the relocation will add to it. - sink.put4(-(i32::from(bytes_at_end)) as u32); - } - } -} - -#[derive(Copy, Clone)] -enum Imm { - None, - Imm8(i8), - Imm32(i32), -} - -impl Imm { - /// Classifies the 32-bit immediate `val` as how this can be encoded - /// with ModRM/SIB bytes. - /// - /// For `evex_scaling` according to Section 2.7.5 of Intel's manual: - /// - /// > EVEX-encoded instructions always use a compressed displacement scheme - /// > by multiplying disp8 in conjunction with a scaling factor N that is - /// > determined based on the vector length, the value of EVEX.b bit - /// > (embedded broadcast) and the input element size of the instruction - /// - /// The `evex_scaling` factor provided here is `Some(N)` for EVEX - /// instructions. This is taken into account where the `Imm` value - /// contained is the raw byte offset. - fn new(val: i32, evex_scaling: Option) -> Imm { - if val == 0 { - return Imm::None; - } - match evex_scaling { - Some(scaling) => { - if val % i32::from(scaling) == 0 { - let scaled = val / i32::from(scaling); - if low8_will_sign_extend_to_32(scaled as u32) { - return Imm::Imm8(scaled as i8); - } - } - Imm::Imm32(val) - } - None => match i8::try_from(val) { - Ok(val) => Imm::Imm8(val), - Err(_) => Imm::Imm32(val), - }, - } - } - - /// Forces `Imm::None` to become `Imm::Imm8(0)`, used for special cases - /// where some base registers require an immediate. - fn force_immediate(&mut self) { - if let Imm::None = self { - *self = Imm::Imm8(0); - } - } - - /// Returns the two "mod" bits present at the upper bits of the mod/rm - /// byte. - fn m0d(&self) -> u8 { - match self { - Imm::None => 0b00, - Imm::Imm8(_) => 0b01, - Imm::Imm32(_) => 0b10, - } - } - - fn emit(&self, sink: &mut BS) { - match self { - Imm::None => {} - Imm::Imm8(n) => sink.put1(*n as u8), - Imm::Imm32(n) => sink.put4(*n as u32), - } - } -} diff --git a/cranelift/codegen/src/isa/x64/inst.isle b/cranelift/codegen/src/isa/x64/inst.isle index 9f424ff01a..84c65b9a93 100644 --- a/cranelift/codegen/src/isa/x64/inst.isle +++ b/cranelift/codegen/src/isa/x64/inst.isle @@ -60,32 +60,6 @@ ;; ========================================= ;; Floating-point operations. - ;; XMM (scalar or vector) binary op that relies on the EVEX - ;; prefix. Takes two inputs. - (XmmRmREvex (op Avx512Opcode) - (src1 Xmm) - (src2 XmmMem) - (dst WritableXmm)) - - ;; Same as `XmmRmREvex` but for unary operations. - (XmmUnaryRmRImmEvex (op Avx512Opcode) - (src XmmMem) - (dst WritableXmm) - (imm u8)) - - ;; XMM (scalar or vector) binary op that relies on the EVEX - ;; prefix. Takes three inputs. - (XmmRmREvex3 (op Avx512Opcode) - (src1 Xmm) - (src2 Xmm) - (src3 XmmMem) - (dst WritableXmm)) - - ;; XMM (scalar or vector) unary op that relies on the EVEX prefix. - (XmmUnaryRmREvex (op Avx512Opcode) - (src XmmMem) - (dst WritableXmm)) - ;; Converts an unsigned int64 to a float32/float64. (CvtUint64ToFloatSeq (dst_size OperandSize) ;; 4 or 8 (src Gpr) @@ -661,15 +635,6 @@ (decl cc_nz_or_z (CC) CC) (extern extractor cc_nz_or_z cc_nz_or_z) -(type Avx512Opcode - (enum Vcvtudq2ps - Vpabsq - Vpermi2b - Vpmullq - Vpopcntb - Vpsraq - VpsraqImm)) - (type FcmpImm extern (enum Equal LessThan @@ -1134,30 +1099,6 @@ (_ Unit (emit (MInst.LoadExtName dst extname offset distance)))) dst)) -;; Helper for creating `MInst.XmmUnaryRmREvex` instructions. -(decl xmm_unary_rm_r_evex (Avx512Opcode XmmMem) Xmm) -(rule (xmm_unary_rm_r_evex op src) - (let ((dst WritableXmm (temp_writable_xmm)) - (_ Unit (emit (MInst.XmmUnaryRmREvex op src dst)))) - dst)) - -;; Helper for creating `MInst.XmmRmREvex` instructions. -(decl xmm_rm_r_evex (Avx512Opcode Xmm XmmMem) Xmm) -(rule (xmm_rm_r_evex op src1 src2) - (let ((dst WritableXmm (temp_writable_xmm)) - (_ Unit (emit (MInst.XmmRmREvex op - src1 - src2 - dst)))) - dst)) - -;; Helper for creating `MInst.XmmUnaryRmRImmEvex` instructions. -(decl xmm_unary_rm_r_imm_evex (Avx512Opcode XmmMem u8) Xmm) -(rule (xmm_unary_rm_r_imm_evex op src imm) - (let ((dst WritableXmm (temp_writable_xmm)) - (_ Unit (emit (MInst.XmmUnaryRmRImmEvex op src dst imm)))) - dst)) - ;; Helper for creating `xmm_min_max_seq` pseudo-instructions. (decl xmm_min_max_seq (Type bool Xmm Xmm) Xmm) (rule (xmm_min_max_seq ty is_min lhs rhs) @@ -2689,40 +2630,27 @@ ;; Helper for creating `vcvtudq2ps` instructions. (decl x64_vcvtudq2ps (XmmMem) Xmm) -(rule (x64_vcvtudq2ps src) - (xmm_unary_rm_r_evex (Avx512Opcode.Vcvtudq2ps) src)) +(rule (x64_vcvtudq2ps src) (x64_vcvtudq2ps_a src)) ;; Helper for creating `vpabsq` instructions. (decl x64_vpabsq (XmmMem) Xmm) -(rule (x64_vpabsq src) - (xmm_unary_rm_r_evex (Avx512Opcode.Vpabsq) src)) +(rule (x64_vpabsq src) (x64_vpabsq_c src)) ;; Helper for creating `vpopcntb` instructions. (decl x64_vpopcntb (XmmMem) Xmm) -(rule (x64_vpopcntb src) - (xmm_unary_rm_r_evex (Avx512Opcode.Vpopcntb) src)) +(rule (x64_vpopcntb src) (x64_vpopcntb_a src)) ;; Helper for creating `vpmullq` instructions. ;; ;; Requires AVX-512 vl and dq. (decl x64_vpmullq (Xmm XmmMem) Xmm) -(rule (x64_vpmullq src1 src2) - (xmm_rm_r_evex (Avx512Opcode.Vpmullq) - src1 - src2)) +(rule (x64_vpmullq src1 src2) (x64_vpmullq_c src1 src2)) ;; Helper for creating `vpermi2b` instructions. ;; ;; Requires AVX-512 vl and vbmi extensions. (decl x64_vpermi2b (Xmm Xmm XmmMem) Xmm) -(rule (x64_vpermi2b src1 src2 src3) - (let ((dst WritableXmm (temp_writable_xmm)) - (_ Unit (emit (MInst.XmmRmREvex3 (Avx512Opcode.Vpermi2b) - src1 - src2 - src3 - dst)))) - dst)) +(rule (x64_vpermi2b src1 src2 src3) (x64_vpermi2b_a src1 src2 src3)) ;; Helpers for creating vector `shift` instructions. (decl x64_psllw (Xmm XmmMemImm) Xmm) @@ -2759,13 +2687,11 @@ ;; Helper for creating `vpsraq` instructions. (decl x64_vpsraq (Xmm XmmMem) Xmm) -(rule (x64_vpsraq src1 src2) - (xmm_rm_r_evex (Avx512Opcode.Vpsraq) src1 src2)) +(rule (x64_vpsraq src1 src2) (x64_vpsraq_g src1 src2)) ;; Helper for creating `vpsraq` instructions. (decl x64_vpsraq_imm (XmmMem u8) Xmm) -(rule (x64_vpsraq_imm src imm) - (xmm_unary_rm_r_imm_evex (Avx512Opcode.VpsraqImm) src imm)) +(rule (x64_vpsraq_imm src imm) (x64_vpsraq_f src imm)) ;; Helper for creating `pextr*` instructions. (decl x64_pextrb (Xmm u8) Gpr) diff --git a/cranelift/codegen/src/isa/x64/inst/args.rs b/cranelift/codegen/src/isa/x64/inst/args.rs index 241a4f1f1b..516eb2cfb4 100644 --- a/cranelift/codegen/src/isa/x64/inst/args.rs +++ b/cranelift/codegen/src/isa/x64/inst/args.rs @@ -7,7 +7,6 @@ use crate::ir::types::*; use crate::isa::x64::inst::Inst; use crate::isa::x64::inst::regs::pretty_print_reg; use crate::machinst::*; -use smallvec::{SmallVec, smallvec}; use std::fmt; use std::string::String; @@ -765,61 +764,6 @@ pub(crate) enum InstructionSet { AVX512VL, } -#[derive(Copy, Clone, PartialEq)] -#[expect(missing_docs, reason = "self-describing")] -pub enum Avx512TupleType { - Full, - FullMem, - Mem128, -} - -pub use crate::isa::x64::lower::isle::generated_code::Avx512Opcode; - -impl Avx512Opcode { - /// Which `InstructionSet`s support the opcode? - pub(crate) fn available_from(&self) -> SmallVec<[InstructionSet; 2]> { - match self { - Avx512Opcode::Vcvtudq2ps - | Avx512Opcode::Vpabsq - | Avx512Opcode::Vpsraq - | Avx512Opcode::VpsraqImm => { - smallvec![InstructionSet::AVX512F, InstructionSet::AVX512VL] - } - Avx512Opcode::Vpermi2b => { - smallvec![InstructionSet::AVX512VL, InstructionSet::AVX512VBMI] - } - Avx512Opcode::Vpmullq => smallvec![InstructionSet::AVX512VL, InstructionSet::AVX512DQ], - Avx512Opcode::Vpopcntb => { - smallvec![InstructionSet::AVX512VL, InstructionSet::AVX512BITALG] - } - } - } - - /// What is the "TupleType" of this opcode, which affects the scaling factor - /// for 8-bit displacements when this instruction uses memory operands. - /// - /// This can be found in the encoding table for each instruction and is - /// interpreted according to Table 2-34 and 2-35 in the Intel instruction - /// manual. - pub fn tuple_type(&self) -> Avx512TupleType { - use Avx512Opcode::*; - use Avx512TupleType::*; - - match self { - Vcvtudq2ps | Vpabsq | Vpmullq | VpsraqImm => Full, - Vpermi2b | Vpopcntb => FullMem, - Vpsraq => Mem128, - } - } -} - -impl fmt::Display for Avx512Opcode { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = format!("{self:?}"); - f.write_str(&s.to_lowercase()) - } -} - /// This defines the ways a value can be extended: either signed- or zero-extension, or none for /// types that are not extended. Contrast with [ExtMode], which defines the widths from and to which /// values can be extended. diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs index 518457282a..2df7a426a8 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit.rs @@ -1,7 +1,5 @@ use crate::ir::KnownSymbol; use crate::ir::immediates::{Ieee32, Ieee64}; -use crate::isa::x64::encoding::evex::{EvexInstruction, EvexVectorLength, RegisterOrAmode}; -use crate::isa::x64::encoding::rex::{LegacyPrefixes, OpcodeMap}; use crate::isa::x64::external::{AsmInst, CraneliftRegisters, PairedGpr}; use crate::isa::x64::inst::args::*; use crate::isa::x64::inst::*; @@ -451,9 +449,9 @@ pub(crate) fn emit( } if let Some(try_call) = call_info.try_call_info.as_ref() { - sink.add_call_site(&try_call.exception_dests); + sink.add_try_call_site(try_call.exception_handlers(&state.frame_layout())); } else { - sink.add_call_site(&[]); + sink.add_call_site(); } // Reclaim the outgoing argument area that was released by the @@ -496,7 +494,7 @@ pub(crate) fn emit( // The addend adjusts for the difference between the end of the instruction and the // beginning of the immediate field. sink.add_reloc_at_offset(offset - 4, Reloc::X86CallPCRel4, &call_info.dest, -4); - sink.add_call_site(&[]); + sink.add_call_site(); } Inst::ReturnCallUnknown { info: call_info } => { @@ -505,7 +503,7 @@ pub(crate) fn emit( emit_return_call_common_sequence(sink, info, state, &call_info); asm::inst::jmpq_m::new(callee).emit(sink, info, state); - sink.add_call_site(&[]); + sink.add_call_site(); } Inst::CallUnknown { @@ -526,9 +524,9 @@ pub(crate) fn emit( } if let Some(try_call) = call_info.try_call_info.as_ref() { - sink.add_call_site(&try_call.exception_dests); + sink.add_try_call_site(try_call.exception_handlers(&state.frame_layout())); } else { - sink.add_call_site(&[]); + sink.add_call_site(); } // Reclaim the outgoing argument area that was released by the callee, to ensure that @@ -794,114 +792,6 @@ pub(crate) fn emit( one_way_jmp(sink, *cc2, trap_label); } - Inst::XmmUnaryRmREvex { op, src, dst } => { - let dst = dst.to_reg().to_reg(); - let src = match src.clone().to_reg_mem().clone() { - RegMem::Reg { reg } => { - RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into()) - } - RegMem::Mem { addr } => { - RegisterOrAmode::Amode(addr.finalize(state.frame_layout(), sink)) - } - }; - - let (prefix, map, w, opcode) = match op { - Avx512Opcode::Vcvtudq2ps => (LegacyPrefixes::_F2, OpcodeMap::_0F, false, 0x7a), - Avx512Opcode::Vpabsq => (LegacyPrefixes::_66, OpcodeMap::_0F38, true, 0x1f), - Avx512Opcode::Vpopcntb => (LegacyPrefixes::_66, OpcodeMap::_0F38, false, 0x54), - _ => unimplemented!("Opcode {:?} not implemented", op), - }; - EvexInstruction::new() - .length(EvexVectorLength::V128) - .prefix(prefix) - .map(map) - .w(w) - .opcode(opcode) - .tuple_type(op.tuple_type()) - .reg(dst.to_real_reg().unwrap().hw_enc()) - .rm(src) - .encode(sink); - } - - Inst::XmmUnaryRmRImmEvex { op, src, dst, imm } => { - let dst = dst.to_reg().to_reg(); - let src = match src.clone().to_reg_mem().clone() { - RegMem::Reg { reg } => { - RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into()) - } - RegMem::Mem { addr } => { - RegisterOrAmode::Amode(addr.finalize(state.frame_layout(), sink)) - } - }; - - let (opcode, opcode_ext, w) = match op { - Avx512Opcode::VpsraqImm => (0x72, 4, true), - _ => unimplemented!("Opcode {:?} not implemented", op), - }; - EvexInstruction::new() - .length(EvexVectorLength::V128) - .prefix(LegacyPrefixes::_66) - .map(OpcodeMap::_0F) - .w(w) - .opcode(opcode) - .reg(opcode_ext) - .vvvvv(dst.to_real_reg().unwrap().hw_enc()) - .tuple_type(op.tuple_type()) - .rm(src) - .imm(*imm) - .encode(sink); - } - - Inst::XmmRmREvex { - op, - src1, - src2, - dst, - } - | Inst::XmmRmREvex3 { - op, - src1: _, // `dst` reuses `src1`. - src2: src1, - src3: src2, - dst, - } => { - let reused_src = match inst { - Inst::XmmRmREvex3 { src1, .. } => Some(src1.to_reg()), - _ => None, - }; - let src1 = src1.to_reg(); - let src2 = match src2.clone().to_reg_mem().clone() { - RegMem::Reg { reg } => { - RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into()) - } - RegMem::Mem { addr } => { - RegisterOrAmode::Amode(addr.finalize(state.frame_layout(), sink)) - } - }; - let dst = dst.to_reg().to_reg(); - if let Some(src1) = reused_src { - debug_assert_eq!(src1, dst); - } - - let (w, opcode, map) = match op { - Avx512Opcode::Vpermi2b => (false, 0x75, OpcodeMap::_0F38), - Avx512Opcode::Vpmullq => (true, 0x40, OpcodeMap::_0F38), - Avx512Opcode::Vpsraq => (true, 0xE2, OpcodeMap::_0F), - _ => unimplemented!("Opcode {:?} not implemented", op), - }; - EvexInstruction::new() - .length(EvexVectorLength::V128) - .prefix(LegacyPrefixes::_66) - .map(map) - .w(w) - .opcode(opcode) - .tuple_type(op.tuple_type()) - .reg(dst.to_real_reg().unwrap().hw_enc()) - .vvvvv(src1.to_real_reg().unwrap().hw_enc()) - .rm(src2) - .encode(sink); - } - Inst::XmmMinMaxSeq { size, is_min, diff --git a/cranelift/codegen/src/isa/x64/inst/emit_state.rs b/cranelift/codegen/src/isa/x64/inst/emit_state.rs index c28776b904..426a17a280 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit_state.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit_state.rs @@ -14,8 +14,8 @@ pub struct EmitState { ctrl_plane: ControlPlane, /// A copy of the frame layout, used during the emission of `Inst::ReturnCallKnown` and - /// `Inst::ReturnCallUnknown` instructions. - frame_layout: FrameLayout, + /// `Inst::ReturnCallUnknown` instructions and exception callsites. + pub frame_layout: FrameLayout, } impl MachInstEmitState for EmitState { diff --git a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs index 0d468cf005..1bd8b0f6b2 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs @@ -19,52 +19,6 @@ use crate::isa::x64::lower::isle::generated_code::{Atomic128RmwSeqOp, AtomicRmwS use alloc::vec::Vec; use cranelift_entity::EntityRef as _; -impl Inst { - fn xmm_unary_rm_r_evex(op: Avx512Opcode, src: RegMem, dst: Writable) -> Inst { - src.assert_regclass_is(RegClass::Float); - debug_assert!(dst.to_reg().class() == RegClass::Float); - Inst::XmmUnaryRmREvex { - op, - src: XmmMem::unwrap_new(src), - dst: WritableXmm::from_writable_reg(dst).unwrap(), - } - } - - fn xmm_rm_r_evex(op: Avx512Opcode, src1: Reg, src2: RegMem, dst: Writable) -> Self { - debug_assert_ne!(op, Avx512Opcode::Vpermi2b); - src2.assert_regclass_is(RegClass::Float); - debug_assert!(src1.class() == RegClass::Float); - debug_assert!(dst.to_reg().class() == RegClass::Float); - Inst::XmmRmREvex { - op, - src1: Xmm::unwrap_new(src1), - src2: XmmMem::unwrap_new(src2), - dst: WritableXmm::from_writable_reg(dst).unwrap(), - } - } - - fn xmm_rm_r_evex3( - op: Avx512Opcode, - src1: Reg, - src2: Reg, - src3: RegMem, - dst: Writable, - ) -> Self { - debug_assert_eq!(op, Avx512Opcode::Vpermi2b); - src3.assert_regclass_is(RegClass::Float); - debug_assert!(src1.class() == RegClass::Float); - debug_assert!(src2.class() == RegClass::Float); - debug_assert!(dst.to_reg().class() == RegClass::Float); - Inst::XmmRmREvex3 { - op, - src1: Xmm::unwrap_new(src1), - src2: Xmm::unwrap_new(src2), - src3: XmmMem::unwrap_new(src3), - dst: WritableXmm::from_writable_reg(dst).unwrap(), - } - } -} - #[test] fn test_x64_emit() { let rax = regs::rax(); @@ -96,7 +50,7 @@ fn test_x64_emit() { let xmm11 = regs::xmm11(); let xmm12 = regs::xmm12(); let _xmm13 = regs::xmm13(); - let xmm14 = regs::xmm14(); + let _xmm14 = regs::xmm14(); let _xmm15 = regs::xmm15(); // And Writable<> versions of the same: @@ -114,13 +68,13 @@ fn test_x64_emit() { let _w_r15 = Writable::::from_reg(r15); let _w_xmm0 = Writable::::from_reg(xmm0); - let w_xmm1 = Writable::::from_reg(xmm1); - let w_xmm2 = Writable::::from_reg(xmm2); + let _w_xmm1 = Writable::::from_reg(xmm1); + let _w_xmm2 = Writable::::from_reg(xmm2); let _w_xmm3 = Writable::::from_reg(xmm3); let _w_xmm4 = Writable::::from_reg(xmm4); let _w_xmm6 = Writable::::from_reg(xmm6); let _w_xmm7 = Writable::::from_reg(xmm7); - let w_xmm8 = Writable::::from_reg(xmm8); + let _w_xmm8 = Writable::::from_reg(xmm8); let _w_xmm9 = Writable::::from_reg(xmm9); let _w_xmm10 = Writable::::from_reg(xmm10); let _w_xmm11 = Writable::::from_reg(xmm11); @@ -222,67 +176,6 @@ fn test_x64_emit() { // ======================================================== // JmpCondCompound isn't a real instruction - // ======================================================== - // XMM_RM_R: Integer Packed - - insns.push(( - Inst::xmm_rm_r_evex(Avx512Opcode::Vpmullq, xmm10, RegMem::reg(xmm14), w_xmm1), - "62D2AD0840CE", - "vpmullq %xmm14, %xmm10, %xmm1", - )); - - insns.push(( - Inst::xmm_rm_r_evex(Avx512Opcode::Vpsraq, xmm10, RegMem::reg(xmm14), w_xmm1), - "62D1AD08E2CE", - "vpsraq %xmm14, %xmm10, %xmm1", - )); - - insns.push(( - Inst::xmm_rm_r_evex3( - Avx512Opcode::Vpermi2b, - xmm1, - xmm10, - RegMem::reg(xmm14), - w_xmm1, - ), - "62D22D0875CE", - "vpermi2b %xmm14, %xmm10, %xmm1, %xmm1", - )); - - insns.push(( - Inst::xmm_rm_r_evex3( - Avx512Opcode::Vpermi2b, - xmm2, - xmm0, - RegMem::reg(xmm1), - w_xmm2, - ), - "62F27D0875D1", - "vpermi2b %xmm1, %xmm0, %xmm2, %xmm2", - )); - - // ======================================================== - // XMM_MOV: Packed Move - - // XmmUnary: moves and unary float ops - insns.push(( - Inst::xmm_unary_rm_r_evex(Avx512Opcode::Vpabsq, RegMem::reg(xmm2), w_xmm8), - "6272FD081FC2", - "vpabsq %xmm2, %xmm8", - )); - - insns.push(( - Inst::xmm_unary_rm_r_evex(Avx512Opcode::Vcvtudq2ps, RegMem::reg(xmm2), w_xmm8), - "62717F087AC2", - "vcvtudq2ps %xmm2, %xmm8", - )); - - insns.push(( - Inst::xmm_unary_rm_r_evex(Avx512Opcode::Vpopcntb, RegMem::reg(xmm2), w_xmm8), - "62727D0854C2", - "vpopcntb %xmm2, %xmm8", - )); - // ======================================================== // Pertaining to atomics. // Use `r9` with a 0 offset. diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs index 7ea1727ae1..f2addf2354 100644 --- a/cranelift/codegen/src/isa/x64/inst/mod.rs +++ b/cranelift/codegen/src/isa/x64/inst/mod.rs @@ -11,7 +11,6 @@ use crate::isa::{CallConv, FunctionAlignment}; use crate::{CodegenError, CodegenResult, settings}; use crate::{machinst::*, trace}; use alloc::boxed::Box; -use alloc::vec::Vec; use core::slice; use cranelift_assembler_x64 as asm; use cranelift_entity::{Signed, Unsigned}; @@ -111,11 +110,6 @@ impl Inst { smallvec![InstructionSet::CMPXCHG16b] } - Inst::XmmUnaryRmREvex { op, .. } - | Inst::XmmRmREvex { op, .. } - | Inst::XmmRmREvex3 { op, .. } - | Inst::XmmUnaryRmRImmEvex { op, .. } => op.available_from(), - Inst::External { inst } => { use cranelift_assembler_x64::Feature::*; let mut features = smallvec![]; @@ -136,6 +130,9 @@ impl Inst { avx2 => features.push(InstructionSet::AVX2), avx512f => features.push(InstructionSet::AVX512F), avx512vl => features.push(InstructionSet::AVX512VL), + avx512dq => features.push(InstructionSet::AVX512DQ), + avx512bitalg => features.push(InstructionSet::AVX512BITALG), + avx512vbmi => features.push(InstructionSet::AVX512VBMI), cmpxchg16b => features.push(InstructionSet::CMPXCHG16b), fma => features.push(InstructionSet::FMA), } @@ -450,52 +447,6 @@ impl PrettyPrint for Inst { format!("checked_srem_seq {dividend}, {divisor}, {dst}") } - Inst::XmmUnaryRmREvex { op, src, dst, .. } => { - let dst = pretty_print_reg(dst.to_reg().to_reg(), 8); - let src = src.pretty_print(8); - let op = ljustify(op.to_string()); - format!("{op} {src}, {dst}") - } - - Inst::XmmUnaryRmRImmEvex { - op, src, dst, imm, .. - } => { - let dst = pretty_print_reg(dst.to_reg().to_reg(), 8); - let src = src.pretty_print(8); - let op = ljustify(op.to_string()); - format!("{op} ${imm}, {src}, {dst}") - } - - Inst::XmmRmREvex { - op, - src1, - src2, - dst, - .. - } => { - let src1 = pretty_print_reg(src1.to_reg(), 8); - let src2 = src2.pretty_print(8); - let dst = pretty_print_reg(dst.to_reg().to_reg(), 8); - let op = ljustify(op.to_string()); - format!("{op} {src2}, {src1}, {dst}") - } - - Inst::XmmRmREvex3 { - op, - src1, - src2, - src3, - dst, - .. - } => { - let src1 = pretty_print_reg(src1.to_reg(), 8); - let src2 = pretty_print_reg(src2.to_reg(), 8); - let src3 = src3.pretty_print(8); - let dst = pretty_print_reg(dst.to_reg().to_reg(), 8); - let op = ljustify(op.to_string()); - format!("{op} {src3}, {src2}, {src1}, {dst}") - } - Inst::XmmMinMaxSeq { lhs, rhs, @@ -908,13 +859,11 @@ impl PrettyPrint for Inst { } fn pretty_print_try_call(info: &TryCallInfo) -> String { - let dests = info - .exception_dests - .iter() - .map(|(tag, label)| format!("{tag:?}: {label:?}")) - .collect::>() - .join(", "); - format!("; jmp {:?}; catch [{dests}]", info.continuation) + format!( + "; jmp {:?}; catch [{}]", + info.continuation, + info.pretty_print_dests() + ) } impl fmt::Debug for Inst { @@ -958,36 +907,6 @@ fn x64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { collector.reg_fixed_use(dividend, regs::rax()); collector.reg_fixed_def(dst, regs::rax()); } - Inst::XmmUnaryRmREvex { src, dst, .. } | Inst::XmmUnaryRmRImmEvex { src, dst, .. } => { - collector.reg_def(dst); - src.get_operands(collector); - } - Inst::XmmRmREvex { - op, - src1, - src2, - dst, - .. - } => { - assert_ne!(*op, Avx512Opcode::Vpermi2b); - collector.reg_use(src1); - src2.get_operands(collector); - collector.reg_def(dst); - } - Inst::XmmRmREvex3 { - op, - src1, - src2, - src3, - dst, - .. - } => { - assert_eq!(*op, Avx512Opcode::Vpermi2b); - collector.reg_use(src1); - collector.reg_use(src2); - src3.get_operands(collector); - collector.reg_reuse_def(dst, 0); // Reuse `src1`. - } Inst::XmmUninitializedValue { dst } => collector.reg_def(dst), Inst::GprUninitializedValue { dst } => collector.reg_def(dst), Inst::XmmMinMaxSeq { lhs, rhs, dst, .. } => { @@ -1068,6 +987,7 @@ fn x64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { defs, clobbers, dest, + try_call_info, .. } = &mut **info; debug_assert_ne!(*dest, ExternalName::LibCall(LibCall::Probestack)); @@ -1081,6 +1001,9 @@ fn x64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { } } collector.reg_clobbers(*clobbers); + if let Some(try_call_info) = try_call_info { + try_call_info.collect_operands(collector); + } } Inst::CallUnknown { info } => { @@ -1090,6 +1013,7 @@ fn x64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { clobbers, callee_conv, dest, + try_call_info, .. } = &mut **info; match dest { @@ -1111,6 +1035,9 @@ fn x64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { } } collector.reg_clobbers(*clobbers); + if let Some(try_call_info) = try_call_info { + try_call_info.collect_operands(collector); + } } Inst::StackSwitchBasic { store_context_ptr, diff --git a/cranelift/codegen/src/isa/x64/mod.rs b/cranelift/codegen/src/isa/x64/mod.rs index 8c217c1417..700172a306 100644 --- a/cranelift/codegen/src/isa/x64/mod.rs +++ b/cranelift/codegen/src/isa/x64/mod.rs @@ -23,7 +23,6 @@ use std::string::String; use target_lexicon::Triple; mod abi; -pub(crate) mod encoding; mod inst; mod lower; mod pcc; diff --git a/cranelift/codegen/src/isa/x64/pcc.rs b/cranelift/codegen/src/isa/x64/pcc.rs index a388c08f3f..3c593677c9 100644 --- a/cranelift/codegen/src/isa/x64/pcc.rs +++ b/cranelift/codegen/src/isa/x64/pcc.rs @@ -76,30 +76,6 @@ pub(crate) fn check( Inst::StackProbeLoop { tmp, .. } => ensure_no_fact(vcode, tmp.to_reg()), - // NOTE: it's assumed that all of these cases perform 128-bit loads, but this hasn't been - // verified. The effect of this will be spurious PCC failures when these instructions are - // involved. - Inst::XmmRmREvex { dst, ref src2, .. } - | Inst::XmmUnaryRmRImmEvex { - dst, src: ref src2, .. - } - | Inst::XmmUnaryRmREvex { - dst, src: ref src2, .. - } - | Inst::XmmRmREvex3 { - dst, - src3: ref src2, - .. - } => { - match <&RegMem>::from(src2) { - RegMem::Mem { addr } => { - check_load(ctx, None, addr, vcode, I8X16, 128)?; - } - RegMem::Reg { .. } => {} - } - ensure_no_fact(vcode, dst.to_writable_reg().to_reg()) - } - Inst::CvtUint64ToFloatSeq { dst, tmp_gpr1, diff --git a/cranelift/codegen/src/lib.rs b/cranelift/codegen/src/lib.rs index 3e86176643..9ead9756b4 100644 --- a/cranelift/codegen/src/lib.rs +++ b/cranelift/codegen/src/lib.rs @@ -23,7 +23,7 @@ extern crate std; #[cfg(not(feature = "std"))] use hashbrown::{HashMap, HashSet, hash_map}; #[cfg(feature = "std")] -use std::collections::{HashMap, HashSet, hash_map}; +use std::collections::{HashMap, hash_map}; pub use crate::context::Context; pub use crate::value_label::{LabelValueLoc, ValueLabelsRanges, ValueLocRange}; @@ -63,8 +63,9 @@ pub mod write; pub use crate::entity::packed_option; pub use crate::machinst::buffer::{ - FinalizedMachCallSite, FinalizedMachReloc, FinalizedRelocTarget, MachCallSite, MachSrcLoc, - MachTextSectionBuilder, MachTrap, OpenPatchRegion, PatchRegion, + ExceptionContextLoc, FinalizedMachCallSite, FinalizedMachExceptionHandler, FinalizedMachReloc, + FinalizedRelocTarget, MachCallSite, MachSrcLoc, MachTextSectionBuilder, MachTrap, + OpenPatchRegion, PatchRegion, }; pub use crate::machinst::{ CallInfo, CompiledCode, Final, MachBuffer, MachBufferFinalized, MachInst, MachInstEmit, diff --git a/cranelift/codegen/src/machinst/abi.rs b/cranelift/codegen/src/machinst/abi.rs index 5bc3843b5d..e76abcf5e0 100644 --- a/cranelift/codegen/src/machinst/abi.rs +++ b/cranelift/codegen/src/machinst/abi.rs @@ -107,7 +107,6 @@ use crate::settings::ProbestackStrategy; use crate::{ir, isa}; use crate::{machinst::*, trace}; use alloc::boxed::Box; -use cranelift_entity::packed_option::PackedOption; use regalloc2::{MachineEnv, PReg, PRegSet}; use rustc_hash::FxHashMap; use smallvec::smallvec; @@ -641,7 +640,20 @@ pub struct TryCallInfo { /// The target to jump to on a normal returhn. pub continuation: MachLabel, /// Exception tags to catch and corresponding destination labels. - pub exception_dests: Box<[(PackedOption, MachLabel)]>, + pub exception_handlers: Box<[TryCallHandler]>, +} + +/// Information about an individual handler at a try-call site. +#[derive(Clone, Debug)] +pub enum TryCallHandler { + /// If the tag matches (given the current context), recover at the + /// label. + Tag(ExceptionTag, MachLabel), + /// Recover at the label unconditionally. + Default(MachLabel), + /// Set the dynamic context for interpreting tags at this point in + /// the handler list. + Context(Reg), } impl CallInfo { @@ -1013,6 +1025,10 @@ impl std::ops::Index for SigSet { /// Structure describing the layout of a function's stack frame. #[derive(Clone, Debug, Default)] pub struct FrameLayout { + /// Word size in bytes, so this struct can be + /// monomorphic/independent of `ABIMachineSpec`. + pub word_bytes: u32, + /// N.B. The areas whose sizes are given in this structure fully /// cover the current function's stack frame, from high to low /// stack addresses in the sequence below. Each size contains @@ -1089,6 +1105,16 @@ impl FrameLayout { pub fn sp_to_sized_stack_slots(&self) -> u32 { self.outgoing_args_size } + + /// Get the offset of a spill slot from SP. + pub fn spillslot_offset(&self, spillslot: SpillSlot) -> i64 { + // Offset from beginning of spillslot area. + let islot = spillslot.index() as i64; + let spill_off = islot * self.word_bytes as i64; + let sp_off = self.stackslots_size as i64 + spill_off; + + sp_off + } } /// ABI object for a function body. @@ -2315,12 +2341,7 @@ impl Callee { /// Get the spill slot offset relative to the fixed allocation area start. pub fn get_spillslot_offset(&self, slot: SpillSlot) -> i64 { - // Offset from beginning of spillslot area. - let islot = slot.index() as i64; - let spill_off = islot * M::word_bytes() as i64; - let sp_off = self.stackslots_size as i64 + spill_off; - - sp_off + self.frame_layout().spillslot_offset(slot) } /// Generate a spill. @@ -2464,6 +2485,52 @@ impl CallInfo { } } +impl TryCallInfo { + pub(crate) fn exception_handlers( + &self, + layout: &FrameLayout, + ) -> impl Iterator { + self.exception_handlers.iter().map(|handler| match handler { + TryCallHandler::Tag(tag, label) => MachExceptionHandler::Tag(*tag, *label), + TryCallHandler::Default(label) => MachExceptionHandler::Default(*label), + TryCallHandler::Context(reg) => { + let loc = if let Some(spillslot) = reg.to_spillslot() { + let offset = layout.spillslot_offset(spillslot); + ExceptionContextLoc::SPOffset(u32::try_from(offset).expect("SP offset cannot be negative or larger than 4GiB")) + } else if let Some(realreg) = reg.to_real_reg() { + ExceptionContextLoc::GPR(realreg.hw_enc()) + } else { + panic!("Virtual register present in try-call handler clause after register allocation"); + }; + MachExceptionHandler::Context(loc) + } + }) + } + + pub(crate) fn pretty_print_dests(&self) -> String { + self.exception_handlers + .iter() + .map(|handler| match handler { + TryCallHandler::Tag(tag, label) => format!("{tag:?}: {label:?}"), + TryCallHandler::Default(label) => format!("default: {label:?}"), + TryCallHandler::Context(loc) => format!("context {loc:?}"), + }) + .collect::>() + .join(", ") + } + + pub(crate) fn collect_operands(&mut self, collector: &mut impl OperandVisitor) { + for handler in &mut self.exception_handlers { + match handler { + TryCallHandler::Context(ctx) => { + collector.any_late_use(ctx); + } + TryCallHandler::Tag(_, _) | TryCallHandler::Default(_) => {} + } + } + } +} + #[cfg(test)] mod tests { use super::SigData; diff --git a/cranelift/codegen/src/machinst/buffer.rs b/cranelift/codegen/src/machinst/buffer.rs index af66a6d978..88e32d30d4 100644 --- a/cranelift/codegen/src/machinst/buffer.rs +++ b/cranelift/codegen/src/machinst/buffer.rs @@ -182,7 +182,6 @@ use crate::{MachInstEmitState, ir}; use crate::{VCodeConstantData, timing}; use core::ops::Range; use cranelift_control::ControlPlane; -use cranelift_entity::packed_option::PackedOption; use cranelift_entity::{PrimaryMap, entity_impl}; use smallvec::SmallVec; use std::cmp::Ordering; @@ -252,7 +251,7 @@ pub struct MachBuffer { /// Any call site records referring to this code. call_sites: SmallVec<[MachCallSite; 16]>, /// Any exception-handler records referred to at call sites. - exception_handlers: SmallVec<[(PackedOption, MachLabel); 16]>, + exception_handlers: SmallVec<[MachExceptionHandler; 16]>, /// Any source location mappings referring to this code. srclocs: SmallVec<[MachSrcLoc; 64]>, /// Any user stack maps for this code. @@ -365,7 +364,7 @@ pub struct MachBufferFinalized { /// Any call site records referring to this code. pub(crate) call_sites: SmallVec<[MachCallSite; 16]>, /// Any exception-handler records referred to at call sites. - pub(crate) exception_handlers: SmallVec<[(PackedOption, CodeOffset); 16]>, + pub(crate) exception_handlers: SmallVec<[FinalizedMachExceptionHandler; 16]>, /// Any source location mappings referring to this code. pub(crate) srclocs: SmallVec<[T::MachSrcLocType; 64]>, /// Any user stack maps for this code. @@ -1523,7 +1522,7 @@ impl MachBuffer { let finalized_exception_handlers = self .exception_handlers .iter() - .map(|(tag, label)| (*tag, self.resolve_label_offset(*label))) + .map(|handler| handler.finalize(|label| self.resolve_label_offset(label))) .collect(); let mut srclocs = self.srclocs; @@ -1610,18 +1609,25 @@ impl MachBuffer { }); } - /// Add a call-site record at the current offset, optionally with exception handlers. - pub fn add_call_site( + /// Add a call-site record at the current offset. + pub fn add_call_site(&mut self) { + self.add_try_call_site(core::iter::empty()); + } + + /// Add a call-site record at the current offset with exception + /// handlers. + pub fn add_try_call_site( &mut self, - exception_handlers: &[(PackedOption, MachLabel)], + exception_handlers: impl Iterator, ) { let start = u32::try_from(self.exception_handlers.len()).unwrap(); - self.exception_handlers - .extend(exception_handlers.into_iter().copied()); + self.exception_handlers.extend(exception_handlers); let end = u32::try_from(self.exception_handlers.len()).unwrap(); + let exception_handler_range = start..end; + self.call_sites.push(MachCallSite { ret_addr: self.data.len() as CodeOffset, - exception_handler_range: start..end, + exception_handler_range, }); } @@ -1774,16 +1780,79 @@ impl MachBufferFinalized { /// call site. pub fn call_sites(&self) -> impl Iterator> + '_ { self.call_sites.iter().map(|call_site| { - let range = call_site.exception_handler_range.clone(); - let range = usize::try_from(range.start).unwrap()..usize::try_from(range.end).unwrap(); + let handler_range = call_site.exception_handler_range.clone(); + let handler_range = usize::try_from(handler_range.start).unwrap() + ..usize::try_from(handler_range.end).unwrap(); FinalizedMachCallSite { ret_addr: call_site.ret_addr, - exception_handlers: &self.exception_handlers[range], + exception_handlers: &self.exception_handlers[handler_range], } }) } } +/// An item in the exception-handler list for a callsite, with label +/// references. Items are interpreted in left-to-right order and the +/// first match wins. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum MachExceptionHandler { + /// A specific tag (in the current dynamic context) should be + /// handled by the code at the given offset. + Tag(ExceptionTag, MachLabel), + /// All exceptions should be handled by the code at the given + /// offset. + Default(MachLabel), + /// The dynamic context for interpreting tags is updated to the + /// value stored in the given machine location (in this frame's + /// context). + Context(ExceptionContextLoc), +} + +impl MachExceptionHandler { + fn finalize CodeOffset>(self, f: F) -> FinalizedMachExceptionHandler { + match self { + Self::Tag(tag, label) => FinalizedMachExceptionHandler::Tag(tag, f(label)), + Self::Default(label) => FinalizedMachExceptionHandler::Default(f(label)), + Self::Context(loc) => FinalizedMachExceptionHandler::Context(loc), + } + } +} + +/// An item in the exception-handler list for a callsite, with final +/// (lowered) code offsets. Items are interpreted in left-to-right +/// order and the first match wins. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr( + feature = "enable-serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub enum FinalizedMachExceptionHandler { + /// A specific tag (in the current dynamic context) should be + /// handled by the code at the given offset. + Tag(ExceptionTag, CodeOffset), + /// All exceptions should be handled by the code at the given + /// offset. + Default(CodeOffset), + /// The dynamic context for interpreting tags is updated to the + /// value stored in the given machine location (in this frame's + /// context). + Context(ExceptionContextLoc), +} + +/// A location for a dynamic exception context value. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr( + feature = "enable-serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub enum ExceptionContextLoc { + /// An offset from SP at the callsite. + SPOffset(u32), + /// A GPR at the callsite. The physical register number for the + /// GPR register file on the target architecture is used. + GPR(u8), +} + /// Metadata about a constant. struct MachBufferConstant { /// A label which has not yet been bound which can be used for this @@ -1962,7 +2031,7 @@ pub struct FinalizedMachCallSite<'a> { /// Exception handlers at this callsite, with target offsets /// *relative to the start of the buffer*. - pub exception_handlers: &'a [(PackedOption, CodeOffset)], + pub exception_handlers: &'a [FinalizedMachExceptionHandler], } /// A source-location mapping resulting from a compilation. @@ -2507,10 +2576,13 @@ mod test { buf.put1(2); buf.add_trap(TrapCode::INTEGER_OVERFLOW); buf.add_trap(TrapCode::INTEGER_DIVISION_BY_ZERO); - buf.add_call_site(&[ - (None.into(), label(1)), - (Some(ExceptionTag::new(42)).into(), label(2)), - ]); + buf.add_try_call_site( + [ + MachExceptionHandler::Tag(ExceptionTag::new(42), label(2)), + MachExceptionHandler::Default(label(1)), + ] + .into_iter(), + ); buf.add_reloc( Reloc::Abs4, &ExternalName::User(UserExternalNameRef::new(0)), @@ -2546,7 +2618,10 @@ mod test { assert_eq!(call_sites[0].ret_addr, 2); assert_eq!( call_sites[0].exception_handlers, - &[(None.into(), 4), (Some(ExceptionTag::new(42)).into(), 5)] + &[ + FinalizedMachExceptionHandler::Tag(ExceptionTag::new(42), 5), + FinalizedMachExceptionHandler::Default(4) + ], ); assert_eq!( buf.relocs() diff --git a/cranelift/codegen/src/machinst/isle.rs b/cranelift/codegen/src/machinst/isle.rs index 7681593bda..a9646a5863 100644 --- a/cranelift/codegen/src/machinst/isle.rs +++ b/cranelift/codegen/src/machinst/isle.rs @@ -622,16 +622,36 @@ macro_rules! isle_lower_prelude_methods { et: ExceptionTable, labels: &MachLabelSlice, ) -> OptionTryCallInfo { - let exception_dests = self.lower_ctx.dfg().exception_tables[et] - .catches() - .map(|(tag, _)| tag.into()) - .zip(labels.iter().cloned()) - .collect::>() - .into_boxed_slice(); + let mut exception_handlers = vec![]; + let mut labels = labels.iter().cloned(); + for item in self.lower_ctx.dfg().exception_tables[et].clone().items() { + match item { + crate::ir::ExceptionTableItem::Tag(tag, _) => { + exception_handlers.push(crate::machinst::abi::TryCallHandler::Tag( + tag, + labels.next().unwrap(), + )); + } + crate::ir::ExceptionTableItem::Default(_) => { + exception_handlers.push(crate::machinst::abi::TryCallHandler::Default( + labels.next().unwrap(), + )); + } + crate::ir::ExceptionTableItem::Context(ctx) => { + let reg = self.put_in_reg(ctx); + exception_handlers.push(crate::machinst::abi::TryCallHandler::Context(reg)); + } + } + } + + let continuation = labels.next().unwrap(); + assert_eq!(labels.next(), None); + + let exception_handlers = exception_handlers.into_boxed_slice(); Some(TryCallInfo { - continuation: *labels.last().unwrap(), - exception_dests, + continuation, + exception_handlers, }) } diff --git a/cranelift/codegen/src/machinst/reg.rs b/cranelift/codegen/src/machinst/reg.rs index 7e74b737ec..f60afcf7d0 100644 --- a/cranelift/codegen/src/machinst/reg.rs +++ b/cranelift/codegen/src/machinst/reg.rs @@ -496,6 +496,17 @@ pub trait OperandVisitorImpl: OperandVisitor { OperandPos::Late, ); } + + /// Add a use that can be allocated to either a register or a + /// spillslot, at the end of the instruction (`After` position). + fn any_late_use(&mut self, reg: &mut impl AsMut) { + self.add_operand( + reg.as_mut(), + OperandConstraint::Any, + OperandKind::Use, + OperandPos::Late, + ); + } } impl OperandVisitorImpl for T {} diff --git a/cranelift/codegen/src/opts/arithmetic.isle b/cranelift/codegen/src/opts/arithmetic.isle index 5b3c8d717b..575dc2966a 100644 --- a/cranelift/codegen/src/opts/arithmetic.isle +++ b/cranelift/codegen/src/opts/arithmetic.isle @@ -325,3 +325,6 @@ (iconst_s ty m))) (if-let m (i64_checked_neg n)) (band ty x (iconst ty (imm64_masked ty (i64_cast_unsigned (i64_not n)))))) + +;; (x + y) - (x | y) --> x & y +(rule (simplify (isub ty (iadd ty x y) (bor ty x y))) (band ty x y)) diff --git a/cranelift/codegen/src/verifier/mod.rs b/cranelift/codegen/src/verifier/mod.rs index 2609a1682f..1c19270f6e 100644 --- a/cranelift/codegen/src/verifier/mod.rs +++ b/cranelift/codegen/src/verifier/mod.rs @@ -68,6 +68,7 @@ use crate::dominator_tree::DominatorTree; use crate::dominator_tree::DominatorTreePreorder; use crate::entity::SparseSet; use crate::flowgraph::{BlockPredecessor, ControlFlowGraph}; +use crate::ir::ExceptionTableItem; use crate::ir::entities::AnyEntity; use crate::ir::instructions::{CallInfo, InstructionFormat, ResolvedConstraint}; use crate::ir::{self, ArgumentExtension, BlockArg, ExceptionTable}; @@ -1413,8 +1414,19 @@ impl<'a> Verifier<'a> { BlockCallTargetType::ExNormalRet, errors, )?; - for (_tag, block) in exdata.catches() { - self.typecheck_block_call(inst, block, BlockCallTargetType::Exception, errors)?; + for item in exdata.items() { + match item { + ExceptionTableItem::Tag(_, block_call) + | ExceptionTableItem::Default(block_call) => { + self.typecheck_block_call( + inst, + &block_call, + BlockCallTargetType::Exception, + errors, + )?; + } + ExceptionTableItem::Context(_) => {} + } } } inst => debug_assert!(!inst.opcode().is_branch()), diff --git a/cranelift/entity/src/map.rs b/cranelift/entity/src/map.rs index 0bae05a594..34ce35c778 100644 --- a/cranelift/entity/src/map.rs +++ b/cranelift/entity/src/map.rs @@ -149,6 +149,23 @@ where } } +impl FromIterator<(K, V)> for SecondaryMap +where + K: EntityRef, + V: Clone + Default, +{ + fn from_iter>(iter: T) -> Self { + let iter = iter.into_iter(); + let (min, max) = iter.size_hint(); + let cap = max.unwrap_or_else(|| 2 * min); + let mut map = Self::with_capacity(cap); + for (k, v) in iter { + map[k] = v; + } + map + } +} + /// Immutable indexing into an `SecondaryMap`. /// /// All keys are permitted. Untouched entries have the default value. diff --git a/cranelift/entity/src/primary.rs b/cranelift/entity/src/primary.rs index b0cd5a2a5d..cd01993246 100644 --- a/cranelift/entity/src/primary.rs +++ b/cranelift/entity/src/primary.rs @@ -7,7 +7,6 @@ use alloc::boxed::Box; use alloc::vec::Vec; use core::fmt; use core::marker::PhantomData; -use core::mem; use core::ops::{Index, IndexMut}; use core::slice; #[cfg(feature = "enable-serde")] @@ -163,35 +162,11 @@ where /// /// Returns an error if an element does not exist, or if the same key was passed more than /// once. - // This implementation is taken from the unstable `get_many_mut`. - // - // Once it has been stabilised we can call that method directly. - pub fn get_many_mut( + pub fn get_disjoint_mut( &mut self, indices: [K; N], - ) -> Result<[&mut V; N], GetManyMutError> { - for (i, &idx) in indices.iter().enumerate() { - if idx.index() >= self.len() { - return Err(GetManyMutError::DoesNotExist(idx)); - } - for &idx2 in &indices[..i] { - if idx == idx2 { - return Err(GetManyMutError::MultipleOf(idx)); - } - } - } - - let slice: *mut V = self.elems.as_mut_ptr(); - let mut arr: mem::MaybeUninit<[&mut V; N]> = mem::MaybeUninit::uninit(); - let arr_ptr = arr.as_mut_ptr(); - - unsafe { - for i in 0..N { - let idx = *indices.get_unchecked(i); - *(*arr_ptr).get_unchecked_mut(i) = &mut *slice.add(idx.index()); - } - Ok(arr.assume_init()) - } + ) -> Result<[&mut V; N], slice::GetDisjointMutError> { + self.elems.get_disjoint_mut(indices.map(|k| k.index())) } /// Performs a binary search on the values with a key extraction function. @@ -236,12 +211,6 @@ where } } -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum GetManyMutError { - DoesNotExist(K), - MultipleOf(K), -} - impl Default for PrimaryMap where K: EntityRef, @@ -337,6 +306,15 @@ where } } +impl From> for Vec +where + K: EntityRef, +{ + fn from(map: PrimaryMap) -> Self { + map.elems + } +} + impl fmt::Debug for PrimaryMap { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut struct_ = f.debug_struct("PrimaryMap"); @@ -557,14 +535,14 @@ mod tests { let _1 = m.push(1); let _2 = m.push(2); - assert_eq!([&mut 0, &mut 2], m.get_many_mut([_0, _2]).unwrap()); + assert_eq!([&mut 0, &mut 2], m.get_disjoint_mut([_0, _2]).unwrap()); assert_eq!( - m.get_many_mut([_0, _0]), - Err(GetManyMutError::MultipleOf(_0)) + m.get_disjoint_mut([_0, _0]), + Err(slice::GetDisjointMutError::OverlappingIndices) ); assert_eq!( - m.get_many_mut([E(4)]), - Err(GetManyMutError::DoesNotExist(E(4))) + m.get_disjoint_mut([E(4)]), + Err(slice::GetDisjointMutError::IndexOutOfBounds) ); } } diff --git a/cranelift/filetests/filetests/egraph/arithmetic.clif b/cranelift/filetests/filetests/egraph/arithmetic.clif index 362f94abdc..7fdd27f982 100644 --- a/cranelift/filetests/filetests/egraph/arithmetic.clif +++ b/cranelift/filetests/filetests/egraph/arithmetic.clif @@ -380,3 +380,14 @@ block0(v0: i32): ; check: v6 = band v0, v5 ; check: return v6 } + +;; (x + y) - (x | y) --> x & y +function %x_plus_y_minus_x_or_y(i32, i32) -> i32 { +block0(v0: i32, v1: i32): + v2 = iadd v0, v1 + v3 = bor v0, v1 + v4 = isub v2, v3 + return v4 + ; check: v5 = band v0, v1 + ; check: return v5 +} diff --git a/cranelift/filetests/filetests/inline/try-call.clif b/cranelift/filetests/filetests/inline/try-call.clif index 8a158edbc1..17b493cf20 100644 --- a/cranelift/filetests/filetests/inline/try-call.clif +++ b/cranelift/filetests/filetests/inline/try-call.clif @@ -336,3 +336,67 @@ block2(v3: i64): ; v6 = iadd v4, v5 ; v4 = 1 ; return v6 ; } + +function %f11(i32) -> i64 tail { + sig0 = (i32) -> i64 tail + fn0 = %foo(i32) -> i64 tail +block0(v0: i32): + v1 = iconst.i32 0 + v2 = iconst.i32 1 + try_call fn0(v0), sig0, block1(ret0, v2), [context v2, tag2: block1(exn0, v1)] +block1(v3: i64, v4: i32): + v5 = iconst.i64 0 + v6 = select v4, v3, v5 + return v6 +} + +; (no functions inlined into %f11) + +;; Inlining a `try_call` site whose callee contains a `try_call`, with +;; tables that have dynamic contexts and overlapping static tag indices. + +function %f12() -> i64 tail { + sig0 = (i32) -> i64 tail + fn0 = %f11(i32) -> i64 tail +block0: + v0 = iconst.i32 99 + try_call fn0(v0), sig0, block1(ret0), [context v0, tag1: block1(exn0)] +block1(v1: i64): + v2 = iconst.i64 1 + v3 = iadd v1, v2 + return v3 +block2(v4: i64): + return v4 +} + +; function %f12() -> i64 tail { +; sig0 = (i32) -> i64 tail +; sig1 = (i32) -> i64 tail +; sig2 = (i32) -> i64 tail +; sig3 = (i32) -> i64 tail +; fn0 = %f11 sig1 +; fn1 = %foo sig3 +; +; block0: +; v0 = iconst.i32 99 +; jump block3 +; +; block3: +; v7 = iconst.i32 0 +; v8 = iconst.i32 1 +; try_call fn1(v0), sig2, block4(ret0, v8), [ context v8, tag2: block4(exn0, v7), context v0, tag1: block1(exn0) ] ; v0 = 99, v7 = 0, v8 = 1, v8 = 1, v0 = 99 +; +; block4(v5: i64, v6: i32): +; v9 = iconst.i64 0 +; v10 = select v6, v5, v9 ; v9 = 0 +; jump block1(v10) +; +; block1(v1: i64): +; v2 = iconst.i64 1 +; v3 = iadd v1, v2 ; v2 = 1 +; return v3 +; +; block2(v4: i64): +; return v4 +; } + diff --git a/cranelift/filetests/filetests/inline/unreachable-block.clif b/cranelift/filetests/filetests/inline/unreachable-block.clif new file mode 100644 index 0000000000..e9bdc8944a --- /dev/null +++ b/cranelift/filetests/filetests/inline/unreachable-block.clif @@ -0,0 +1,46 @@ +test inline precise-output +target x86_64 + +function %f0(i32, i32) -> i32 { +block0(v0: i32, v1: i32): + v2 = iadd v0, v1 + return v2 + +;; This block is unreachable, despite it being in the function's layout! We +;; should not include this block in callers' layouts when inlining because we +;; will skip copying its instructions over into the caller due to how we use a +;; reachability-based DFS traversal when inlining instructions. Ideally, we +;; wouldn't even include it in callers' entity maps at all, but that is not +;; required for correctness, while producing valid CLIF that does not have empty +;; blocks without terminators is. +block1: + trap user42 + +} + +; (no functions inlined into %f0) + +function %f1() -> i32 { + fn0 = %f0(i32, i32) -> i32 +block0(): + v0 = iconst.i32 10 + v1 = call fn0(v0, v0) + return v1 +} + +; function %f1() -> i32 fast { +; sig0 = (i32, i32) -> i32 fast +; fn0 = %f0 sig0 +; +; block0: +; v0 = iconst.i32 10 +; jump block1 +; +; block1: +; v3 = iadd.i32 v0, v0 ; v0 = 10, v0 = 10 +; jump block3(v3) +; +; block3(v2: i32): +; v1 -> v2 +; return v1 +; } diff --git a/cranelift/filetests/filetests/isa/aarch64/exceptions.clif b/cranelift/filetests/filetests/isa/aarch64/exceptions.clif index 604e9fbef5..670173d57e 100644 --- a/cranelift/filetests/filetests/isa/aarch64/exceptions.clif +++ b/cranelift/filetests/filetests/isa/aarch64/exceptions.clif @@ -37,7 +37,7 @@ function %f0(i32) -> i32, f32, f64 { ; fmov d1, #1 ; mov x2, x0 ; str q1, [sp] -; bl 0; b MachLabel(1); catch [None: MachLabel(2)] +; bl 0; b MachLabel(1); catch [default: MachLabel(2)] ; block1: ; movz w0, #1 ; ldr q1, [sp] @@ -159,7 +159,7 @@ function %f2(i32) -> i32, f32, f64 { ; str q1, [sp] ; load_ext_name x11, TestCase(%g)+0 ; mov x2, x0 -; blr x11; b MachLabel(1); catch [None: MachLabel(2)] +; blr x11; b MachLabel(1); catch [default: MachLabel(2)] ; block1: ; movz w0, #1 ; ldr q1, [sp] @@ -247,3 +247,153 @@ function %f2(i32) -> i32, f32, f64 { ; ldp x29, x30, [sp], #0x10 ; ret +function %f4(i64, i32) -> i32, f32, f64 { + sig0 = (i32) -> f32 tail + fn0 = %g(i32) -> f32 tail + + block0(v0: i64, v1: i32): + v2 = f64const 0x1.0 + try_call fn0(v1), sig0, block1(ret0, v2), [ context v0, tag0: block2(exn0), tag1: block2(exn0), context v1, tag0: block3 ] + + block1(v3: f32, v4: f64): + v5 = iconst.i32 1 + return v5, v3, v4 + + block2(v6: i64): + v7 = ireduce.i32 v6 + v8 = iadd_imm.i32 v7, 1 + v9 = f32const 0x0.0 + return v8, v9, v2 + + block3: + jump block2(v0) +} + +; VCode: +; stp fp, lr, [sp, #-16]! +; mov fp, sp +; stp x27, x28, [sp, #-16]! +; stp x25, x26, [sp, #-16]! +; stp x23, x24, [sp, #-16]! +; stp x21, x22, [sp, #-16]! +; stp x19, x20, [sp, #-16]! +; stp d14, d15, [sp, #-16]! +; stp d12, d13, [sp, #-16]! +; stp d10, d11, [sp, #-16]! +; stp d8, d9, [sp, #-16]! +; sub sp, sp, #32 +; block0: +; str x0, [sp, #8] +; fmov d1, #1 +; str q1, [sp, #16] +; load_ext_name x12, TestCase(%g)+0 +; mov x2, x1 +; str x1, [sp] +; blr x12; b MachLabel(3); catch [context stack1, tag0: MachLabel(1), tag1: MachLabel(2), context stack0, tag0: MachLabel(4)] +; block1: +; ldr q1, [sp, #16] +; b label5 +; block2: +; ldr q1, [sp, #16] +; b label5 +; block3: +; movz w0, #1 +; ldr q1, [sp, #16] +; add sp, sp, #32 +; ldp d8, d9, [sp], #16 +; ldp d10, d11, [sp], #16 +; ldp d12, d13, [sp], #16 +; ldp d14, d15, [sp], #16 +; ldp x19, x20, [sp], #16 +; ldp x21, x22, [sp], #16 +; ldp x23, x24, [sp], #16 +; ldp x25, x26, [sp], #16 +; ldp x27, x28, [sp], #16 +; ldp fp, lr, [sp], #16 +; ret +; block4: +; ldr q1, [sp, #16] +; ldr x0, [sp, #8] +; b label5 +; block5: +; add w0, w0, #1 +; movi v0.2s, #0 +; add sp, sp, #32 +; ldp d8, d9, [sp], #16 +; ldp d10, d11, [sp], #16 +; ldp d12, d13, [sp], #16 +; ldp d14, d15, [sp], #16 +; ldp x19, x20, [sp], #16 +; ldp x21, x22, [sp], #16 +; ldp x23, x24, [sp], #16 +; ldp x25, x26, [sp], #16 +; ldp x27, x28, [sp], #16 +; ldp fp, lr, [sp], #16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; stp x29, x30, [sp, #-0x10]! +; mov x29, sp +; stp x27, x28, [sp, #-0x10]! +; stp x25, x26, [sp, #-0x10]! +; stp x23, x24, [sp, #-0x10]! +; stp x21, x22, [sp, #-0x10]! +; stp x19, x20, [sp, #-0x10]! +; stp d14, d15, [sp, #-0x10]! +; stp d12, d13, [sp, #-0x10]! +; stp d10, d11, [sp, #-0x10]! +; stp d8, d9, [sp, #-0x10]! +; sub sp, sp, #0x20 +; block1: ; offset 0x30 +; stur x0, [sp, #8] +; fmov d1, #1.00000000 +; stur q1, [sp, #0x10] +; ldr x12, #0x44 +; b #0x4c +; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %g 0 +; .byte 0x00, 0x00, 0x00, 0x00 +; mov x2, x1 +; stur x1, [sp] +; blr x12 +; b #0x6c +; block2: ; offset 0x5c +; ldur q1, [sp, #0x10] +; b #0xac +; block3: ; offset 0x64 +; ldur q1, [sp, #0x10] +; b #0xac +; block4: ; offset 0x6c +; mov w0, #1 +; ldur q1, [sp, #0x10] +; add sp, sp, #0x20 +; ldp d8, d9, [sp], #0x10 +; ldp d10, d11, [sp], #0x10 +; ldp d12, d13, [sp], #0x10 +; ldp d14, d15, [sp], #0x10 +; ldp x19, x20, [sp], #0x10 +; ldp x21, x22, [sp], #0x10 +; ldp x23, x24, [sp], #0x10 +; ldp x25, x26, [sp], #0x10 +; ldp x27, x28, [sp], #0x10 +; ldp x29, x30, [sp], #0x10 +; ret +; block5: ; offset 0xa4 +; ldur q1, [sp, #0x10] +; ldur x0, [sp, #8] +; block6: ; offset 0xac +; add w0, w0, #1 +; movi v0.2s, #0 +; add sp, sp, #0x20 +; ldp d8, d9, [sp], #0x10 +; ldp d10, d11, [sp], #0x10 +; ldp d12, d13, [sp], #0x10 +; ldp d14, d15, [sp], #0x10 +; ldp x19, x20, [sp], #0x10 +; ldp x21, x22, [sp], #0x10 +; ldp x23, x24, [sp], #0x10 +; ldp x25, x26, [sp], #0x10 +; ldp x27, x28, [sp], #0x10 +; ldp x29, x30, [sp], #0x10 +; ret + diff --git a/cranelift/filetests/filetests/isa/aarch64/no_spill_floats_on_try_call.clif b/cranelift/filetests/filetests/isa/aarch64/no_spill_floats_on_try_call.clif index ca221b2bb1..961db90cbc 100644 --- a/cranelift/filetests/filetests/isa/aarch64/no_spill_floats_on_try_call.clif +++ b/cranelift/filetests/filetests/isa/aarch64/no_spill_floats_on_try_call.clif @@ -49,7 +49,7 @@ block2(v0: i64): ; stp fp, lr, [sp, #-16]! ; mov fp, sp ; block0: -; bl 0; b MachLabel(1); catch [Some(tag0): MachLabel(2)] +; bl 0; b MachLabel(1); catch [tag0: MachLabel(2)] ; block1: ; ldp fp, lr, [sp], #16 ; ret @@ -67,3 +67,4 @@ block2(v0: i64): ; ret ; block3: ; offset 0x14 ; .byte 0x1f, 0xc1, 0x00, 0x00 ; trap: user1 + diff --git a/cranelift/filetests/filetests/isa/pulley32/exceptions.clif b/cranelift/filetests/filetests/isa/pulley32/exceptions.clif index 40f643708c..d920bd500e 100644 --- a/cranelift/filetests/filetests/isa/pulley32/exceptions.clif +++ b/cranelift/filetests/filetests/isa/pulley32/exceptions.clif @@ -40,7 +40,7 @@ function %f0(i32) -> i32, f32, f64 { ; block0: ; fconst64 f1, 4607182418800017408 ; fstore64 Slot(0), f1 // flags = notrap aligned -; call CallInfo { dest: PulleyCall { name: TestCase(%g), args: [XReg(p0i)] }, uses: [], defs: [CallRetPair { vreg: Writable { reg: p0f }, location: Reg(p0f, types::F32) }, CallRetPair { vreg: Writable { reg: p0i }, location: Reg(p0i, types::I32) }, CallRetPair { vreg: Writable { reg: p1i }, location: Reg(p1i, types::I32) }], clobbers: PRegSet { bits: [4294967292, 4294967294, 4294967295, 0] }, callee_conv: Tail, caller_conv: Fast, callee_pop_size: 0, try_call_info: Some(TryCallInfo { continuation: MachLabel(1), exception_dests: [(None, MachLabel(2))] }) }; jump MachLabel(1); catch [None: MachLabel(2)] +; call CallInfo { dest: PulleyCall { name: TestCase(%g), args: [XReg(p0i)] }, uses: [], defs: [CallRetPair { vreg: Writable { reg: p0f }, location: Reg(p0f, types::F32) }, CallRetPair { vreg: Writable { reg: p0i }, location: Reg(p0i, types::I32) }, CallRetPair { vreg: Writable { reg: p1i }, location: Reg(p1i, types::I32) }], clobbers: PRegSet { bits: [4294967292, 4294967294, 4294967295, 0] }, callee_conv: Tail, caller_conv: Fast, callee_pop_size: 0, try_call_info: Some(TryCallInfo { continuation: MachLabel(1), exception_handlers: [Default(MachLabel(2))] }) }; jump MachLabel(1); catch [default: MachLabel(2)] ; block1: ; xone x0 ; f1 = fload64 Slot(0) // flags = notrap aligned @@ -187,7 +187,7 @@ function %f2(i32, i32) -> i32, f32, f64 { ; block0: ; fconst64 f1, 4607182418800017408 ; fstore64 Slot(0), f1 // flags = notrap aligned -; indirect_call x1, CallInfo { dest: XReg(p1i), uses: [CallArgPair { vreg: p0i, preg: p0i }], defs: [CallRetPair { vreg: Writable { reg: p0f }, location: Reg(p0f, types::F32) }, CallRetPair { vreg: Writable { reg: p0i }, location: Reg(p0i, types::I32) }, CallRetPair { vreg: Writable { reg: p1i }, location: Reg(p1i, types::I32) }], clobbers: PRegSet { bits: [4294967292, 4294967294, 4294967295, 0] }, callee_conv: Tail, caller_conv: Fast, callee_pop_size: 0, try_call_info: Some(TryCallInfo { continuation: MachLabel(1), exception_dests: [(None, MachLabel(2))] }) }; jump MachLabel(1); catch [None: MachLabel(2)] +; indirect_call x1, CallInfo { dest: XReg(p1i), uses: [CallArgPair { vreg: p0i, preg: p0i }], defs: [CallRetPair { vreg: Writable { reg: p0f }, location: Reg(p0f, types::F32) }, CallRetPair { vreg: Writable { reg: p0i }, location: Reg(p0i, types::I32) }, CallRetPair { vreg: Writable { reg: p1i }, location: Reg(p1i, types::I32) }], clobbers: PRegSet { bits: [4294967292, 4294967294, 4294967295, 0] }, callee_conv: Tail, caller_conv: Fast, callee_pop_size: 0, try_call_info: Some(TryCallInfo { continuation: MachLabel(1), exception_handlers: [Default(MachLabel(2))] }) }; jump MachLabel(1); catch [default: MachLabel(2)] ; block1: ; xone x0 ; f1 = fload64 Slot(0) // flags = notrap aligned @@ -295,3 +295,178 @@ function %f2(i32, i32) -> i32, f32, f64 { ; pop_frame_restore 272, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, sp, spilltmp0 ; ret +function %f4(i32, i32) -> i32, f32, f64 { + sig0 = (i32) -> f32 tail + fn0 = colocated %g(i32) -> f32 tail + + block0(v0: i32, v1: i32): + v2 = f64const 0x1.0 + try_call fn0(v1), sig0, block1(ret0, v2), [ context v0, tag0: block2(exn0), tag1: block2(exn0), context v1, tag0: block3 ] + + block1(v3: f32, v4: f64): + v5 = iconst.i32 1 + return v5, v3, v4 + + block2(v6: i32): + v8 = iadd_imm.i32 v6, 1 + v9 = f32const 0x0.0 + return v8, v9, v2 + + block3: + jump block2(v0) +} + +; VCode: +; push_frame_save 288, {x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, sp, spilltmp0} +; fstore64 sp+152, f16 // flags = notrap aligned +; fstore64 sp+144, f17 // flags = notrap aligned +; fstore64 sp+136, f18 // flags = notrap aligned +; fstore64 sp+128, f19 // flags = notrap aligned +; fstore64 sp+120, f20 // flags = notrap aligned +; fstore64 sp+112, f21 // flags = notrap aligned +; fstore64 sp+104, f22 // flags = notrap aligned +; fstore64 sp+96, f23 // flags = notrap aligned +; fstore64 sp+88, f24 // flags = notrap aligned +; fstore64 sp+80, f25 // flags = notrap aligned +; fstore64 sp+72, f26 // flags = notrap aligned +; fstore64 sp+64, f27 // flags = notrap aligned +; fstore64 sp+56, f28 // flags = notrap aligned +; fstore64 sp+48, f29 // flags = notrap aligned +; fstore64 sp+40, f30 // flags = notrap aligned +; fstore64 sp+32, f31 // flags = notrap aligned +; block0: +; xstore64 Slot(0), x1 // flags = notrap aligned +; xstore64 Slot(8), x0 // flags = notrap aligned +; fconst64 f1, 4607182418800017408 +; x2 = xload64 Slot(0) // flags = notrap aligned +; fstore64 Slot(16), f1 // flags = notrap aligned +; call CallInfo { dest: PulleyCall { name: TestCase(%g), args: [XReg(p2i)] }, uses: [], defs: [CallRetPair { vreg: Writable { reg: p0f }, location: Reg(p0f, types::F32) }, CallRetPair { vreg: Writable { reg: p0i }, location: Reg(p0i, types::I32) }, CallRetPair { vreg: Writable { reg: p1i }, location: Reg(p1i, types::I32) }], clobbers: PRegSet { bits: [4294967292, 4294967294, 4294967295, 0] }, callee_conv: Tail, caller_conv: Fast, callee_pop_size: 0, try_call_info: Some(TryCallInfo { continuation: MachLabel(3), exception_handlers: [Context(stack2), Tag(tag0, MachLabel(1)), Tag(tag1, MachLabel(2)), Context(stack0), Tag(tag0, MachLabel(4))] }) }; jump MachLabel(3); catch [context stack2, tag0: MachLabel(1), tag1: MachLabel(2), context stack0, tag0: MachLabel(4)] +; block1: +; xmov x3, x0 +; f1 = fload64 Slot(16) // flags = notrap aligned +; jump label5 +; block2: +; xmov x3, x0 +; f1 = fload64 Slot(16) // flags = notrap aligned +; jump label5 +; block3: +; xone x0 +; f1 = fload64 Slot(16) // flags = notrap aligned +; f16 = fload64 sp+152 // flags = notrap aligned +; f17 = fload64 sp+144 // flags = notrap aligned +; f18 = fload64 sp+136 // flags = notrap aligned +; f19 = fload64 sp+128 // flags = notrap aligned +; f20 = fload64 sp+120 // flags = notrap aligned +; f21 = fload64 sp+112 // flags = notrap aligned +; f22 = fload64 sp+104 // flags = notrap aligned +; f23 = fload64 sp+96 // flags = notrap aligned +; f24 = fload64 sp+88 // flags = notrap aligned +; f25 = fload64 sp+80 // flags = notrap aligned +; f26 = fload64 sp+72 // flags = notrap aligned +; f27 = fload64 sp+64 // flags = notrap aligned +; f28 = fload64 sp+56 // flags = notrap aligned +; f29 = fload64 sp+48 // flags = notrap aligned +; f30 = fload64 sp+40 // flags = notrap aligned +; f31 = fload64 sp+32 // flags = notrap aligned +; pop_frame_restore 288, {x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, sp, spilltmp0} +; ret +; block4: +; f1 = fload64 Slot(16) // flags = notrap aligned +; x3 = xload64 Slot(8) // flags = notrap aligned +; jump label5 +; block5: +; xadd32_u8 x0, x3, 1 +; fconst32 f0, 0 +; f16 = fload64 sp+152 // flags = notrap aligned +; f17 = fload64 sp+144 // flags = notrap aligned +; f18 = fload64 sp+136 // flags = notrap aligned +; f19 = fload64 sp+128 // flags = notrap aligned +; f20 = fload64 sp+120 // flags = notrap aligned +; f21 = fload64 sp+112 // flags = notrap aligned +; f22 = fload64 sp+104 // flags = notrap aligned +; f23 = fload64 sp+96 // flags = notrap aligned +; f24 = fload64 sp+88 // flags = notrap aligned +; f25 = fload64 sp+80 // flags = notrap aligned +; f26 = fload64 sp+72 // flags = notrap aligned +; f27 = fload64 sp+64 // flags = notrap aligned +; f28 = fload64 sp+56 // flags = notrap aligned +; f29 = fload64 sp+48 // flags = notrap aligned +; f30 = fload64 sp+40 // flags = notrap aligned +; f31 = fload64 sp+32 // flags = notrap aligned +; pop_frame_restore 288, {x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, sp, spilltmp0} +; ret +; +; Disassembled: +; push_frame_save 288, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, sp, spilltmp0 +; fstore64le_o32 sp, 152, f16 +; fstore64le_o32 sp, 144, f17 +; fstore64le_o32 sp, 136, f18 +; fstore64le_o32 sp, 128, f19 +; fstore64le_o32 sp, 120, f20 +; fstore64le_o32 sp, 112, f21 +; fstore64le_o32 sp, 104, f22 +; fstore64le_o32 sp, 96, f23 +; fstore64le_o32 sp, 88, f24 +; fstore64le_o32 sp, 80, f25 +; fstore64le_o32 sp, 72, f26 +; fstore64le_o32 sp, 64, f27 +; fstore64le_o32 sp, 56, f28 +; fstore64le_o32 sp, 48, f29 +; fstore64le_o32 sp, 40, f30 +; fstore64le_o32 sp, 32, f31 +; xstore64le_o32 sp, 0, x1 +; xstore64le_o32 sp, 8, x0 +; fconst64 f1, 4607182418800017408 +; xload64le_o32 x2, sp, 0 +; fstore64le_o32 sp, 16, f1 +; call1 x2, 0x0 // target = 0xbf +; jump 0x27 // target = 0xec +; xmov x3, x0 +; fload64le_o32 f1, sp, 16 +; jump 0xc7 // target = 0x19d +; xmov x3, x0 +; fload64le_o32 f1, sp, 16 +; jump 0xb6 // target = 0x19d +; xone x0 +; fload64le_o32 f1, sp, 16 +; fload64le_o32 f16, sp, 152 +; fload64le_o32 f17, sp, 144 +; fload64le_o32 f18, sp, 136 +; fload64le_o32 f19, sp, 128 +; fload64le_o32 f20, sp, 120 +; fload64le_o32 f21, sp, 112 +; fload64le_o32 f22, sp, 104 +; fload64le_o32 f23, sp, 96 +; fload64le_o32 f24, sp, 88 +; fload64le_o32 f25, sp, 80 +; fload64le_o32 f26, sp, 72 +; fload64le_o32 f27, sp, 64 +; fload64le_o32 f28, sp, 56 +; fload64le_o32 f29, sp, 48 +; fload64le_o32 f30, sp, 40 +; fload64le_o32 f31, sp, 32 +; pop_frame_restore 288, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, sp, spilltmp0 +; ret +; fload64le_o32 f1, sp, 16 +; xload64le_o32 x3, sp, 8 +; xadd32_u8 x0, x3, 1 +; fconst32 f0, 0 +; fload64le_o32 f16, sp, 152 +; fload64le_o32 f17, sp, 144 +; fload64le_o32 f18, sp, 136 +; fload64le_o32 f19, sp, 128 +; fload64le_o32 f20, sp, 120 +; fload64le_o32 f21, sp, 112 +; fload64le_o32 f22, sp, 104 +; fload64le_o32 f23, sp, 96 +; fload64le_o32 f24, sp, 88 +; fload64le_o32 f25, sp, 80 +; fload64le_o32 f26, sp, 72 +; fload64le_o32 f27, sp, 64 +; fload64le_o32 f28, sp, 56 +; fload64le_o32 f29, sp, 48 +; fload64le_o32 f30, sp, 40 +; fload64le_o32 f31, sp, 32 +; pop_frame_restore 288, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, sp, spilltmp0 +; ret + diff --git a/cranelift/filetests/filetests/isa/pulley64/exceptions.clif b/cranelift/filetests/filetests/isa/pulley64/exceptions.clif index 2f03e53b12..2180627201 100644 --- a/cranelift/filetests/filetests/isa/pulley64/exceptions.clif +++ b/cranelift/filetests/filetests/isa/pulley64/exceptions.clif @@ -41,7 +41,7 @@ function %f0(i32) -> i32, f32, f64 { ; block0: ; fconst64 f1, 4607182418800017408 ; fstore64 Slot(0), f1 // flags = notrap aligned -; call CallInfo { dest: PulleyCall { name: TestCase(%g), args: [XReg(p0i)] }, uses: [], defs: [CallRetPair { vreg: Writable { reg: p0f }, location: Reg(p0f, types::F32) }, CallRetPair { vreg: Writable { reg: p0i }, location: Reg(p0i, types::I64) }, CallRetPair { vreg: Writable { reg: p1i }, location: Reg(p1i, types::I64) }], clobbers: PRegSet { bits: [4294967292, 4294967294, 4294967295, 0] }, callee_conv: Tail, caller_conv: Fast, callee_pop_size: 0, try_call_info: Some(TryCallInfo { continuation: MachLabel(1), exception_dests: [(None, MachLabel(2))] }) }; jump MachLabel(1); catch [None: MachLabel(2)] +; call CallInfo { dest: PulleyCall { name: TestCase(%g), args: [XReg(p0i)] }, uses: [], defs: [CallRetPair { vreg: Writable { reg: p0f }, location: Reg(p0f, types::F32) }, CallRetPair { vreg: Writable { reg: p0i }, location: Reg(p0i, types::I64) }, CallRetPair { vreg: Writable { reg: p1i }, location: Reg(p1i, types::I64) }], clobbers: PRegSet { bits: [4294967292, 4294967294, 4294967295, 0] }, callee_conv: Tail, caller_conv: Fast, callee_pop_size: 0, try_call_info: Some(TryCallInfo { continuation: MachLabel(1), exception_handlers: [Default(MachLabel(2))] }) }; jump MachLabel(1); catch [default: MachLabel(2)] ; block1: ; xone x0 ; f1 = fload64 Slot(0) // flags = notrap aligned @@ -189,7 +189,7 @@ function %f2(i32, i64) -> i32, f32, f64 { ; block0: ; fconst64 f1, 4607182418800017408 ; fstore64 Slot(0), f1 // flags = notrap aligned -; indirect_call x1, CallInfo { dest: XReg(p1i), uses: [CallArgPair { vreg: p0i, preg: p0i }], defs: [CallRetPair { vreg: Writable { reg: p0f }, location: Reg(p0f, types::F32) }, CallRetPair { vreg: Writable { reg: p0i }, location: Reg(p0i, types::I64) }, CallRetPair { vreg: Writable { reg: p1i }, location: Reg(p1i, types::I64) }], clobbers: PRegSet { bits: [4294967292, 4294967294, 4294967295, 0] }, callee_conv: Tail, caller_conv: Fast, callee_pop_size: 0, try_call_info: Some(TryCallInfo { continuation: MachLabel(1), exception_dests: [(None, MachLabel(2))] }) }; jump MachLabel(1); catch [None: MachLabel(2)] +; indirect_call x1, CallInfo { dest: XReg(p1i), uses: [CallArgPair { vreg: p0i, preg: p0i }], defs: [CallRetPair { vreg: Writable { reg: p0f }, location: Reg(p0f, types::F32) }, CallRetPair { vreg: Writable { reg: p0i }, location: Reg(p0i, types::I64) }, CallRetPair { vreg: Writable { reg: p1i }, location: Reg(p1i, types::I64) }], clobbers: PRegSet { bits: [4294967292, 4294967294, 4294967295, 0] }, callee_conv: Tail, caller_conv: Fast, callee_pop_size: 0, try_call_info: Some(TryCallInfo { continuation: MachLabel(1), exception_handlers: [Default(MachLabel(2))] }) }; jump MachLabel(1); catch [default: MachLabel(2)] ; block1: ; xone x0 ; f1 = fload64 Slot(0) // flags = notrap aligned @@ -297,3 +297,178 @@ function %f2(i32, i64) -> i32, f32, f64 { ; pop_frame_restore 272, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, sp, spilltmp0 ; ret +function %f4(i64, i32) -> i32, f32, f64 { + sig0 = (i32) -> f32 tail + fn0 = %g(i32) -> f32 tail + + block0(v0: i64, v1: i32): + v2 = f64const 0x1.0 + try_call fn0(v1), sig0, block1(ret0, v2), [ context v0, tag0: block2(exn0), tag1: block2(exn0), context v1, tag0: block3 ] + + block1(v3: f32, v4: f64): + v5 = iconst.i32 1 + return v5, v3, v4 + + block2(v6: i64): + v7 = ireduce.i32 v6 + v8 = iadd_imm.i32 v7, 1 + v9 = f32const 0x0.0 + return v8, v9, v2 + + block3: + jump block2(v0) +} + +; VCode: +; push_frame_save 288, {x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, sp, spilltmp0} +; fstore64 sp+152, f16 // flags = notrap aligned +; fstore64 sp+144, f17 // flags = notrap aligned +; fstore64 sp+136, f18 // flags = notrap aligned +; fstore64 sp+128, f19 // flags = notrap aligned +; fstore64 sp+120, f20 // flags = notrap aligned +; fstore64 sp+112, f21 // flags = notrap aligned +; fstore64 sp+104, f22 // flags = notrap aligned +; fstore64 sp+96, f23 // flags = notrap aligned +; fstore64 sp+88, f24 // flags = notrap aligned +; fstore64 sp+80, f25 // flags = notrap aligned +; fstore64 sp+72, f26 // flags = notrap aligned +; fstore64 sp+64, f27 // flags = notrap aligned +; fstore64 sp+56, f28 // flags = notrap aligned +; fstore64 sp+48, f29 // flags = notrap aligned +; fstore64 sp+40, f30 // flags = notrap aligned +; fstore64 sp+32, f31 // flags = notrap aligned +; block0: +; xstore64 Slot(8), x0 // flags = notrap aligned +; fconst64 f1, 4607182418800017408 +; xmov x0, x1 +; xstore64 Slot(0), x1 // flags = notrap aligned +; fstore64 Slot(16), f1 // flags = notrap aligned +; indirect_call_host CallInfo { dest: TestCase(%g), uses: [CallArgPair { vreg: p0i, preg: p0i }], defs: [CallRetPair { vreg: Writable { reg: p0f }, location: Reg(p0f, types::F32) }, CallRetPair { vreg: Writable { reg: p0i }, location: Reg(p0i, types::I64) }, CallRetPair { vreg: Writable { reg: p1i }, location: Reg(p1i, types::I64) }], clobbers: PRegSet { bits: [4294967292, 4294967294, 4294967295, 0] }, callee_conv: Tail, caller_conv: Fast, callee_pop_size: 0, try_call_info: Some(TryCallInfo { continuation: MachLabel(3), exception_handlers: [Context(stack1), Tag(tag0, MachLabel(1)), Tag(tag1, MachLabel(2)), Context(stack0), Tag(tag0, MachLabel(4))] }) }; jump MachLabel(3); catch [context stack1, tag0: MachLabel(1), tag1: MachLabel(2), context stack0, tag0: MachLabel(4)] +; block1: +; xmov x2, x0 +; f1 = fload64 Slot(16) // flags = notrap aligned +; jump label5 +; block2: +; xmov x2, x0 +; f1 = fload64 Slot(16) // flags = notrap aligned +; jump label5 +; block3: +; xone x0 +; f1 = fload64 Slot(16) // flags = notrap aligned +; f16 = fload64 sp+152 // flags = notrap aligned +; f17 = fload64 sp+144 // flags = notrap aligned +; f18 = fload64 sp+136 // flags = notrap aligned +; f19 = fload64 sp+128 // flags = notrap aligned +; f20 = fload64 sp+120 // flags = notrap aligned +; f21 = fload64 sp+112 // flags = notrap aligned +; f22 = fload64 sp+104 // flags = notrap aligned +; f23 = fload64 sp+96 // flags = notrap aligned +; f24 = fload64 sp+88 // flags = notrap aligned +; f25 = fload64 sp+80 // flags = notrap aligned +; f26 = fload64 sp+72 // flags = notrap aligned +; f27 = fload64 sp+64 // flags = notrap aligned +; f28 = fload64 sp+56 // flags = notrap aligned +; f29 = fload64 sp+48 // flags = notrap aligned +; f30 = fload64 sp+40 // flags = notrap aligned +; f31 = fload64 sp+32 // flags = notrap aligned +; pop_frame_restore 288, {x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, sp, spilltmp0} +; ret +; block4: +; f1 = fload64 Slot(16) // flags = notrap aligned +; x2 = xload64 Slot(8) // flags = notrap aligned +; jump label5 +; block5: +; xadd32_u8 x0, x2, 1 +; fconst32 f0, 0 +; f16 = fload64 sp+152 // flags = notrap aligned +; f17 = fload64 sp+144 // flags = notrap aligned +; f18 = fload64 sp+136 // flags = notrap aligned +; f19 = fload64 sp+128 // flags = notrap aligned +; f20 = fload64 sp+120 // flags = notrap aligned +; f21 = fload64 sp+112 // flags = notrap aligned +; f22 = fload64 sp+104 // flags = notrap aligned +; f23 = fload64 sp+96 // flags = notrap aligned +; f24 = fload64 sp+88 // flags = notrap aligned +; f25 = fload64 sp+80 // flags = notrap aligned +; f26 = fload64 sp+72 // flags = notrap aligned +; f27 = fload64 sp+64 // flags = notrap aligned +; f28 = fload64 sp+56 // flags = notrap aligned +; f29 = fload64 sp+48 // flags = notrap aligned +; f30 = fload64 sp+40 // flags = notrap aligned +; f31 = fload64 sp+32 // flags = notrap aligned +; pop_frame_restore 288, {x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, sp, spilltmp0} +; ret +; +; Disassembled: +; push_frame_save 288, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, sp, spilltmp0 +; fstore64le_o32 sp, 152, f16 +; fstore64le_o32 sp, 144, f17 +; fstore64le_o32 sp, 136, f18 +; fstore64le_o32 sp, 128, f19 +; fstore64le_o32 sp, 120, f20 +; fstore64le_o32 sp, 112, f21 +; fstore64le_o32 sp, 104, f22 +; fstore64le_o32 sp, 96, f23 +; fstore64le_o32 sp, 88, f24 +; fstore64le_o32 sp, 80, f25 +; fstore64le_o32 sp, 72, f26 +; fstore64le_o32 sp, 64, f27 +; fstore64le_o32 sp, 56, f28 +; fstore64le_o32 sp, 48, f29 +; fstore64le_o32 sp, 40, f30 +; fstore64le_o32 sp, 32, f31 +; xstore64le_o32 sp, 8, x0 +; fconst64 f1, 4607182418800017408 +; xmov x0, x1 +; xstore64le_o32 sp, 0, x1 +; fstore64le_o32 sp, 16, f1 +; call_indirect_host 0 +; xmov x2, x0 +; fload64le_o32 f1, sp, 16 +; jump 0xc7 // target = 0x192 +; xmov x2, x0 +; fload64le_o32 f1, sp, 16 +; jump 0xb6 // target = 0x192 +; xone x0 +; fload64le_o32 f1, sp, 16 +; fload64le_o32 f16, sp, 152 +; fload64le_o32 f17, sp, 144 +; fload64le_o32 f18, sp, 136 +; fload64le_o32 f19, sp, 128 +; fload64le_o32 f20, sp, 120 +; fload64le_o32 f21, sp, 112 +; fload64le_o32 f22, sp, 104 +; fload64le_o32 f23, sp, 96 +; fload64le_o32 f24, sp, 88 +; fload64le_o32 f25, sp, 80 +; fload64le_o32 f26, sp, 72 +; fload64le_o32 f27, sp, 64 +; fload64le_o32 f28, sp, 56 +; fload64le_o32 f29, sp, 48 +; fload64le_o32 f30, sp, 40 +; fload64le_o32 f31, sp, 32 +; pop_frame_restore 288, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, sp, spilltmp0 +; ret +; fload64le_o32 f1, sp, 16 +; xload64le_o32 x2, sp, 8 +; xadd32_u8 x0, x2, 1 +; fconst32 f0, 0 +; fload64le_o32 f16, sp, 152 +; fload64le_o32 f17, sp, 144 +; fload64le_o32 f18, sp, 136 +; fload64le_o32 f19, sp, 128 +; fload64le_o32 f20, sp, 120 +; fload64le_o32 f21, sp, 112 +; fload64le_o32 f22, sp, 104 +; fload64le_o32 f23, sp, 96 +; fload64le_o32 f24, sp, 88 +; fload64le_o32 f25, sp, 80 +; fload64le_o32 f26, sp, 72 +; fload64le_o32 f27, sp, 64 +; fload64le_o32 f28, sp, 56 +; fload64le_o32 f29, sp, 48 +; fload64le_o32 f30, sp, 40 +; fload64le_o32 f31, sp, 32 +; pop_frame_restore 288, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, sp, spilltmp0 +; ret + diff --git a/cranelift/filetests/filetests/isa/riscv64/exceptions.clif b/cranelift/filetests/filetests/isa/riscv64/exceptions.clif index 89b2da54b2..e80453fc37 100644 --- a/cranelift/filetests/filetests/isa/riscv64/exceptions.clif +++ b/cranelift/filetests/filetests/isa/riscv64/exceptions.clif @@ -54,7 +54,7 @@ function %f0(i32) -> i32, f32, f64 { ; slli a1,a4,40 ; fmv.d.x fa1,a1 ; fsd fa1,0(slot) -; call %g; j MachLabel(1); catch [None: MachLabel(2)] +; call %g; j MachLabel(1); catch [default: MachLabel(2)] ; block1: ; li a0,1 ; fld fa1,0(slot) @@ -275,7 +275,7 @@ function %f2(i32) -> i32, f32, f64 { ; fmv.d.x fa1,a1 ; fsd fa1,0(slot) ; load_sym a1,%g+0 -; callind a1; j MachLabel(1); catch [None: MachLabel(2)] +; callind a1; j MachLabel(1); catch [default: MachLabel(2)] ; block1: ; li a0,1 ; fld fa1,0(slot) @@ -445,3 +445,254 @@ function %f2(i32) -> i32, f32, f64 { ; addi sp, sp, 0x10 ; ret +function %f4(i64, i32) -> i32, f32, f64 { + sig0 = (i32) -> f32 tail + fn0 = %g(i32) -> f32 tail + + block0(v0: i64, v1: i32): + v2 = f64const 0x1.0 + try_call fn0(v1), sig0, block1(ret0, v2), [ context v0, tag0: block2(exn0), tag1: block2(exn0), context v1, tag0: block3 ] + + block1(v3: f32, v4: f64): + v5 = iconst.i32 1 + return v5, v3, v4 + + block2(v6: i64): + v7 = ireduce.i32 v6 + v8 = iadd_imm.i32 v7, 1 + v9 = f32const 0x0.0 + return v8, v9, v2 + + block3: + jump block2(v0) +} + +; VCode: +; addi sp,sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; addi sp,sp,-224 +; sd fp,216(sp) +; sd s1,208(sp) +; sd s2,200(sp) +; sd s3,192(sp) +; sd s4,184(sp) +; sd s5,176(sp) +; sd s6,168(sp) +; sd s7,160(sp) +; sd s8,152(sp) +; sd s9,144(sp) +; sd s10,136(sp) +; sd s11,128(sp) +; fsd fs0,120(sp) +; fsd fs2,112(sp) +; fsd fs3,104(sp) +; fsd fs4,96(sp) +; fsd fs5,88(sp) +; fsd fs6,80(sp) +; fsd fs7,72(sp) +; fsd fs8,64(sp) +; fsd fs9,56(sp) +; fsd fs10,48(sp) +; fsd fs11,40(sp) +; block0: +; sd a0,8(slot) +; lui a0,1023 +; slli a2,a0,40 +; fmv.d.x fa1,a2 +; fsd fa1,16(slot) +; load_sym a2,%g+0 +; mv a0,a1 +; sd a1,0(slot) +; callind a2; j MachLabel(3); catch [context stack1, tag0: MachLabel(1), tag1: MachLabel(2), context stack0, tag0: MachLabel(4)] +; block1: +; fld fa1,16(slot) +; j label5 +; block2: +; fld fa1,16(slot) +; j label5 +; block3: +; li a0,1 +; fld fa1,16(slot) +; ld fp,216(sp) +; ld s1,208(sp) +; ld s2,200(sp) +; ld s3,192(sp) +; ld s4,184(sp) +; ld s5,176(sp) +; ld s6,168(sp) +; ld s7,160(sp) +; ld s8,152(sp) +; ld s9,144(sp) +; ld s10,136(sp) +; ld s11,128(sp) +; fld fs0,120(sp) +; fld fs2,112(sp) +; fld fs3,104(sp) +; fld fs4,96(sp) +; fld fs5,88(sp) +; fld fs6,80(sp) +; fld fs7,72(sp) +; fld fs8,64(sp) +; fld fs9,56(sp) +; fld fs10,48(sp) +; fld fs11,40(sp) +; addi sp,sp,224 +; ld ra,8(sp) +; ld fp,0(sp) +; addi sp,sp,16 +; ret +; block4: +; fld fa1,16(slot) +; ld a0,8(slot) +; j label5 +; block5: +; addiw a0,a0,1 +; fmv.w.x fa0,zero +; ld fp,216(sp) +; ld s1,208(sp) +; ld s2,200(sp) +; ld s3,192(sp) +; ld s4,184(sp) +; ld s5,176(sp) +; ld s6,168(sp) +; ld s7,160(sp) +; ld s8,152(sp) +; ld s9,144(sp) +; ld s10,136(sp) +; ld s11,128(sp) +; fld fs0,120(sp) +; fld fs2,112(sp) +; fld fs3,104(sp) +; fld fs4,96(sp) +; fld fs5,88(sp) +; fld fs6,80(sp) +; fld fs7,72(sp) +; fld fs8,64(sp) +; fld fs9,56(sp) +; fld fs10,48(sp) +; fld fs11,40(sp) +; addi sp,sp,224 +; ld ra,8(sp) +; ld fp,0(sp) +; addi sp,sp,16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; addi sp, sp, -0xe0 +; sd s0, 0xd8(sp) +; sd s1, 0xd0(sp) +; sd s2, 0xc8(sp) +; sd s3, 0xc0(sp) +; sd s4, 0xb8(sp) +; sd s5, 0xb0(sp) +; sd s6, 0xa8(sp) +; sd s7, 0xa0(sp) +; sd s8, 0x98(sp) +; sd s9, 0x90(sp) +; sd s10, 0x88(sp) +; sd s11, 0x80(sp) +; fsd fs0, 0x78(sp) +; fsd fs2, 0x70(sp) +; fsd fs3, 0x68(sp) +; fsd fs4, 0x60(sp) +; fsd fs5, 0x58(sp) +; fsd fs6, 0x50(sp) +; fsd fs7, 0x48(sp) +; fsd fs8, 0x40(sp) +; fsd fs9, 0x38(sp) +; fsd fs10, 0x30(sp) +; fsd fs11, 0x28(sp) +; block1: ; offset 0x70 +; sd a0, 8(sp) +; lui a0, 0x3ff +; slli a2, a0, 0x28 +; fmv.d.x fa1, a2 +; fsd fa1, 0x10(sp) +; auipc a2, 0 +; ld a2, 0xc(a2) +; j 0xc +; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %g 0 +; .byte 0x00, 0x00, 0x00, 0x00 +; mv a0, a1 +; sd a1, 0(sp) +; jalr a2 +; j 0x14 +; block2: ; offset 0xa8 +; fld fa1, 0x10(sp) +; j 0x8c +; block3: ; offset 0xb0 +; fld fa1, 0x10(sp) +; j 0x84 +; block4: ; offset 0xb8 +; addi a0, zero, 1 +; fld fa1, 0x10(sp) +; ld s0, 0xd8(sp) +; ld s1, 0xd0(sp) +; ld s2, 0xc8(sp) +; ld s3, 0xc0(sp) +; ld s4, 0xb8(sp) +; ld s5, 0xb0(sp) +; ld s6, 0xa8(sp) +; ld s7, 0xa0(sp) +; ld s8, 0x98(sp) +; ld s9, 0x90(sp) +; ld s10, 0x88(sp) +; ld s11, 0x80(sp) +; fld fs0, 0x78(sp) +; fld fs2, 0x70(sp) +; fld fs3, 0x68(sp) +; fld fs4, 0x60(sp) +; fld fs5, 0x58(sp) +; fld fs6, 0x50(sp) +; fld fs7, 0x48(sp) +; fld fs8, 0x40(sp) +; fld fs9, 0x38(sp) +; fld fs10, 0x30(sp) +; fld fs11, 0x28(sp) +; addi sp, sp, 0xe0 +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 +; ret +; block5: ; offset 0x130 +; fld fa1, 0x10(sp) +; ld a0, 8(sp) +; block6: ; offset 0x138 +; addiw a0, a0, 1 +; fmv.w.x fa0, zero +; ld s0, 0xd8(sp) +; ld s1, 0xd0(sp) +; ld s2, 0xc8(sp) +; ld s3, 0xc0(sp) +; ld s4, 0xb8(sp) +; ld s5, 0xb0(sp) +; ld s6, 0xa8(sp) +; ld s7, 0xa0(sp) +; ld s8, 0x98(sp) +; ld s9, 0x90(sp) +; ld s10, 0x88(sp) +; ld s11, 0x80(sp) +; fld fs0, 0x78(sp) +; fld fs2, 0x70(sp) +; fld fs3, 0x68(sp) +; fld fs4, 0x60(sp) +; fld fs5, 0x58(sp) +; fld fs6, 0x50(sp) +; fld fs7, 0x48(sp) +; fld fs8, 0x40(sp) +; fld fs9, 0x38(sp) +; fld fs10, 0x30(sp) +; fld fs11, 0x28(sp) +; addi sp, sp, 0xe0 +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 +; ret + diff --git a/cranelift/filetests/filetests/isa/s390x/exceptions.clif b/cranelift/filetests/filetests/isa/s390x/exceptions.clif index 0b3a64a6f3..b67010a707 100644 --- a/cranelift/filetests/filetests/isa/s390x/exceptions.clif +++ b/cranelift/filetests/filetests/isa/s390x/exceptions.clif @@ -34,7 +34,7 @@ function %f0(i32) -> i32, f32, f64 { ; block0: ; larl %r1, [const(1)] ; ld %f2, 0(%r1) ; vst %v2, 160(%r15) -; brasl %r14, %g; jg MachLabel(1); catch [None: MachLabel(2)] +; brasl %r14, %g; jg MachLabel(1); catch [default: MachLabel(2)] ; block1: ; lhi %r2, 1 ; vl %v2, 160(%r15) @@ -152,7 +152,7 @@ function %f2(i32) -> i32, f32, f64 { ; larl %r1, [const(1)] ; ld %f2, 0(%r1) ; vst %v2, 160(%r15) ; bras %r1, 12 ; data %g + 0 ; lg %r5, 0(%r1) -; basr %r14, %r5; jg MachLabel(1); catch [None: MachLabel(2)] +; basr %r14, %r5; jg MachLabel(1); catch [default: MachLabel(2)] ; block1: ; lhi %r2, 1 ; vl %v2, 160(%r15) @@ -242,3 +242,157 @@ function %f2(i32) -> i32, f32, f64 { ; .byte 0x00, 0x00 ; .byte 0x00, 0x00 +function %f4(i64, i32) -> i32, f32, f64 { + sig0 = (i32) -> f32 tail + fn0 = %g(i32) -> f32 tail + + block0(v0: i64, v1: i32): + v2 = f64const 0x1.0 + try_call fn0(v1), sig0, block1(ret0, v2), [ context v0, tag0: block2(exn0), tag1: block2(exn0), context v1, tag0: block3 ] + + block1(v3: f32, v4: f64): + v5 = iconst.i32 1 + return v5, v3, v4 + + block2(v6: i64): + v7 = ireduce.i32 v6 + v8 = iadd_imm.i32 v7, 1 + v9 = f32const 0x0.0 + return v8, v9, v2 + + block3: + jump block2(v0) +} + +; VCode: +; stmg %r6, %r15, 48(%r15) +; aghi %r15, -256 +; std %f8, 192(%r15) +; std %f9, 200(%r15) +; std %f10, 208(%r15) +; std %f11, 216(%r15) +; std %f12, 224(%r15) +; std %f13, 232(%r15) +; std %f14, 240(%r15) +; std %f15, 248(%r15) +; block0: +; stg %r2, 168(%r15) +; larl %r1, [const(1)] ; ld %f2, 0(%r1) +; vst %v2, 176(%r15) +; bras %r1, 12 ; data %g + 0 ; lg %r4, 0(%r1) +; lgr %r2, %r3 +; stg %r3, 160(%r15) +; basr %r14, %r4; jg MachLabel(3); catch [context stack1, tag0: MachLabel(1), tag1: MachLabel(2), context stack0, tag0: MachLabel(4)] +; block1: +; lgr %r2, %r6 +; vl %v2, 176(%r15) +; jg label5 +; block2: +; lgr %r2, %r6 +; vl %v2, 176(%r15) +; jg label5 +; block3: +; lhi %r2, 1 +; vl %v2, 176(%r15) +; ld %f8, 192(%r15) +; ld %f9, 200(%r15) +; ld %f10, 208(%r15) +; ld %f11, 216(%r15) +; ld %f12, 224(%r15) +; ld %f13, 232(%r15) +; ld %f14, 240(%r15) +; ld %f15, 248(%r15) +; lmg %r6, %r15, 304(%r15) +; br %r14 +; block4: +; vl %v2, 176(%r15) +; lg %r2, 168(%r15) +; jg label5 +; block5: +; ahi %r2, 1 +; larl %r1, [const(0)] ; le %f0, 0(%r1) +; ld %f8, 192(%r15) +; ld %f9, 200(%r15) +; ld %f10, 208(%r15) +; ld %f11, 216(%r15) +; ld %f12, 224(%r15) +; ld %f13, 232(%r15) +; ld %f14, 240(%r15) +; ld %f15, 248(%r15) +; lmg %r6, %r15, 304(%r15) +; br %r14 +; +; Disassembled: +; block0: ; offset 0x0 +; stmg %r6, %r15, 0x30(%r15) +; aghi %r15, -0x100 +; std %f8, 0xc0(%r15) +; std %f9, 0xc8(%r15) +; std %f10, 0xd0(%r15) +; std %f11, 0xd8(%r15) +; std %f12, 0xe0(%r15) +; std %f13, 0xe8(%r15) +; std %f14, 0xf0(%r15) +; std %f15, 0xf8(%r15) +; block1: ; offset 0x2a +; stg %r2, 0xa8(%r15) +; larl %r1, 0xf8 +; ld %f2, 0(%r1) +; vst %v2, 0xb0(%r15) +; bras %r1, 0x4c +; .byte 0x00, 0x00 ; reloc_external Abs8 %g 0 +; .byte 0x00, 0x00 +; .byte 0x00, 0x00 +; .byte 0x00, 0x00 +; lg %r4, 0(%r1) +; lgr %r2, %r3 +; stg %r3, 0xa0(%r15) +; basr %r14, %r4 +; jg 0x84 +; block2: ; offset 0x64 +; lgr %r2, %r6 +; vl %v2, 0xb0(%r15) +; jg 0xc2 +; block3: ; offset 0x74 +; lgr %r2, %r6 +; vl %v2, 0xb0(%r15) +; jg 0xc2 +; block4: ; offset 0x84 +; lhi %r2, 1 +; vl %v2, 0xb0(%r15) +; ld %f8, 0xc0(%r15) +; ld %f9, 0xc8(%r15) +; ld %f10, 0xd0(%r15) +; ld %f11, 0xd8(%r15) +; ld %f12, 0xe0(%r15) +; ld %f13, 0xe8(%r15) +; ld %f14, 0xf0(%r15) +; ld %f15, 0xf8(%r15) +; lmg %r6, %r15, 0x130(%r15) +; br %r14 +; block5: ; offset 0xb6 +; vl %v2, 0xb0(%r15) +; lg %r2, 0xa8(%r15) +; block6: ; offset 0xc2 +; ahi %r2, 1 +; larl %r1, 0x100 +; le %f0, 0(%r1) +; ld %f8, 0xc0(%r15) +; ld %f9, 0xc8(%r15) +; ld %f10, 0xd0(%r15) +; ld %f11, 0xd8(%r15) +; ld %f12, 0xe0(%r15) +; ld %f13, 0xe8(%r15) +; ld %f14, 0xf0(%r15) +; ld %f15, 0xf8(%r15) +; lmg %r6, %r15, 0x130(%r15) +; br %r14 +; sur %f15, %f0 +; .byte 0x00, 0x00 +; .byte 0x00, 0x00 +; .byte 0x00, 0x00 +; .byte 0x00, 0x00 +; .byte 0x00, 0x00 +; .byte 0x00, 0x00 +; .byte 0x00, 0x00 + diff --git a/cranelift/filetests/filetests/isa/x64/exceptions.clif b/cranelift/filetests/filetests/isa/x64/exceptions.clif index c5d6366cea..a83b34e69e 100644 --- a/cranelift/filetests/filetests/isa/x64/exceptions.clif +++ b/cranelift/filetests/filetests/isa/x64/exceptions.clif @@ -34,7 +34,7 @@ function %f0(i32) -> i32, f32, f64 { ; movq %rcx, %xmm1 ; movdqu %xmm1, +(%rsp) ; load_ext_name %g+0, %rdx -; call *%rdx; jmp MachLabel(1); catch [None: MachLabel(2)] +; call *%rdx; jmp MachLabel(1); catch [default: MachLabel(2)] ; block1: ; movl $0x1, %eax ; movdqu +(%rsp), %xmm1 @@ -164,7 +164,7 @@ function %f1(i32) -> i32, f32, f64 { ; movq %r10, %xmm1 ; movdqu %xmm1, +(%rsp) ; load_ext_name %g+0, %r11 -; call *%r11; jmp MachLabel(6); catch [None: MachLabel(5)] +; call *%r11; jmp MachLabel(6); catch [default: MachLabel(5)] ; block5: ; movq %rax, %rsi ; movq %rsi, %r11 @@ -298,7 +298,7 @@ function %f2(i32) -> i32, f32, f64 { ; movq %rcx, %xmm1 ; movdqu %xmm1, +(%rsp) ; load_ext_name %g+0, %rdx -; call *%rdx; jmp MachLabel(1); catch [None: MachLabel(2)] +; call *%rdx; jmp MachLabel(1); catch [default: MachLabel(2)] ; block1: ; movl $0x1, %eax ; movdqu +(%rsp), %xmm1 @@ -405,3 +405,137 @@ block2: ; block2: ; offset 0x10 ; jmp 0x10 +function %f4(i64, i32) -> i32, f32, f64 { + sig0 = (i32) -> f32 tail + fn0 = %g(i32) -> f32 tail + + block0(v0: i64, v1: i32): + v2 = f64const 0x1.0 + try_call fn0(v1), sig0, block1(ret0, v2), [ context v0, tag0: block2(exn0), tag1: block2(exn0), context v1, tag0: block3 ] + + block1(v3: f32, v4: f64): + v5 = iconst.i32 1 + return v5, v3, v4 + + block2(v6: i64): + v7 = ireduce.i32 v6 + v8 = iadd_imm.i32 v7, 1 + v9 = f32const 0x0.0 + return v8, v9, v2 + + block3: + jump block2(v0) +} + +; VCode: +; pushq %rbp +; movq %rsp, %rbp +; subq $0x50, %rsp +; movq %rbx, 0x20(%rsp) +; movq %r12, 0x28(%rsp) +; movq %r13, 0x30(%rsp) +; movq %r14, 0x38(%rsp) +; movq %r15, 0x40(%rsp) +; block0: +; movq %rdi, +8(%rsp) +; movabsq $0x3ff0000000000000, %rdx +; movq %rdx, %xmm1 +; movdqu %xmm1, +0x10(%rsp) +; load_ext_name %g+0, %r8 +; movq %rsi, %rdi +; movq %rsi, +(%rsp) +; call *%r8; jmp MachLabel(3); catch [context stack1, tag0: MachLabel(1), tag1: MachLabel(2), context stack0, tag0: MachLabel(4)] +; block1: +; movq %rax, %rdi +; movdqu +0x10(%rsp), %xmm1 +; jmp label5 +; block2: +; movq %rax, %rdi +; movdqu +0x10(%rsp), %xmm1 +; jmp label5 +; block3: +; movl $0x1, %eax +; movdqu +0x10(%rsp), %xmm1 +; movq 0x20(%rsp), %rbx +; movq 0x28(%rsp), %r12 +; movq 0x30(%rsp), %r13 +; movq 0x38(%rsp), %r14 +; movq 0x40(%rsp), %r15 +; addq $0x50, %rsp +; movq %rbp, %rsp +; popq %rbp +; retq +; block4: +; movdqu +0x10(%rsp), %xmm1 +; movq +8(%rsp), %rdi +; jmp label5 +; block5: +; leal 1(%rdi), %eax +; uninit %xmm0 +; xorps %xmm0, %xmm0 +; movq 0x20(%rsp), %rbx +; movq 0x28(%rsp), %r12 +; movq 0x30(%rsp), %r13 +; movq 0x38(%rsp), %r14 +; movq 0x40(%rsp), %r15 +; addq $0x50, %rsp +; movq %rbp, %rsp +; popq %rbp +; retq +; +; Disassembled: +; block0: ; offset 0x0 +; pushq %rbp +; movq %rsp, %rbp +; subq $0x50, %rsp +; movq %rbx, 0x20(%rsp) +; movq %r12, 0x28(%rsp) +; movq %r13, 0x30(%rsp) +; movq %r14, 0x38(%rsp) +; movq %r15, 0x40(%rsp) +; block1: ; offset 0x21 +; movq %rdi, 8(%rsp) +; movabsq $0x3ff0000000000000, %rdx +; movq %rdx, %xmm1 +; movdqu %xmm1, 0x10(%rsp) +; movabsq $0, %r8 ; reloc_external Abs8 %g 0 +; movq %rsi, %rdi +; movq %rsi, (%rsp) +; callq *%r8 +; jmp 0x70 +; block2: ; offset 0x54 +; movq %rax, %rdi +; movdqu 0x10(%rsp), %xmm1 +; jmp 0xa8 +; block3: ; offset 0x62 +; movq %rax, %rdi +; movdqu 0x10(%rsp), %xmm1 +; jmp 0xa8 +; block4: ; offset 0x70 +; movl $1, %eax +; movdqu 0x10(%rsp), %xmm1 +; movq 0x20(%rsp), %rbx +; movq 0x28(%rsp), %r12 +; movq 0x30(%rsp), %r13 +; movq 0x38(%rsp), %r14 +; movq 0x40(%rsp), %r15 +; addq $0x50, %rsp +; movq %rbp, %rsp +; popq %rbp +; retq +; block5: ; offset 0x9d +; movdqu 0x10(%rsp), %xmm1 +; movq 8(%rsp), %rdi +; block6: ; offset 0xa8 +; leal 1(%rdi), %eax +; xorps %xmm0, %xmm0 +; movq 0x20(%rsp), %rbx +; movq 0x28(%rsp), %r12 +; movq 0x30(%rsp), %r13 +; movq 0x38(%rsp), %r14 +; movq 0x40(%rsp), %r15 +; addq $0x50, %rsp +; movq %rbp, %rsp +; popq %rbp +; retq + diff --git a/cranelift/filetests/filetests/isa/x64/shuffle-avx512.clif b/cranelift/filetests/filetests/isa/x64/shuffle-avx512.clif index a2a82dbead..e5c964e59b 100644 --- a/cranelift/filetests/filetests/isa/x64/shuffle-avx512.clif +++ b/cranelift/filetests/filetests/isa/x64/shuffle-avx512.clif @@ -15,7 +15,7 @@ block0(v0: i8x16, v1: i8x16): ; movdqa %xmm0, %xmm5 ; movdqu (%rip), %xmm0 ; movdqa %xmm5, %xmm6 -; vpermi2b %xmm1, %xmm6, %xmm0, %xmm0 +; vpermi2b %xmm1, %xmm6, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -54,7 +54,7 @@ block0(v0: i8x16, v1: i8x16): ; movdqa %xmm0, %xmm5 ; movdqu (%rip), %xmm0 ; movdqa %xmm5, %xmm6 -; vpermi2b %xmm1, %xmm6, %xmm0, %xmm0 +; vpermi2b %xmm1, %xmm6, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/isa/x64/simd-abs-avx512.clif b/cranelift/filetests/filetests/isa/x64/simd-abs-avx512.clif index 74ec07330c..a712936d40 100644 --- a/cranelift/filetests/filetests/isa/x64/simd-abs-avx512.clif +++ b/cranelift/filetests/filetests/isa/x64/simd-abs-avx512.clif @@ -12,7 +12,7 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; vpabsq 0(%rdi), %xmm0 +; vpabsq (%rdi), %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/isa/x64/simd-i64x2-shift-avx512.clif b/cranelift/filetests/filetests/isa/x64/simd-i64x2-shift-avx512.clif index 75b61752fb..3facc4a285 100644 --- a/cranelift/filetests/filetests/isa/x64/simd-i64x2-shift-avx512.clif +++ b/cranelift/filetests/filetests/isa/x64/simd-i64x2-shift-avx512.clif @@ -17,10 +17,10 @@ block0(v0: i64x2, v1: i64): ; movq %rdi, %r9 ; andq $0x3f, %r9 ; vmovd %r9d, %xmm1 -; vpsraq %xmm1, %xmm0, %xmm0 +; vpsraq %xmm1, %xmm0, %xmm0 ; andq $0x3f, %rdi ; vmovd %edi, %xmm1 -; vpsraq %xmm1, %xmm0, %xmm1 +; vpsraq %xmm1, %xmm0, %xmm1 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -51,7 +51,7 @@ block0(v0: i64x2): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; vpsraqimm $31, %xmm0, %xmm0 +; vpsraq $0x1f, %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -77,7 +77,7 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; vpsraqimm $31, 0(%rdi), %xmm0 +; vpsraq $0x1f, (%rdi), %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -103,7 +103,7 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; vpsraqimm $31, 7(%rdi), %xmm0 +; vpsraq $0x1f, 7(%rdi), %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -129,7 +129,7 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; vpsraqimm $31, 16(%rdi), %xmm0 +; vpsraq $0x1f, 0x10(%rdi), %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/src/test_inline.rs b/cranelift/filetests/src/test_inline.rs index ffebdde1bc..55e7189b64 100644 --- a/cranelift/filetests/src/test_inline.rs +++ b/cranelift/filetests/src/test_inline.rs @@ -121,7 +121,7 @@ struct Inliner<'a>(Ref<'a, HashMap>); impl<'a> Inline for Inliner<'a> { fn inline( - &self, + &mut self, caller: &ir::Function, _inst: ir::Inst, _opcode: ir::Opcode, diff --git a/cranelift/fuzzgen/Cargo.toml b/cranelift/fuzzgen/Cargo.toml index 2d30f45cd6..fb323ad31d 100644 --- a/cranelift/fuzzgen/Cargo.toml +++ b/cranelift/fuzzgen/Cargo.toml @@ -20,3 +20,4 @@ cranelift-native = { workspace = true } anyhow = { workspace = true, features = ['std'] } arbitrary = { workspace = true } target-lexicon = { workspace = true, features = ["std"] } +rand = { workspace = true } diff --git a/cranelift/fuzzgen/src/function_generator.rs b/cranelift/fuzzgen/src/function_generator.rs index d8b08e0320..6c01851740 100644 --- a/cranelift/fuzzgen/src/function_generator.rs +++ b/cranelift/fuzzgen/src/function_generator.rs @@ -1577,19 +1577,20 @@ where } fn generate_funcrefs(&mut self, builder: &mut FunctionBuilder) -> Result<()> { - let usercalls: Vec<(ExternalName, Signature)> = self + let usercalls: Vec<_> = self .resources .usercalls .iter() .map(|(name, signature)| { let user_func_ref = builder.func.declare_imported_user_function(name.clone()); let name = ExternalName::User(user_func_ref); - (name, signature.clone()) + let never_colocated = false; + (name, signature.clone(), never_colocated) }) .collect(); let lib_callconv = self.system_callconv(); - let libcalls: Vec<(ExternalName, Signature)> = self + let libcalls: Vec<_> = self .resources .libcalls .iter() @@ -1600,16 +1601,24 @@ where .unwrap(); let signature = libcall.signature(lib_callconv, pointer_type); let name = ExternalName::LibCall(*libcall); - (name, signature) + // libcalls can't be colocated to generated code because we + // don't know where in the address space the function will go + // relative to where the libcall is. + let never_colocated = true; + (name, signature, never_colocated) }) .collect(); - for (name, signature) in usercalls.into_iter().chain(libcalls) { + for (name, signature, never_colocated) in usercalls.into_iter().chain(libcalls) { let sig_ref = builder.import_signature(signature.clone()); let func_ref = builder.import_function(ExtFuncData { name, signature: sig_ref, - colocated: self.u.arbitrary()?, + colocated: if never_colocated { + false + } else { + self.u.arbitrary()? + }, }); self.resources diff --git a/cranelift/fuzzgen/src/lib.rs b/cranelift/fuzzgen/src/lib.rs index 3c11a9b0ad..1fe703d584 100644 --- a/cranelift/fuzzgen/src/lib.rs +++ b/cranelift/fuzzgen/src/lib.rs @@ -13,6 +13,7 @@ use cranelift::prelude::settings::SettingKind; use cranelift::prelude::*; use cranelift_arbitrary::CraneliftArbitrary; use cranelift_native::builder_with_options; +use rand::{Rng, SeedableRng, rngs::SmallRng}; use target_isa_extras::TargetIsaExtras; use target_lexicon::Architecture; @@ -306,10 +307,15 @@ where // the values so that we can pass it into the builder again. let max_isa = max_builder.finish(Flags::new(settings::builder()))?; - // We give each of the flags a chance of being copied over. Otherwise we keep the default. + // We give each of the flags a chance of being copied over. Otherwise we + // keep the default. Note that a constant amount of data is taken from + // `self.u` as a seed for a `SmallRng` which is then transitively used + // to make decisions about what flags to include. This is done to ensure + // that the same test case generates similarly across different machines + // with different CPUs when `Host` is used above. + let mut rng = SmallRng::from_seed(self.u.arbitrary()?); for value in max_isa.isa_flags().iter() { - let should_copy = bool::arbitrary(self.u)?; - if !should_copy { + if rng.random() { continue; } builder.set(value.name, &value.value_string())?; diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs index 4b036ed007..0a9675e6a8 100644 --- a/cranelift/reader/src/parser.rs +++ b/cranelift/reader/src/parser.rs @@ -1923,13 +1923,14 @@ impl<'a> Parser<'a> { // Parse an exception-table decl. // // exception-table ::= * SigRef(sig) "," BlockCall "," "[" (exception-table-entry ( "," exception-table-entry )*)? "]" - // exception-table-entry ::= * ExceptionTag(tag) ":" BlockCall - // * "default" ":" BlockCall + // exception-table-entry ::= ExceptionTag(tag) ":" BlockCall + // | "default" ":" BlockCall + // | "context" value fn parse_exception_table(&mut self, ctx: &mut Context) -> ParseResult { let sig = self.match_sig("expected signature of called function")?; self.match_token(Token::Comma, "expected comma after signature argument")?; - let mut tags_and_targets = vec![]; + let mut handlers = vec![]; let block_num = self.match_block("expected branch destination block")?; let args = self.parse_opt_block_call_args()?; @@ -1940,53 +1941,53 @@ impl<'a> Parser<'a> { "expected comma after normal-return destination", )?; - match self.token() { - Some(Token::LBracket) => { - self.consume(); - loop { - if let Some(Token::RBracket) = self.token() { - break; - } - - let tag = match self.token() { - Some(Token::ExceptionTag(tag)) => { - self.consume(); - Some(ir::ExceptionTag::from_u32(tag)) - } - Some(Token::Identifier("default")) => { - self.consume(); - None - } - _ => return err!(self.loc, "invalid token"), - }; + self.match_token( + Token::LBracket, + "expected an open-bracket for exception table list", + )?; + loop { + match self.token() { + Some(Token::RBracket) => { + break; + } + Some(Token::ExceptionTag(tag)) => { + self.consume(); self.match_token(Token::Colon, "expected ':' after exception tag")?; - + let tag = ir::ExceptionTag::from_u32(tag); let block_num = self.match_block("expected branch destination block")?; let args = self.parse_opt_block_call_args()?; let block_call = ctx.function.dfg.block_call(block_num, &args); - - tags_and_targets.push((tag, block_call)); - - if let Some(Token::Comma) = self.token() { - self.consume(); - } else { - break; - } + handlers.push(ir::ExceptionTableItem::Tag(tag, block_call)); + } + Some(Token::Identifier("default")) => { + self.consume(); + self.match_token(Token::Colon, "expected ':' after 'default'")?; + let block_num = self.match_block("expected branch destination block")?; + let args = self.parse_opt_block_call_args()?; + let block_call = ctx.function.dfg.block_call(block_num, &args); + handlers.push(ir::ExceptionTableItem::Default(block_call)); + } + Some(Token::Identifier("context")) => { + self.consume(); + let val = self.match_value("expected value for exception-handler context")?; + handlers.push(ir::ExceptionTableItem::Context(val)); } - self.match_token(Token::RBracket, "expected closing bracket")?; + _ => return err!(self.loc, "invalid token"), } - _ => {} - }; + + if let Some(Token::Comma) = self.token() { + self.consume(); + } else { + break; + } + } + self.match_token(Token::RBracket, "expected closing bracket")?; Ok(ctx .function .dfg .exception_tables - .push(ir::ExceptionTableData::new( - sig, - normal_return, - tags_and_targets, - ))) + .push(ir::ExceptionTableData::new(sig, normal_return, handlers))) } // Parse a constant decl. diff --git a/crates/c-api/CMakeLists.txt b/crates/c-api/CMakeLists.txt index df203697d9..f64102295e 100644 --- a/crates/c-api/CMakeLists.txt +++ b/crates/c-api/CMakeLists.txt @@ -51,6 +51,12 @@ endif() list(TRANSFORM WASMTIME_SHARED_FILES PREPEND ${WASMTIME_TARGET_DIR}/) list(TRANSFORM WASMTIME_STATIC_FILES PREPEND ${WASMTIME_TARGET_DIR}/) +list(APPEND WASMTIME_BUILD_ENV "${CARGO_PROFILE_PANIC}=abort") +if (APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET) + # On macOS, we need to set the deployment target for the build + list(APPEND WASMTIME_BUILD_ENV "MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}") +endif() + include(ExternalProject) find_program(WASMTIME_CARGO_BINARY cargo) if(NOT WASMTIME_CARGO_BINARY) @@ -62,7 +68,7 @@ ExternalProject_Add( CONFIGURE_COMMAND "" INSTALL_COMMAND "${WASMTIME_INSTALL_COMMAND}" BUILD_COMMAND - ${CMAKE_COMMAND} -E env ${CARGO_PROFILE_PANIC}=abort + ${CMAKE_COMMAND} -E env ${WASMTIME_BUILD_ENV} ${WASMTIME_CARGO_BINARY} build --target ${WASMTIME_TARGET} --package wasmtime-c-api @@ -90,7 +96,8 @@ else() target_link_libraries(wasmtime INTERFACE ${WASMTIME_STATIC_FILES} ws2_32 advapi32 userenv ntdll shell32 ole32 bcrypt) elseif(WASMTIME_TARGET MATCHES "darwin") - target_link_libraries(wasmtime INTERFACE ${WASMTIME_STATIC_FILES}) + target_link_libraries(wasmtime INTERFACE ${WASMTIME_STATIC_FILES} + "-framework CoreFoundation") else() target_link_libraries(wasmtime INTERFACE ${WASMTIME_STATIC_FILES} pthread dl m) diff --git a/crates/c-api/include/wasmtime/component/linker.h b/crates/c-api/include/wasmtime/component/linker.h index be77d88a55..38b5875c01 100644 --- a/crates/c-api/include/wasmtime/component/linker.h +++ b/crates/c-api/include/wasmtime/component/linker.h @@ -134,7 +134,7 @@ typedef wasmtime_error_t *(*wasmtime_component_func_callback_t)( WASM_API_EXTERN wasmtime_error_t *wasmtime_component_linker_instance_add_func( wasmtime_component_linker_instance_t *linker_instance, const char *name, size_t name_len, wasmtime_component_func_callback_t callback, void *data, - void (*finalizer)()); + void (*finalizer)(void *)); #ifdef WASMTIME_FEATURE_WASI diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs index ada037e263..64f109f349 100644 --- a/crates/cli-flags/src/lib.rs +++ b/crates/cli-flags/src/lib.rs @@ -237,6 +237,9 @@ wasmtime_option_group! { /// object files. pub native_unwind_info: Option, + /// Whether to perform function inlining during compilation. + pub inlining: Option, + #[prefixed = "cranelift"] #[serde(default)] /// Set a cranelift-specific option. Use `wasmtime settings` to see @@ -822,6 +825,9 @@ impl CommonOptions { if let Some(enable) = self.codegen.native_unwind_info { config.native_unwind_info(enable); } + if let Some(enable) = self.codegen.inlining { + config.compiler_inlining(enable); + } // async_stack_size enabled by either async or stack-switching, so // cannot directly use match_feature! diff --git a/crates/cli-flags/src/opt.rs b/crates/cli-flags/src/opt.rs index b57afccf15..706ba1635d 100644 --- a/crates/cli-flags/src/opt.rs +++ b/crates/cli-flags/src/opt.rs @@ -23,6 +23,7 @@ macro_rules! wasmtime_option_group { pub struct $opts:ident { $( $(#[doc = $doc:tt])* + $(#[doc($doc_attr:meta)])? $(#[serde($serde_attr:meta)])* pub $opt:ident: $container:ident<$payload:ty>, )+ @@ -31,6 +32,7 @@ macro_rules! wasmtime_option_group { #[prefixed = $prefix:tt] $(#[serde($serde_attr2:meta)])* $(#[doc = $prefixed_doc:tt])* + $(#[doc($prefixed_doc_attr:meta)])? pub $prefixed:ident: Vec<(String, Option)>, )? } @@ -43,6 +45,7 @@ macro_rules! wasmtime_option_group { pub struct $opts { $( $(#[serde($serde_attr)])* + $(#[doc($doc_attr)])? pub $opt: $container<$payload>, )+ $( diff --git a/crates/component-macro/Cargo.toml b/crates/component-macro/Cargo.toml index 7230c450bf..5936974dbd 100644 --- a/crates/component-macro/Cargo.toml +++ b/crates/component-macro/Cargo.toml @@ -40,5 +40,5 @@ prettyplease = "0.2.31" similar = { workspace = true } [features] -async = [] +async = ['wasmtime-wit-bindgen/async'] component-model-async = ['async', 'wasmtime-wit-bindgen/component-model-async'] diff --git a/crates/component-macro/src/bindgen.rs b/crates/component-macro/src/bindgen.rs index d40c95c1ad..c97832e97d 100644 --- a/crates/component-macro/src/bindgen.rs +++ b/crates/component-macro/src/bindgen.rs @@ -1,6 +1,6 @@ use proc_macro2::{Span, TokenStream}; use quote::ToTokens; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::env; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; @@ -8,7 +8,7 @@ use syn::parse::{Error, Parse, ParseStream, Result}; use syn::punctuated::Punctuated; use syn::{Token, braced, token}; use wasmtime_wit_bindgen::{ - AsyncConfig, CallStyle, Opts, Ownership, TrappableError, TrappableImports, + FunctionConfig, FunctionFilter, FunctionFlags, Opts, Ownership, TrappableError, }; use wit_parser::{PackageId, Resolve, UnresolvedPackageGroup, WorldId}; @@ -21,22 +21,6 @@ pub struct Config { } pub fn expand(input: &Config) -> Result { - if let (CallStyle::Async | CallStyle::Concurrent, false) = - (input.opts.call_style(), cfg!(feature = "async")) - { - return Err(Error::new( - Span::call_site(), - "cannot enable async bindings unless `async` crate feature is active", - )); - } - - if input.opts.concurrent_imports && !cfg!(feature = "component-model-async") { - return Err(Error::new( - Span::call_site(), - "cannot enable `concurrent_imports` option unless `component-model-async` crate feature is active", - )); - } - let mut src = match input.opts.generate(&input.resolve, input.world) { Ok(s) => s, Err(e) => return Err(Error::new(Span::call_site(), e.to_string())), @@ -94,7 +78,8 @@ impl Parse for Config { let mut world = None; let mut inline = None; let mut paths = Vec::new(); - let mut async_configured = false; + let mut imports_configured = false; + let mut exports_configured = false; let mut include_generated_code_from_file = false; if input.peek(token::Brace) { @@ -118,20 +103,8 @@ impl Parse for Config { } inline = Some(s.value()); } - Opt::Tracing(val) => opts.tracing = val, - Opt::VerboseTracing(val) => opts.verbose_tracing = val, Opt::Debug(val) => opts.debug = val, - Opt::Async(val, span) => { - if async_configured { - return Err(Error::new(span, "cannot specify second async config")); - } - async_configured = true; - opts.async_ = val; - } - Opt::ConcurrentImports(val) => opts.concurrent_imports = val, - Opt::ConcurrentExports(val) => opts.concurrent_exports = val, Opt::TrappableErrorType(val) => opts.trappable_error_type = val, - Opt::TrappableImports(val) => opts.trappable_imports = val, Opt::Ownership(val) => opts.ownership = val, Opt::Interfaces(s) => { if inline.is_some() { @@ -172,6 +145,20 @@ impl Parse for Config { opts.wasmtime_crate = Some(f.into_token_stream().to_string()) } Opt::IncludeGeneratedCodeFromFile(i) => include_generated_code_from_file = i, + Opt::Imports(config, span) => { + if imports_configured { + return Err(Error::new(span, "cannot specify imports configuration")); + } + opts.imports = config; + imports_configured = true; + } + Opt::Exports(config, span) => { + if exports_configured { + return Err(Error::new(span, "cannot specify exports configuration")); + } + opts.exports = config; + exports_configured = true; + } } } } else { @@ -290,39 +277,38 @@ mod kw { syn::custom_keyword!(with); syn::custom_keyword!(except_imports); syn::custom_keyword!(only_imports); - syn::custom_keyword!(trappable_imports); syn::custom_keyword!(additional_derives); syn::custom_keyword!(stringify); syn::custom_keyword!(skip_mut_forwarding_impls); syn::custom_keyword!(require_store_data_send); syn::custom_keyword!(wasmtime_crate); syn::custom_keyword!(include_generated_code_from_file); - syn::custom_keyword!(concurrent_imports); - syn::custom_keyword!(concurrent_exports); syn::custom_keyword!(debug); + syn::custom_keyword!(imports); + syn::custom_keyword!(exports); + syn::custom_keyword!(store); + syn::custom_keyword!(trappable); + syn::custom_keyword!(ignore_wit); + syn::custom_keyword!(exact); } enum Opt { World(syn::LitStr), Path(Vec), Inline(syn::LitStr), - Tracing(bool), - VerboseTracing(bool), - Async(AsyncConfig, Span), TrappableErrorType(Vec), Ownership(Ownership), Interfaces(syn::LitStr), With(HashMap), - TrappableImports(TrappableImports), AdditionalDerives(Vec), Stringify(bool), SkipMutForwardingImpls(bool), RequireStoreDataSend(bool), WasmtimeCrate(syn::Path), IncludeGeneratedCodeFromFile(bool), - ConcurrentImports(bool), - ConcurrentExports(bool), Debug(bool), + Imports(FunctionConfig, Span), + Exports(FunctionConfig, Span), } impl Parse for Opt { @@ -360,60 +346,6 @@ impl Parse for Opt { input.parse::()?; input.parse::()?; Ok(Opt::World(input.parse()?)) - } else if l.peek(kw::tracing) { - input.parse::()?; - input.parse::()?; - Ok(Opt::Tracing(input.parse::()?.value)) - } else if l.peek(kw::verbose_tracing) { - input.parse::()?; - input.parse::()?; - Ok(Opt::VerboseTracing(input.parse::()?.value)) - } else if l.peek(Token![async]) { - let span = input.parse::()?.span; - input.parse::()?; - if input.peek(syn::LitBool) { - match input.parse::()?.value { - true => Ok(Opt::Async(AsyncConfig::All, span)), - false => Ok(Opt::Async(AsyncConfig::None, span)), - } - } else { - let contents; - syn::braced!(contents in input); - - let l = contents.lookahead1(); - let ctor: fn(HashSet) -> AsyncConfig = if l.peek(kw::except_imports) { - contents.parse::()?; - contents.parse::()?; - AsyncConfig::AllExceptImports - } else if l.peek(kw::only_imports) { - contents.parse::()?; - contents.parse::()?; - AsyncConfig::OnlyImports - } else { - return Err(l.error()); - }; - - let list; - syn::bracketed!(list in contents); - let fields: Punctuated = - list.parse_terminated(Parse::parse, Token![,])?; - - if contents.peek(Token![,]) { - contents.parse::()?; - } - Ok(Opt::Async( - ctor(fields.iter().map(|s| s.value()).collect()), - span, - )) - } - } else if l.peek(kw::concurrent_imports) { - input.parse::()?; - input.parse::()?; - Ok(Opt::ConcurrentImports(input.parse::()?.value)) - } else if l.peek(kw::concurrent_exports) { - input.parse::()?; - input.parse::()?; - Ok(Opt::ConcurrentExports(input.parse::()?.value)) } else if l.peek(kw::ownership) { input.parse::()?; input.parse::()?; @@ -472,22 +404,6 @@ impl Parse for Opt { let fields: Punctuated<(String, String), Token![,]> = contents.parse_terminated(with_field_parse, Token![,])?; Ok(Opt::With(HashMap::from_iter(fields))) - } else if l.peek(kw::trappable_imports) { - input.parse::()?; - input.parse::()?; - let config = if input.peek(syn::LitBool) { - match input.parse::()?.value { - true => TrappableImports::All, - false => TrappableImports::None, - } - } else { - let contents; - syn::bracketed!(contents in input); - let fields: Punctuated = - contents.parse_terminated(Parse::parse, Token![,])?; - TrappableImports::Only(fields.iter().map(|s| s.value()).collect()) - }; - Ok(Opt::TrappableImports(config)) } else if l.peek(kw::additional_derives) { input.parse::()?; input.parse::()?; @@ -521,6 +437,14 @@ impl Parse for Opt { Ok(Opt::IncludeGeneratedCodeFromFile( input.parse::()?.value, )) + } else if l.peek(kw::imports) { + let span = input.parse::()?.span; + input.parse::()?; + Ok(Opt::Imports(parse_function_config(input)?, span)) + } else if l.peek(kw::exports) { + let span = input.parse::()?.span; + input.parse::()?; + Ok(Opt::Exports(parse_function_config(input)?, span)) } else { Err(l.error()) } @@ -579,3 +503,74 @@ fn with_field_parse(input: ParseStream<'_>) -> Result<(String, String)> { Ok((interface, buf)) } + +fn parse_function_config(input: ParseStream<'_>) -> Result { + let content; + syn::braced!(content in input); + let mut ret = FunctionConfig::new(); + + let list = Punctuated::::parse_terminated(&content)?; + for item in list.into_iter() { + ret.push(item.filter, item.flags); + } + + return Ok(ret); + + struct FunctionConfigSyntax { + filter: FunctionFilter, + flags: FunctionFlags, + } + + impl Parse for FunctionConfigSyntax { + fn parse(input: ParseStream<'_>) -> Result { + let l = input.lookahead1(); + let filter = if l.peek(syn::LitStr) { + FunctionFilter::Name(input.parse::()?.value()) + } else if l.peek(Token![default]) { + input.parse::()?; + FunctionFilter::Default + } else { + return Err(l.error()); + }; + + input.parse::()?; + + let mut flags = FunctionFlags::empty(); + while !input.is_empty() { + let l = input.lookahead1(); + if l.peek(Token![async]) { + input.parse::()?; + flags |= FunctionFlags::ASYNC; + } else if l.peek(kw::tracing) { + input.parse::()?; + flags |= FunctionFlags::TRACING; + } else if l.peek(kw::verbose_tracing) { + input.parse::()?; + flags |= FunctionFlags::VERBOSE_TRACING; + } else if l.peek(kw::store) { + input.parse::()?; + flags |= FunctionFlags::STORE; + } else if l.peek(kw::trappable) { + input.parse::()?; + flags |= FunctionFlags::TRAPPABLE; + } else if l.peek(kw::ignore_wit) { + input.parse::()?; + flags |= FunctionFlags::IGNORE_WIT; + } else if l.peek(kw::exact) { + input.parse::()?; + flags |= FunctionFlags::EXACT; + } else { + return Err(l.error()); + } + + if input.peek(Token![|]) { + input.parse::()?; + } else { + break; + } + } + + Ok(FunctionConfigSyntax { filter, flags }) + } + } +} diff --git a/crates/component-macro/tests/codegen.rs b/crates/component-macro/tests/codegen.rs index db2fae0d18..ac110aa903 100644 --- a/crates/component-macro/tests/codegen.rs +++ b/crates/component-macro/tests/codegen.rs @@ -9,22 +9,22 @@ macro_rules! gentest { mod async_ { wasmtime::component::bindgen!({ path: $path, - async: true, + imports: { default: async }, + exports: { default: async }, }); } mod concurrent { wasmtime::component::bindgen!({ path: $path, - async: true, - concurrent_imports: true, - concurrent_exports: true, + imports: { default: async | store }, + exports: { default: async | store }, }); } mod tracing { wasmtime::component::bindgen!({ path: $path, - tracing: true, - verbose_tracing: true, + imports: { default: tracing | verbose_tracing }, + exports: { default: tracing | verbose_tracing }, ownership: Borrowing { duplicate_if_necessary: true } @@ -361,7 +361,6 @@ mod trappable_imports { import foo: func(); } ", - trappable_imports: false, }); struct X; @@ -379,7 +378,7 @@ mod trappable_imports { import foo: func(); } ", - trappable_imports: true, + imports: { default: trappable }, }); struct X; @@ -400,7 +399,7 @@ mod trappable_imports { import bar: func(); } ", - trappable_imports: ["foo"], + imports: { "foo": trappable }, }); struct X; @@ -441,7 +440,11 @@ mod trappable_imports { } ", - trappable_imports: ["foo"], + imports: { + "foo": trappable | exact, + "i/foo": trappable, + "foo:foo/a/foo": trappable, + }, with: { "foo:foo/a/r": R }, }); @@ -501,11 +504,11 @@ mod trappable_imports { } ", - trappable_imports: [ - "[constructor]r", - "[method]r.foo", - "[static]r.bar", - ], + imports: { + "foo:foo/a/[constructor]r": trappable, + "foo:foo/a/[method]r.foo": trappable, + "foo:foo/a/[static]r.bar": trappable, + }, with: { "foo:foo/a/r": R }, }); @@ -600,8 +603,8 @@ mod with_and_mixing_async { import bar; } ", - async: { - only_imports: ["bar"], + imports: { + "my:inline/bar/bar": async, }, }); } diff --git a/crates/component-macro/tests/expanded.rs b/crates/component-macro/tests/expanded.rs index 4cecf2ba21..bb4711522c 100644 --- a/crates/component-macro/tests/expanded.rs +++ b/crates/component-macro/tests/expanded.rs @@ -11,22 +11,22 @@ macro_rules! genexpand { process_expanded($path, "_async", wasmtime::component::bindgen!({ path: $path, - async: true, stringify: true, + imports: { default: async }, + exports: { default: async }, }))?; process_expanded($path, "_concurrent", wasmtime::component::bindgen!({ path: $path, - async: true, - concurrent_imports: true, - concurrent_exports: true, + imports: { default: async | store }, + exports: { default: async | store }, stringify: true, }))?; process_expanded($path, "_tracing_async", wasmtime::component::bindgen!({ path: $path, - async: true, - tracing: true, + imports: { default: async | tracing }, + exports: { default: async | tracing }, stringify: true, }))?; }; diff --git a/crates/component-macro/tests/expanded/char.rs b/crates/component-macro/tests/expanded/char.rs index c00fb0f7d4..62978b4019 100644 --- a/crates/component-macro/tests/expanded/char.rs +++ b/crates/component-macro/tests/expanded/char.rs @@ -52,6 +52,17 @@ impl<_T: 'static> TheWorldPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `the-world`. /// @@ -144,12 +155,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::chars::HostWithStore, for<'a> D::Data<'a>: foo::foo::chars::Host, T: 'static, { @@ -167,6 +191,11 @@ pub mod foo { pub mod chars { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { /// A function that accepts a character fn take_char(&mut self, x: char) -> (); @@ -188,7 +217,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -258,7 +287,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/chars` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/char_async.rs b/crates/component-macro/tests/expanded/char_async.rs index 23b140f34e..00d0be6d90 100644 --- a/crates/component-macro/tests/expanded/char_async.rs +++ b/crates/component-macro/tests/expanded/char_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -150,12 +155,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::chars::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::chars::Host + Send, T: 'static + Send, { @@ -173,21 +191,35 @@ pub mod foo { pub mod chars { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { /// A function that accepts a character - async fn take_char(&mut self, x: char) -> (); + fn take_char( + &mut self, + x: char, + ) -> impl ::core::future::Future + Send; /// A function that returns a character - async fn return_char(&mut self) -> char; + fn return_char( + &mut self, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { /// A function that accepts a character - async fn take_char(&mut self, x: char) -> () { - Host::take_char(*self, x).await + fn take_char( + &mut self, + x: char, + ) -> impl ::core::future::Future + Send { + async move { Host::take_char(*self, x).await } } /// A function that returns a character - async fn return_char(&mut self) -> char { - Host::return_char(*self).await + fn return_char( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::return_char(*self).await } } } pub fn add_to_linker( @@ -195,7 +227,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -269,7 +301,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/chars` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/char_concurrent.rs b/crates/component-macro/tests/expanded/char_concurrent.rs index e65536df88..18b9d225a1 100644 --- a/crates/component-macro/tests/expanded/char_concurrent.rs +++ b/crates/component-macro/tests/expanded/char_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -150,12 +155,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::chars::HostConcurrent + Send, + D: foo::foo::chars::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::chars::Host + Send, T: 'static + Send, { @@ -173,23 +191,17 @@ pub mod foo { pub mod chars { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { /// A function that accepts a character fn take_char( accessor: &wasmtime::component::Accessor, x: char, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; /// A function that returns a character fn return_char( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -197,7 +209,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -207,7 +219,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (char,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::take_char(accessor, arg0) + let r = ::take_char(accessor, arg0) .await; Ok(r) }) @@ -218,7 +230,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::return_char(accessor).await; + let r = ::return_char(accessor).await; Ok((r,)) }) }, diff --git a/crates/component-macro/tests/expanded/char_tracing_async.rs b/crates/component-macro/tests/expanded/char_tracing_async.rs index 53ac7ad525..01ad8a97d4 100644 --- a/crates/component-macro/tests/expanded/char_tracing_async.rs +++ b/crates/component-macro/tests/expanded/char_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -150,12 +155,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::chars::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::chars::Host + Send, T: 'static + Send, { @@ -173,21 +191,35 @@ pub mod foo { pub mod chars { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { /// A function that accepts a character - async fn take_char(&mut self, x: char) -> (); + fn take_char( + &mut self, + x: char, + ) -> impl ::core::future::Future + Send; /// A function that returns a character - async fn return_char(&mut self) -> char; + fn return_char( + &mut self, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { /// A function that accepts a character - async fn take_char(&mut self, x: char) -> () { - Host::take_char(*self, x).await + fn take_char( + &mut self, + x: char, + ) -> impl ::core::future::Future + Send { + async move { Host::take_char(*self, x).await } } /// A function that returns a character - async fn return_char(&mut self) -> char { - Host::return_char(*self).await + fn return_char( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::return_char(*self).await } } } pub fn add_to_linker( @@ -195,7 +227,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -298,7 +330,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/chars` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/conventions.rs b/crates/component-macro/tests/expanded/conventions.rs index 4845819244..6e8dc054ab 100644 --- a/crates/component-macro/tests/expanded/conventions.rs +++ b/crates/component-macro/tests/expanded/conventions.rs @@ -52,6 +52,17 @@ impl<_T: 'static> TheWorldPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `the-world`. /// @@ -146,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::conventions::HostWithStore, for<'a> D::Data<'a>: foo::foo::conventions::Host, T: 'static, { @@ -201,6 +225,11 @@ pub mod foo { >::ALIGN32 ); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn kebab_case(&mut self) -> (); fn foo(&mut self, x: LudicrousSpeed) -> (); @@ -270,7 +299,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -478,7 +507,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/conventions` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/conventions_async.rs b/crates/component-macro/tests/expanded/conventions_async.rs index 3ff91f38b5..8c43e152d1 100644 --- a/crates/component-macro/tests/expanded/conventions_async.rs +++ b/crates/component-macro/tests/expanded/conventions_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::conventions::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::conventions::Host + Send, T: 'static + Send, { @@ -207,69 +225,107 @@ pub mod foo { >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn kebab_case(&mut self) -> (); - async fn foo(&mut self, x: LudicrousSpeed) -> (); - async fn function_with_dashes(&mut self) -> (); - async fn function_with_no_weird_characters(&mut self) -> (); - async fn apple(&mut self) -> (); - async fn apple_pear(&mut self) -> (); - async fn apple_pear_grape(&mut self) -> (); - async fn a0(&mut self) -> (); + fn kebab_case( + &mut self, + ) -> impl ::core::future::Future + Send; + fn foo( + &mut self, + x: LudicrousSpeed, + ) -> impl ::core::future::Future + Send; + fn function_with_dashes( + &mut self, + ) -> impl ::core::future::Future + Send; + fn function_with_no_weird_characters( + &mut self, + ) -> impl ::core::future::Future + Send; + fn apple(&mut self) -> impl ::core::future::Future + Send; + fn apple_pear( + &mut self, + ) -> impl ::core::future::Future + Send; + fn apple_pear_grape( + &mut self, + ) -> impl ::core::future::Future + Send; + fn a0(&mut self) -> impl ::core::future::Future + Send; /// Comment out identifiers that collide when mapped to snake_case, for now; see /// https://github.com/WebAssembly/component-model/issues/118 /// APPLE: func() /// APPLE-pear-GRAPE: func() /// apple-PEAR-grape: func() - async fn is_xml(&mut self) -> (); - async fn explicit(&mut self) -> (); - async fn explicit_kebab(&mut self) -> (); + fn is_xml(&mut self) -> impl ::core::future::Future + Send; + fn explicit( + &mut self, + ) -> impl ::core::future::Future + Send; + fn explicit_kebab( + &mut self, + ) -> impl ::core::future::Future + Send; /// Identifiers with the same name as keywords are quoted. - async fn bool(&mut self) -> (); + fn bool(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn kebab_case(&mut self) -> () { - Host::kebab_case(*self).await + fn kebab_case( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::kebab_case(*self).await } } - async fn foo(&mut self, x: LudicrousSpeed) -> () { - Host::foo(*self, x).await + fn foo( + &mut self, + x: LudicrousSpeed, + ) -> impl ::core::future::Future + Send { + async move { Host::foo(*self, x).await } } - async fn function_with_dashes(&mut self) -> () { - Host::function_with_dashes(*self).await + fn function_with_dashes( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::function_with_dashes(*self).await } } - async fn function_with_no_weird_characters(&mut self) -> () { - Host::function_with_no_weird_characters(*self).await + fn function_with_no_weird_characters( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::function_with_no_weird_characters(*self).await } } - async fn apple(&mut self) -> () { - Host::apple(*self).await + fn apple(&mut self) -> impl ::core::future::Future + Send { + async move { Host::apple(*self).await } } - async fn apple_pear(&mut self) -> () { - Host::apple_pear(*self).await + fn apple_pear( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::apple_pear(*self).await } } - async fn apple_pear_grape(&mut self) -> () { - Host::apple_pear_grape(*self).await + fn apple_pear_grape( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::apple_pear_grape(*self).await } } - async fn a0(&mut self) -> () { - Host::a0(*self).await + fn a0(&mut self) -> impl ::core::future::Future + Send { + async move { Host::a0(*self).await } } /// Comment out identifiers that collide when mapped to snake_case, for now; see /// https://github.com/WebAssembly/component-model/issues/118 /// APPLE: func() /// APPLE-pear-GRAPE: func() /// apple-PEAR-grape: func() - async fn is_xml(&mut self) -> () { - Host::is_xml(*self).await + fn is_xml(&mut self) -> impl ::core::future::Future + Send { + async move { Host::is_xml(*self).await } } - async fn explicit(&mut self) -> () { - Host::explicit(*self).await + fn explicit( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::explicit(*self).await } } - async fn explicit_kebab(&mut self) -> () { - Host::explicit_kebab(*self).await + fn explicit_kebab( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::explicit_kebab(*self).await } } /// Identifiers with the same name as keywords are quoted. - async fn bool(&mut self) -> () { - Host::bool(*self).await + fn bool(&mut self) -> impl ::core::future::Future + Send { + async move { Host::bool(*self).await } } } pub fn add_to_linker( @@ -277,7 +333,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -509,7 +565,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/conventions` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/conventions_concurrent.rs b/crates/component-macro/tests/expanded/conventions_concurrent.rs index d28cac5e71..9c4e16e970 100644 --- a/crates/component-macro/tests/expanded/conventions_concurrent.rs +++ b/crates/component-macro/tests/expanded/conventions_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::conventions::HostConcurrent + Send, + D: foo::foo::conventions::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::conventions::Host + Send, T: 'static + Send, { @@ -207,49 +225,32 @@ pub mod foo { >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn kebab_case( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn foo( accessor: &wasmtime::component::Accessor, x: LudicrousSpeed, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn function_with_dashes( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn function_with_no_weird_characters( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn apple( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn apple_pear( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn apple_pear_grape( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn a0( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; /// Comment out identifiers that collide when mapped to snake_case, for now; see /// https://github.com/WebAssembly/component-model/issues/118 /// APPLE: func() @@ -257,27 +258,18 @@ pub mod foo { /// apple-PEAR-grape: func() fn is_xml( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn explicit( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn explicit_kebab( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; /// Identifiers with the same name as keywords are quoted. fn bool( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -285,7 +277,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -295,7 +287,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::kebab_case(accessor).await; + let r = ::kebab_case(accessor).await; Ok(r) }) }, @@ -308,7 +300,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::foo(accessor, arg0).await; + let r = ::foo(accessor, arg0).await; Ok(r) }) }, @@ -318,7 +310,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::function_with_dashes(accessor) + let r = ::function_with_dashes(accessor) .await; Ok(r) }) @@ -329,7 +321,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::function_with_no_weird_characters( + let r = ::function_with_no_weird_characters( accessor, ) .await; @@ -342,7 +334,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::apple(accessor).await; + let r = ::apple(accessor).await; Ok(r) }) }, @@ -352,7 +344,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::apple_pear(accessor).await; + let r = ::apple_pear(accessor).await; Ok(r) }) }, @@ -362,7 +354,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::apple_pear_grape(accessor) + let r = ::apple_pear_grape(accessor) .await; Ok(r) }) @@ -373,7 +365,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::a0(accessor).await; + let r = ::a0(accessor).await; Ok(r) }) }, @@ -383,7 +375,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::is_xml(accessor).await; + let r = ::is_xml(accessor).await; Ok(r) }) }, @@ -393,7 +385,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::explicit(accessor).await; + let r = ::explicit(accessor).await; Ok(r) }) }, @@ -403,8 +395,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::explicit_kebab(accessor) - .await; + let r = ::explicit_kebab(accessor).await; Ok(r) }) }, @@ -414,7 +405,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::bool(accessor).await; + let r = ::bool(accessor).await; Ok(r) }) }, diff --git a/crates/component-macro/tests/expanded/conventions_tracing_async.rs b/crates/component-macro/tests/expanded/conventions_tracing_async.rs index c7782896a3..5f2dcaf078 100644 --- a/crates/component-macro/tests/expanded/conventions_tracing_async.rs +++ b/crates/component-macro/tests/expanded/conventions_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::conventions::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::conventions::Host + Send, T: 'static + Send, { @@ -207,69 +225,107 @@ pub mod foo { >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn kebab_case(&mut self) -> (); - async fn foo(&mut self, x: LudicrousSpeed) -> (); - async fn function_with_dashes(&mut self) -> (); - async fn function_with_no_weird_characters(&mut self) -> (); - async fn apple(&mut self) -> (); - async fn apple_pear(&mut self) -> (); - async fn apple_pear_grape(&mut self) -> (); - async fn a0(&mut self) -> (); + fn kebab_case( + &mut self, + ) -> impl ::core::future::Future + Send; + fn foo( + &mut self, + x: LudicrousSpeed, + ) -> impl ::core::future::Future + Send; + fn function_with_dashes( + &mut self, + ) -> impl ::core::future::Future + Send; + fn function_with_no_weird_characters( + &mut self, + ) -> impl ::core::future::Future + Send; + fn apple(&mut self) -> impl ::core::future::Future + Send; + fn apple_pear( + &mut self, + ) -> impl ::core::future::Future + Send; + fn apple_pear_grape( + &mut self, + ) -> impl ::core::future::Future + Send; + fn a0(&mut self) -> impl ::core::future::Future + Send; /// Comment out identifiers that collide when mapped to snake_case, for now; see /// https://github.com/WebAssembly/component-model/issues/118 /// APPLE: func() /// APPLE-pear-GRAPE: func() /// apple-PEAR-grape: func() - async fn is_xml(&mut self) -> (); - async fn explicit(&mut self) -> (); - async fn explicit_kebab(&mut self) -> (); + fn is_xml(&mut self) -> impl ::core::future::Future + Send; + fn explicit( + &mut self, + ) -> impl ::core::future::Future + Send; + fn explicit_kebab( + &mut self, + ) -> impl ::core::future::Future + Send; /// Identifiers with the same name as keywords are quoted. - async fn bool(&mut self) -> (); + fn bool(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn kebab_case(&mut self) -> () { - Host::kebab_case(*self).await + fn kebab_case( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::kebab_case(*self).await } } - async fn foo(&mut self, x: LudicrousSpeed) -> () { - Host::foo(*self, x).await + fn foo( + &mut self, + x: LudicrousSpeed, + ) -> impl ::core::future::Future + Send { + async move { Host::foo(*self, x).await } } - async fn function_with_dashes(&mut self) -> () { - Host::function_with_dashes(*self).await + fn function_with_dashes( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::function_with_dashes(*self).await } } - async fn function_with_no_weird_characters(&mut self) -> () { - Host::function_with_no_weird_characters(*self).await + fn function_with_no_weird_characters( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::function_with_no_weird_characters(*self).await } } - async fn apple(&mut self) -> () { - Host::apple(*self).await + fn apple(&mut self) -> impl ::core::future::Future + Send { + async move { Host::apple(*self).await } } - async fn apple_pear(&mut self) -> () { - Host::apple_pear(*self).await + fn apple_pear( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::apple_pear(*self).await } } - async fn apple_pear_grape(&mut self) -> () { - Host::apple_pear_grape(*self).await + fn apple_pear_grape( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::apple_pear_grape(*self).await } } - async fn a0(&mut self) -> () { - Host::a0(*self).await + fn a0(&mut self) -> impl ::core::future::Future + Send { + async move { Host::a0(*self).await } } /// Comment out identifiers that collide when mapped to snake_case, for now; see /// https://github.com/WebAssembly/component-model/issues/118 /// APPLE: func() /// APPLE-pear-GRAPE: func() /// apple-PEAR-grape: func() - async fn is_xml(&mut self) -> () { - Host::is_xml(*self).await + fn is_xml(&mut self) -> impl ::core::future::Future + Send { + async move { Host::is_xml(*self).await } } - async fn explicit(&mut self) -> () { - Host::explicit(*self).await + fn explicit( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::explicit(*self).await } } - async fn explicit_kebab(&mut self) -> () { - Host::explicit_kebab(*self).await + fn explicit_kebab( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::explicit_kebab(*self).await } } /// Identifiers with the same name as keywords are quoted. - async fn bool(&mut self) -> () { - Host::bool(*self).await + fn bool(&mut self) -> impl ::core::future::Future + Send { + async move { Host::bool(*self).await } } } pub fn add_to_linker( @@ -277,7 +333,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -669,7 +725,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/conventions` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/dead-code.rs b/crates/component-macro/tests/expanded/dead-code.rs index af37a12303..147d40b2ec 100644 --- a/crates/component-macro/tests/expanded/dead-code.rs +++ b/crates/component-macro/tests/expanded/dead-code.rs @@ -52,6 +52,17 @@ impl<_T: 'static> ImportsPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> ImportsPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `imports`. /// @@ -138,12 +149,26 @@ const _: () = { let indices = ImportsIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`ImportsPre::new`] and + /// [`ImportsPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + ImportsPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: a::b::interface_with_live_type::HostWithStore + + a::b::interface_with_dead_type::HostWithStore, for<'a> D::Data< 'a, >: a::b::interface_with_live_type::Host @@ -182,6 +207,11 @@ pub mod a { 4 == < LiveType as wasmtime::component::ComponentType >::ALIGN32 ); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn f(&mut self) -> LiveType; } @@ -195,7 +225,7 @@ pub mod a { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -265,6 +295,11 @@ pub mod a { assert!(8 == < V as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < V as wasmtime::component::ComponentType >::ALIGN32); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host {} impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( @@ -272,7 +307,7 @@ pub mod a { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { diff --git a/crates/component-macro/tests/expanded/dead-code_async.rs b/crates/component-macro/tests/expanded/dead-code_async.rs index a765e8886a..cb7c088904 100644 --- a/crates/component-macro/tests/expanded/dead-code_async.rs +++ b/crates/component-macro/tests/expanded/dead-code_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> ImportsPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> ImportsPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct ImportsIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Imports::instantiate_async`] which only needs a +/// [`Imports::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`ImportsPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`ImportsPre::instantiate_async`] to +/// method then uses [`ImportsPre::instantiate`] to /// create a [`Imports`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Imports { /// Convenience wrapper around [`ImportsPre::new`] and - /// [`ImportsPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`ImportsPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - ImportsPre::new(pre)?.instantiate_async(store).await + ImportsPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`ImportsIndices::new`] and /// [`ImportsIndices::load`]. @@ -144,12 +149,26 @@ const _: () = { let indices = ImportsIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`ImportsPre::new`] and + /// [`ImportsPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + ImportsPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: a::b::interface_with_live_type::HostWithStore + + a::b::interface_with_dead_type::HostWithStore + Send, for<'a> D::Data< 'a, >: a::b::interface_with_live_type::Host @@ -188,13 +207,19 @@ pub mod a { 4 == < LiveType as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn f(&mut self) -> LiveType; + fn f(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn f(&mut self) -> LiveType { - Host::f(*self).await + fn f( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::f(*self).await } } } pub fn add_to_linker( @@ -202,7 +227,7 @@ pub mod a { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -274,17 +299,21 @@ pub mod a { assert!(8 == < V as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < V as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("a:b/interface-with-dead-type")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/dead-code_concurrent.rs b/crates/component-macro/tests/expanded/dead-code_concurrent.rs index caf5db15d8..48a3e7be18 100644 --- a/crates/component-macro/tests/expanded/dead-code_concurrent.rs +++ b/crates/component-macro/tests/expanded/dead-code_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> ImportsPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> ImportsPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct ImportsIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Imports::instantiate_async`] which only needs a +/// [`Imports::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`ImportsPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`ImportsPre::instantiate_async`] to +/// method then uses [`ImportsPre::instantiate`] to /// create a [`Imports`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Imports { /// Convenience wrapper around [`ImportsPre::new`] and - /// [`ImportsPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`ImportsPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - ImportsPre::new(pre)?.instantiate_async(store).await + ImportsPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`ImportsIndices::new`] and /// [`ImportsIndices::load`]. @@ -144,12 +149,26 @@ const _: () = { let indices = ImportsIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`ImportsPre::new`] and + /// [`ImportsPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + ImportsPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: a::b::interface_with_live_type::HostConcurrent + Send, + D: a::b::interface_with_live_type::HostWithStore + + a::b::interface_with_dead_type::HostWithStore + Send, for<'a> D::Data< 'a, >: a::b::interface_with_live_type::Host @@ -188,15 +207,11 @@ pub mod a { 4 == < LiveType as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn f( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -204,7 +219,7 @@ pub mod a { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -214,7 +229,7 @@ pub mod a { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::f(accessor).await; + let r = ::f(accessor).await; Ok((r,)) }) }, @@ -276,17 +291,21 @@ pub mod a { assert!(8 == < V as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < V as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("a:b/interface-with-dead-type")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/dead-code_tracing_async.rs b/crates/component-macro/tests/expanded/dead-code_tracing_async.rs index 7b187c2e62..23e631fb8f 100644 --- a/crates/component-macro/tests/expanded/dead-code_tracing_async.rs +++ b/crates/component-macro/tests/expanded/dead-code_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> ImportsPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> ImportsPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct ImportsIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Imports::instantiate_async`] which only needs a +/// [`Imports::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`ImportsPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`ImportsPre::instantiate_async`] to +/// method then uses [`ImportsPre::instantiate`] to /// create a [`Imports`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Imports { /// Convenience wrapper around [`ImportsPre::new`] and - /// [`ImportsPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`ImportsPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - ImportsPre::new(pre)?.instantiate_async(store).await + ImportsPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`ImportsIndices::new`] and /// [`ImportsIndices::load`]. @@ -144,12 +149,26 @@ const _: () = { let indices = ImportsIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`ImportsPre::new`] and + /// [`ImportsPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + ImportsPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: a::b::interface_with_live_type::HostWithStore + + a::b::interface_with_dead_type::HostWithStore + Send, for<'a> D::Data< 'a, >: a::b::interface_with_live_type::Host @@ -188,13 +207,19 @@ pub mod a { 4 == < LiveType as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn f(&mut self) -> LiveType; + fn f(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn f(&mut self) -> LiveType { - Host::f(*self).await + fn f( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::f(*self).await } } } pub fn add_to_linker( @@ -202,7 +227,7 @@ pub mod a { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -287,17 +312,21 @@ pub mod a { assert!(8 == < V as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < V as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("a:b/interface-with-dead-type")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/direct-import.rs b/crates/component-macro/tests/expanded/direct-import.rs index 55e0f1943f..387956071b 100644 --- a/crates/component-macro/tests/expanded/direct-import.rs +++ b/crates/component-macro/tests/expanded/direct-import.rs @@ -52,6 +52,17 @@ impl<_T: 'static> FooPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> FooPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `foo`. /// @@ -87,6 +98,11 @@ pub struct FooIndices {} /// [`Component`]: wasmtime::component::Component /// [`Linker`]: wasmtime::component::Linker pub struct Foo {} +pub trait FooImportsWithStore: wasmtime::component::HasData {} +impl<_T: ?Sized> FooImportsWithStore for _T +where + _T: wasmtime::component::HasData, +{} pub trait FooImports { fn foo(&mut self) -> (); } @@ -146,12 +162,25 @@ const _: () = { let indices = FooIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker_imports( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: FooImportsWithStore, for<'a> D::Data<'a>: FooImports, T: 'static, { @@ -172,7 +201,7 @@ const _: () = { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: FooImportsWithStore, for<'a> D::Data<'a>: FooImports, T: 'static, { diff --git a/crates/component-macro/tests/expanded/direct-import_async.rs b/crates/component-macro/tests/expanded/direct-import_async.rs index 76c0eefee2..5c5b53cdb4 100644 --- a/crates/component-macro/tests/expanded/direct-import_async.rs +++ b/crates/component-macro/tests/expanded/direct-import_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> FooPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> FooPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct FooIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Foo::instantiate_async`] which only needs a +/// [`Foo::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`FooPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`FooPre::instantiate_async`] to +/// method then uses [`FooPre::instantiate`] to /// create a [`Foo`]. /// /// * If you've instantiated the instance yourself already @@ -90,13 +98,17 @@ pub struct FooIndices {} /// [`Component`]: wasmtime::component::Component /// [`Linker`]: wasmtime::component::Linker pub struct Foo {} -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] +pub trait FooImportsWithStore: wasmtime::component::HasData + Send {} +impl<_T: ?Sized> FooImportsWithStore for _T +where + _T: wasmtime::component::HasData + Send, +{} pub trait FooImports: Send { - async fn foo(&mut self) -> (); + fn foo(&mut self) -> impl ::core::future::Future + Send; } impl<_T: FooImports + ?Sized + Send> FooImports for &mut _T { - async fn foo(&mut self) -> () { - FooImports::foo(*self).await + fn foo(&mut self) -> impl ::core::future::Future + Send { + async move { FooImports::foo(*self).await } } } const _: () = { @@ -132,17 +144,14 @@ const _: () = { } impl Foo { /// Convenience wrapper around [`FooPre::new`] and - /// [`FooPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`FooPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - FooPre::new(pre)?.instantiate_async(store).await + FooPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`FooIndices::new`] and /// [`FooIndices::load`]. @@ -153,12 +162,25 @@ const _: () = { let indices = FooIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker_imports( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: FooImportsWithStore, for<'a> D::Data<'a>: FooImports, T: 'static + Send, { @@ -181,7 +203,7 @@ const _: () = { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: FooImportsWithStore + Send, for<'a> D::Data<'a>: FooImports + Send, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/direct-import_concurrent.rs b/crates/component-macro/tests/expanded/direct-import_concurrent.rs index e5d0de6d1f..af13e65bc0 100644 --- a/crates/component-macro/tests/expanded/direct-import_concurrent.rs +++ b/crates/component-macro/tests/expanded/direct-import_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> FooPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> FooPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct FooIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Foo::instantiate_async`] which only needs a +/// [`Foo::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`FooPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`FooPre::instantiate_async`] to +/// method then uses [`FooPre::instantiate`] to /// create a [`Foo`]. /// /// * If you've instantiated the instance yourself already @@ -90,15 +98,11 @@ pub struct FooIndices {} /// [`Component`]: wasmtime::component::Component /// [`Linker`]: wasmtime::component::Linker pub struct Foo {} -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] -pub trait FooImportsConcurrent: wasmtime::component::HasData + Send { +pub trait FooImportsWithStore: wasmtime::component::HasData + Send { fn foo( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait FooImports: Send {} impl<_T: FooImports + ?Sized + Send> FooImports for &mut _T {} const _: () = { @@ -134,17 +138,14 @@ const _: () = { } impl Foo { /// Convenience wrapper around [`FooPre::new`] and - /// [`FooPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`FooPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - FooPre::new(pre)?.instantiate_async(store).await + FooPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`FooIndices::new`] and /// [`FooIndices::load`]. @@ -155,12 +156,25 @@ const _: () = { let indices = FooIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker_imports( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: FooImportsConcurrent, + D: FooImportsWithStore, for<'a> D::Data<'a>: FooImports, T: 'static + Send, { @@ -171,7 +185,7 @@ const _: () = { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::foo(accessor).await; + let r = ::foo(accessor).await; Ok(r) }) }, @@ -183,7 +197,7 @@ const _: () = { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: FooImportsConcurrent + Send, + D: FooImportsWithStore + Send, for<'a> D::Data<'a>: FooImports + Send, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/direct-import_tracing_async.rs b/crates/component-macro/tests/expanded/direct-import_tracing_async.rs index 9213dd4d53..20bfe56554 100644 --- a/crates/component-macro/tests/expanded/direct-import_tracing_async.rs +++ b/crates/component-macro/tests/expanded/direct-import_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> FooPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> FooPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct FooIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Foo::instantiate_async`] which only needs a +/// [`Foo::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`FooPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`FooPre::instantiate_async`] to +/// method then uses [`FooPre::instantiate`] to /// create a [`Foo`]. /// /// * If you've instantiated the instance yourself already @@ -90,13 +98,17 @@ pub struct FooIndices {} /// [`Component`]: wasmtime::component::Component /// [`Linker`]: wasmtime::component::Linker pub struct Foo {} -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] +pub trait FooImportsWithStore: wasmtime::component::HasData + Send {} +impl<_T: ?Sized> FooImportsWithStore for _T +where + _T: wasmtime::component::HasData + Send, +{} pub trait FooImports: Send { - async fn foo(&mut self) -> (); + fn foo(&mut self) -> impl ::core::future::Future + Send; } impl<_T: FooImports + ?Sized + Send> FooImports for &mut _T { - async fn foo(&mut self) -> () { - FooImports::foo(*self).await + fn foo(&mut self) -> impl ::core::future::Future + Send { + async move { FooImports::foo(*self).await } } } const _: () = { @@ -132,17 +144,14 @@ const _: () = { } impl Foo { /// Convenience wrapper around [`FooPre::new`] and - /// [`FooPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`FooPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - FooPre::new(pre)?.instantiate_async(store).await + FooPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`FooIndices::new`] and /// [`FooIndices::load`]. @@ -153,12 +162,25 @@ const _: () = { let indices = FooIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker_imports( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: FooImportsWithStore, for<'a> D::Data<'a>: FooImports, T: 'static + Send, { @@ -194,7 +216,7 @@ const _: () = { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: FooImportsWithStore + Send, for<'a> D::Data<'a>: FooImports + Send, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/empty.rs b/crates/component-macro/tests/expanded/empty.rs index 0163ed5a8a..e0bbef06aa 100644 --- a/crates/component-macro/tests/expanded/empty.rs +++ b/crates/component-macro/tests/expanded/empty.rs @@ -52,6 +52,17 @@ impl<_T: 'static> EmptyPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> EmptyPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `empty`. /// @@ -138,5 +149,18 @@ const _: () = { let indices = EmptyIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`EmptyPre::new`] and + /// [`EmptyPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + EmptyPre::new(pre)?.instantiate_async(store).await + } } }; diff --git a/crates/component-macro/tests/expanded/empty_async.rs b/crates/component-macro/tests/expanded/empty_async.rs index d86cbd390e..e0bbef06aa 100644 --- a/crates/component-macro/tests/expanded/empty_async.rs +++ b/crates/component-macro/tests/expanded/empty_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> EmptyPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> EmptyPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct EmptyIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Empty::instantiate_async`] which only needs a +/// [`Empty::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`EmptyPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`EmptyPre::instantiate_async`] to +/// method then uses [`EmptyPre::instantiate`] to /// create a [`Empty`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Empty { /// Convenience wrapper around [`EmptyPre::new`] and - /// [`EmptyPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`EmptyPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - EmptyPre::new(pre)?.instantiate_async(store).await + EmptyPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`EmptyIndices::new`] and /// [`EmptyIndices::load`]. @@ -144,5 +149,18 @@ const _: () = { let indices = EmptyIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`EmptyPre::new`] and + /// [`EmptyPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + EmptyPre::new(pre)?.instantiate_async(store).await + } } }; diff --git a/crates/component-macro/tests/expanded/empty_concurrent.rs b/crates/component-macro/tests/expanded/empty_concurrent.rs index d86cbd390e..e0bbef06aa 100644 --- a/crates/component-macro/tests/expanded/empty_concurrent.rs +++ b/crates/component-macro/tests/expanded/empty_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> EmptyPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> EmptyPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct EmptyIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Empty::instantiate_async`] which only needs a +/// [`Empty::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`EmptyPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`EmptyPre::instantiate_async`] to +/// method then uses [`EmptyPre::instantiate`] to /// create a [`Empty`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Empty { /// Convenience wrapper around [`EmptyPre::new`] and - /// [`EmptyPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`EmptyPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - EmptyPre::new(pre)?.instantiate_async(store).await + EmptyPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`EmptyIndices::new`] and /// [`EmptyIndices::load`]. @@ -144,5 +149,18 @@ const _: () = { let indices = EmptyIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`EmptyPre::new`] and + /// [`EmptyPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + EmptyPre::new(pre)?.instantiate_async(store).await + } } }; diff --git a/crates/component-macro/tests/expanded/empty_tracing_async.rs b/crates/component-macro/tests/expanded/empty_tracing_async.rs index d86cbd390e..e0bbef06aa 100644 --- a/crates/component-macro/tests/expanded/empty_tracing_async.rs +++ b/crates/component-macro/tests/expanded/empty_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> EmptyPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> EmptyPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct EmptyIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Empty::instantiate_async`] which only needs a +/// [`Empty::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`EmptyPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`EmptyPre::instantiate_async`] to +/// method then uses [`EmptyPre::instantiate`] to /// create a [`Empty`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Empty { /// Convenience wrapper around [`EmptyPre::new`] and - /// [`EmptyPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`EmptyPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - EmptyPre::new(pre)?.instantiate_async(store).await + EmptyPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`EmptyIndices::new`] and /// [`EmptyIndices::load`]. @@ -144,5 +149,18 @@ const _: () = { let indices = EmptyIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`EmptyPre::new`] and + /// [`EmptyPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + EmptyPre::new(pre)?.instantiate_async(store).await + } } }; diff --git a/crates/component-macro/tests/expanded/flags.rs b/crates/component-macro/tests/expanded/flags.rs index d33669e4e1..c45bdb6279 100644 --- a/crates/component-macro/tests/expanded/flags.rs +++ b/crates/component-macro/tests/expanded/flags.rs @@ -52,6 +52,17 @@ impl<_T: 'static> TheFlagsPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> TheFlagsPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `the-flags`. /// @@ -144,12 +155,25 @@ const _: () = { let indices = TheFlagsIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheFlagsPre::new`] and + /// [`TheFlagsPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheFlagsPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::flegs::HostWithStore, for<'a> D::Data<'a>: foo::foo::flegs::Host, T: 'static, { @@ -281,6 +305,11 @@ pub mod foo { assert!(8 == < Flag64 as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Flag64 as wasmtime::component::ComponentType >::ALIGN32); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn roundtrip_flag1(&mut self, x: Flag1) -> Flag1; fn roundtrip_flag2(&mut self, x: Flag2) -> Flag2; @@ -318,7 +347,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -605,7 +634,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/flegs` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/flags_async.rs b/crates/component-macro/tests/expanded/flags_async.rs index b57d805222..7f4374215e 100644 --- a/crates/component-macro/tests/expanded/flags_async.rs +++ b/crates/component-macro/tests/expanded/flags_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheFlagsPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheFlagsPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheFlagsIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheFlags::instantiate_async`] which only needs a +/// [`TheFlags::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheFlagsPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheFlagsPre::instantiate_async`] to +/// method then uses [`TheFlagsPre::instantiate`] to /// create a [`TheFlags`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl TheFlags { /// Convenience wrapper around [`TheFlagsPre::new`] and - /// [`TheFlagsPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheFlagsPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheFlagsPre::new(pre)?.instantiate_async(store).await + TheFlagsPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheFlagsIndices::new`] and /// [`TheFlagsIndices::load`]. @@ -150,12 +155,25 @@ const _: () = { let indices = TheFlagsIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheFlagsPre::new`] and + /// [`TheFlagsPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheFlagsPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::flegs::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::flegs::Host + Send, T: 'static + Send, { @@ -287,37 +305,83 @@ pub mod foo { assert!(8 == < Flag64 as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Flag64 as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn roundtrip_flag1(&mut self, x: Flag1) -> Flag1; - async fn roundtrip_flag2(&mut self, x: Flag2) -> Flag2; - async fn roundtrip_flag4(&mut self, x: Flag4) -> Flag4; - async fn roundtrip_flag8(&mut self, x: Flag8) -> Flag8; - async fn roundtrip_flag16(&mut self, x: Flag16) -> Flag16; - async fn roundtrip_flag32(&mut self, x: Flag32) -> Flag32; - async fn roundtrip_flag64(&mut self, x: Flag64) -> Flag64; + fn roundtrip_flag1( + &mut self, + x: Flag1, + ) -> impl ::core::future::Future + Send; + fn roundtrip_flag2( + &mut self, + x: Flag2, + ) -> impl ::core::future::Future + Send; + fn roundtrip_flag4( + &mut self, + x: Flag4, + ) -> impl ::core::future::Future + Send; + fn roundtrip_flag8( + &mut self, + x: Flag8, + ) -> impl ::core::future::Future + Send; + fn roundtrip_flag16( + &mut self, + x: Flag16, + ) -> impl ::core::future::Future + Send; + fn roundtrip_flag32( + &mut self, + x: Flag32, + ) -> impl ::core::future::Future + Send; + fn roundtrip_flag64( + &mut self, + x: Flag64, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn roundtrip_flag1(&mut self, x: Flag1) -> Flag1 { - Host::roundtrip_flag1(*self, x).await + fn roundtrip_flag1( + &mut self, + x: Flag1, + ) -> impl ::core::future::Future + Send { + async move { Host::roundtrip_flag1(*self, x).await } } - async fn roundtrip_flag2(&mut self, x: Flag2) -> Flag2 { - Host::roundtrip_flag2(*self, x).await + fn roundtrip_flag2( + &mut self, + x: Flag2, + ) -> impl ::core::future::Future + Send { + async move { Host::roundtrip_flag2(*self, x).await } } - async fn roundtrip_flag4(&mut self, x: Flag4) -> Flag4 { - Host::roundtrip_flag4(*self, x).await + fn roundtrip_flag4( + &mut self, + x: Flag4, + ) -> impl ::core::future::Future + Send { + async move { Host::roundtrip_flag4(*self, x).await } } - async fn roundtrip_flag8(&mut self, x: Flag8) -> Flag8 { - Host::roundtrip_flag8(*self, x).await + fn roundtrip_flag8( + &mut self, + x: Flag8, + ) -> impl ::core::future::Future + Send { + async move { Host::roundtrip_flag8(*self, x).await } } - async fn roundtrip_flag16(&mut self, x: Flag16) -> Flag16 { - Host::roundtrip_flag16(*self, x).await + fn roundtrip_flag16( + &mut self, + x: Flag16, + ) -> impl ::core::future::Future + Send { + async move { Host::roundtrip_flag16(*self, x).await } } - async fn roundtrip_flag32(&mut self, x: Flag32) -> Flag32 { - Host::roundtrip_flag32(*self, x).await + fn roundtrip_flag32( + &mut self, + x: Flag32, + ) -> impl ::core::future::Future + Send { + async move { Host::roundtrip_flag32(*self, x).await } } - async fn roundtrip_flag64(&mut self, x: Flag64) -> Flag64 { - Host::roundtrip_flag64(*self, x).await + fn roundtrip_flag64( + &mut self, + x: Flag64, + ) -> impl ::core::future::Future + Send { + async move { Host::roundtrip_flag64(*self, x).await } } } pub fn add_to_linker( @@ -325,7 +389,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -626,7 +690,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/flegs` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/flags_concurrent.rs b/crates/component-macro/tests/expanded/flags_concurrent.rs index e92bde4e0d..dc33af2145 100644 --- a/crates/component-macro/tests/expanded/flags_concurrent.rs +++ b/crates/component-macro/tests/expanded/flags_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheFlagsPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheFlagsPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheFlagsIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheFlags::instantiate_async`] which only needs a +/// [`TheFlags::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheFlagsPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheFlagsPre::instantiate_async`] to +/// method then uses [`TheFlagsPre::instantiate`] to /// create a [`TheFlags`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl TheFlags { /// Convenience wrapper around [`TheFlagsPre::new`] and - /// [`TheFlagsPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheFlagsPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheFlagsPre::new(pre)?.instantiate_async(store).await + TheFlagsPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheFlagsIndices::new`] and /// [`TheFlagsIndices::load`]. @@ -150,12 +155,25 @@ const _: () = { let indices = TheFlagsIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheFlagsPre::new`] and + /// [`TheFlagsPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheFlagsPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::flegs::HostConcurrent + Send, + D: foo::foo::flegs::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::flegs::Host + Send, T: 'static + Send, { @@ -287,52 +305,36 @@ pub mod foo { assert!(8 == < Flag64 as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Flag64 as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn roundtrip_flag1( accessor: &wasmtime::component::Accessor, x: Flag1, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn roundtrip_flag2( accessor: &wasmtime::component::Accessor, x: Flag2, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn roundtrip_flag4( accessor: &wasmtime::component::Accessor, x: Flag4, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn roundtrip_flag8( accessor: &wasmtime::component::Accessor, x: Flag8, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn roundtrip_flag16( accessor: &wasmtime::component::Accessor, x: Flag16, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn roundtrip_flag32( accessor: &wasmtime::component::Accessor, x: Flag32, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn roundtrip_flag64( accessor: &wasmtime::component::Accessor, x: Flag64, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -340,7 +342,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -350,10 +352,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (Flag1,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::roundtrip_flag1( - accessor, - arg0, - ) + let r = ::roundtrip_flag1(accessor, arg0) .await; Ok((r,)) }) @@ -364,10 +363,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (Flag2,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::roundtrip_flag2( - accessor, - arg0, - ) + let r = ::roundtrip_flag2(accessor, arg0) .await; Ok((r,)) }) @@ -378,10 +374,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (Flag4,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::roundtrip_flag4( - accessor, - arg0, - ) + let r = ::roundtrip_flag4(accessor, arg0) .await; Ok((r,)) }) @@ -392,10 +385,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (Flag8,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::roundtrip_flag8( - accessor, - arg0, - ) + let r = ::roundtrip_flag8(accessor, arg0) .await; Ok((r,)) }) @@ -406,7 +396,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (Flag16,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::roundtrip_flag16( + let r = ::roundtrip_flag16( accessor, arg0, ) @@ -420,7 +410,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (Flag32,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::roundtrip_flag32( + let r = ::roundtrip_flag32( accessor, arg0, ) @@ -434,7 +424,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (Flag64,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::roundtrip_flag64( + let r = ::roundtrip_flag64( accessor, arg0, ) diff --git a/crates/component-macro/tests/expanded/flags_tracing_async.rs b/crates/component-macro/tests/expanded/flags_tracing_async.rs index bf6b819d05..7030fcf65e 100644 --- a/crates/component-macro/tests/expanded/flags_tracing_async.rs +++ b/crates/component-macro/tests/expanded/flags_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheFlagsPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheFlagsPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheFlagsIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheFlags::instantiate_async`] which only needs a +/// [`TheFlags::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheFlagsPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheFlagsPre::instantiate_async`] to +/// method then uses [`TheFlagsPre::instantiate`] to /// create a [`TheFlags`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl TheFlags { /// Convenience wrapper around [`TheFlagsPre::new`] and - /// [`TheFlagsPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheFlagsPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheFlagsPre::new(pre)?.instantiate_async(store).await + TheFlagsPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheFlagsIndices::new`] and /// [`TheFlagsIndices::load`]. @@ -150,12 +155,25 @@ const _: () = { let indices = TheFlagsIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheFlagsPre::new`] and + /// [`TheFlagsPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheFlagsPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::flegs::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::flegs::Host + Send, T: 'static + Send, { @@ -287,37 +305,83 @@ pub mod foo { assert!(8 == < Flag64 as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Flag64 as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn roundtrip_flag1(&mut self, x: Flag1) -> Flag1; - async fn roundtrip_flag2(&mut self, x: Flag2) -> Flag2; - async fn roundtrip_flag4(&mut self, x: Flag4) -> Flag4; - async fn roundtrip_flag8(&mut self, x: Flag8) -> Flag8; - async fn roundtrip_flag16(&mut self, x: Flag16) -> Flag16; - async fn roundtrip_flag32(&mut self, x: Flag32) -> Flag32; - async fn roundtrip_flag64(&mut self, x: Flag64) -> Flag64; + fn roundtrip_flag1( + &mut self, + x: Flag1, + ) -> impl ::core::future::Future + Send; + fn roundtrip_flag2( + &mut self, + x: Flag2, + ) -> impl ::core::future::Future + Send; + fn roundtrip_flag4( + &mut self, + x: Flag4, + ) -> impl ::core::future::Future + Send; + fn roundtrip_flag8( + &mut self, + x: Flag8, + ) -> impl ::core::future::Future + Send; + fn roundtrip_flag16( + &mut self, + x: Flag16, + ) -> impl ::core::future::Future + Send; + fn roundtrip_flag32( + &mut self, + x: Flag32, + ) -> impl ::core::future::Future + Send; + fn roundtrip_flag64( + &mut self, + x: Flag64, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn roundtrip_flag1(&mut self, x: Flag1) -> Flag1 { - Host::roundtrip_flag1(*self, x).await + fn roundtrip_flag1( + &mut self, + x: Flag1, + ) -> impl ::core::future::Future + Send { + async move { Host::roundtrip_flag1(*self, x).await } } - async fn roundtrip_flag2(&mut self, x: Flag2) -> Flag2 { - Host::roundtrip_flag2(*self, x).await + fn roundtrip_flag2( + &mut self, + x: Flag2, + ) -> impl ::core::future::Future + Send { + async move { Host::roundtrip_flag2(*self, x).await } } - async fn roundtrip_flag4(&mut self, x: Flag4) -> Flag4 { - Host::roundtrip_flag4(*self, x).await + fn roundtrip_flag4( + &mut self, + x: Flag4, + ) -> impl ::core::future::Future + Send { + async move { Host::roundtrip_flag4(*self, x).await } } - async fn roundtrip_flag8(&mut self, x: Flag8) -> Flag8 { - Host::roundtrip_flag8(*self, x).await + fn roundtrip_flag8( + &mut self, + x: Flag8, + ) -> impl ::core::future::Future + Send { + async move { Host::roundtrip_flag8(*self, x).await } } - async fn roundtrip_flag16(&mut self, x: Flag16) -> Flag16 { - Host::roundtrip_flag16(*self, x).await + fn roundtrip_flag16( + &mut self, + x: Flag16, + ) -> impl ::core::future::Future + Send { + async move { Host::roundtrip_flag16(*self, x).await } } - async fn roundtrip_flag32(&mut self, x: Flag32) -> Flag32 { - Host::roundtrip_flag32(*self, x).await + fn roundtrip_flag32( + &mut self, + x: Flag32, + ) -> impl ::core::future::Future + Send { + async move { Host::roundtrip_flag32(*self, x).await } } - async fn roundtrip_flag64(&mut self, x: Flag64) -> Flag64 { - Host::roundtrip_flag64(*self, x).await + fn roundtrip_flag64( + &mut self, + x: Flag64, + ) -> impl ::core::future::Future + Send { + async move { Host::roundtrip_flag64(*self, x).await } } } pub fn add_to_linker( @@ -325,7 +389,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -738,7 +802,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/flegs` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/floats.rs b/crates/component-macro/tests/expanded/floats.rs index cc81bf36d1..98a56f77ba 100644 --- a/crates/component-macro/tests/expanded/floats.rs +++ b/crates/component-macro/tests/expanded/floats.rs @@ -52,6 +52,17 @@ impl<_T: 'static> TheWorldPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `the-world`. /// @@ -146,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::floats::HostWithStore, for<'a> D::Data<'a>: foo::foo::floats::Host, T: 'static, { @@ -169,6 +193,11 @@ pub mod foo { pub mod floats { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn f32_param(&mut self, x: f32) -> (); fn f64_param(&mut self, x: f64) -> (); @@ -194,7 +223,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -281,7 +310,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/floats` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/floats_async.rs b/crates/component-macro/tests/expanded/floats_async.rs index 26df588c87..52f43ee0c3 100644 --- a/crates/component-macro/tests/expanded/floats_async.rs +++ b/crates/component-macro/tests/expanded/floats_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::floats::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::floats::Host + Send, T: 'static + Send, { @@ -175,25 +193,49 @@ pub mod foo { pub mod floats { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn f32_param(&mut self, x: f32) -> (); - async fn f64_param(&mut self, x: f64) -> (); - async fn f32_result(&mut self) -> f32; - async fn f64_result(&mut self) -> f64; + fn f32_param( + &mut self, + x: f32, + ) -> impl ::core::future::Future + Send; + fn f64_param( + &mut self, + x: f64, + ) -> impl ::core::future::Future + Send; + fn f32_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn f64_result( + &mut self, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn f32_param(&mut self, x: f32) -> () { - Host::f32_param(*self, x).await + fn f32_param( + &mut self, + x: f32, + ) -> impl ::core::future::Future + Send { + async move { Host::f32_param(*self, x).await } } - async fn f64_param(&mut self, x: f64) -> () { - Host::f64_param(*self, x).await + fn f64_param( + &mut self, + x: f64, + ) -> impl ::core::future::Future + Send { + async move { Host::f64_param(*self, x).await } } - async fn f32_result(&mut self) -> f32 { - Host::f32_result(*self).await + fn f32_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::f32_result(*self).await } } - async fn f64_result(&mut self) -> f64 { - Host::f64_result(*self).await + fn f64_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::f64_result(*self).await } } } pub fn add_to_linker( @@ -201,7 +243,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -296,7 +338,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/floats` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/floats_concurrent.rs b/crates/component-macro/tests/expanded/floats_concurrent.rs index dd21ad33da..4074956a70 100644 --- a/crates/component-macro/tests/expanded/floats_concurrent.rs +++ b/crates/component-macro/tests/expanded/floats_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::floats::HostConcurrent + Send, + D: foo::foo::floats::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::floats::Host + Send, T: 'static + Send, { @@ -175,32 +193,22 @@ pub mod foo { pub mod floats { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn f32_param( accessor: &wasmtime::component::Accessor, x: f32, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn f64_param( accessor: &wasmtime::component::Accessor, x: f64, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn f32_result( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn f64_result( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -208,7 +216,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -218,7 +226,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (f32,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::f32_param(accessor, arg0) + let r = ::f32_param(accessor, arg0) .await; Ok(r) }) @@ -229,7 +237,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (f64,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::f64_param(accessor, arg0) + let r = ::f64_param(accessor, arg0) .await; Ok(r) }) @@ -240,7 +248,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::f32_result(accessor).await; + let r = ::f32_result(accessor).await; Ok((r,)) }) }, @@ -250,7 +258,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::f64_result(accessor).await; + let r = ::f64_result(accessor).await; Ok((r,)) }) }, diff --git a/crates/component-macro/tests/expanded/floats_tracing_async.rs b/crates/component-macro/tests/expanded/floats_tracing_async.rs index b370ccc781..e830d4ae59 100644 --- a/crates/component-macro/tests/expanded/floats_tracing_async.rs +++ b/crates/component-macro/tests/expanded/floats_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::floats::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::floats::Host + Send, T: 'static + Send, { @@ -175,25 +193,49 @@ pub mod foo { pub mod floats { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn f32_param(&mut self, x: f32) -> (); - async fn f64_param(&mut self, x: f64) -> (); - async fn f32_result(&mut self) -> f32; - async fn f64_result(&mut self) -> f64; + fn f32_param( + &mut self, + x: f32, + ) -> impl ::core::future::Future + Send; + fn f64_param( + &mut self, + x: f64, + ) -> impl ::core::future::Future + Send; + fn f32_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn f64_result( + &mut self, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn f32_param(&mut self, x: f32) -> () { - Host::f32_param(*self, x).await + fn f32_param( + &mut self, + x: f32, + ) -> impl ::core::future::Future + Send { + async move { Host::f32_param(*self, x).await } } - async fn f64_param(&mut self, x: f64) -> () { - Host::f64_param(*self, x).await + fn f64_param( + &mut self, + x: f64, + ) -> impl ::core::future::Future + Send { + async move { Host::f64_param(*self, x).await } } - async fn f32_result(&mut self) -> f32 { - Host::f32_result(*self).await + fn f32_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::f32_result(*self).await } } - async fn f64_result(&mut self) -> f64 { - Host::f64_result(*self).await + fn f64_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::f64_result(*self).await } } } pub fn add_to_linker( @@ -201,7 +243,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -354,7 +396,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/floats` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/function-new.rs b/crates/component-macro/tests/expanded/function-new.rs index 8eb3b4e99f..e316c531fa 100644 --- a/crates/component-macro/tests/expanded/function-new.rs +++ b/crates/component-macro/tests/expanded/function-new.rs @@ -52,6 +52,17 @@ impl<_T: 'static> FooPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> FooPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `foo`. /// @@ -158,6 +169,19 @@ const _: () = { let indices = FooIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } pub fn call_new( &self, mut store: S, diff --git a/crates/component-macro/tests/expanded/function-new_async.rs b/crates/component-macro/tests/expanded/function-new_async.rs index 9756b7fe8a..354c3148d8 100644 --- a/crates/component-macro/tests/expanded/function-new_async.rs +++ b/crates/component-macro/tests/expanded/function-new_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> FooPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> FooPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct FooIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Foo::instantiate_async`] which only needs a +/// [`Foo::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`FooPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`FooPre::instantiate_async`] to +/// method then uses [`FooPre::instantiate`] to /// create a [`Foo`]. /// /// * If you've instantiated the instance yourself already @@ -143,17 +151,14 @@ const _: () = { } impl Foo { /// Convenience wrapper around [`FooPre::new`] and - /// [`FooPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`FooPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - FooPre::new(pre)?.instantiate_async(store).await + FooPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`FooIndices::new`] and /// [`FooIndices::load`]. @@ -164,6 +169,19 @@ const _: () = { let indices = FooIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } pub async fn call_new( &self, mut store: S, diff --git a/crates/component-macro/tests/expanded/function-new_concurrent.rs b/crates/component-macro/tests/expanded/function-new_concurrent.rs index c26160d6b5..5d82a32c69 100644 --- a/crates/component-macro/tests/expanded/function-new_concurrent.rs +++ b/crates/component-macro/tests/expanded/function-new_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> FooPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> FooPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct FooIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Foo::instantiate_async`] which only needs a +/// [`Foo::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`FooPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`FooPre::instantiate_async`] to +/// method then uses [`FooPre::instantiate`] to /// create a [`Foo`]. /// /// * If you've instantiated the instance yourself already @@ -143,17 +151,14 @@ const _: () = { } impl Foo { /// Convenience wrapper around [`FooPre::new`] and - /// [`FooPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`FooPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - FooPre::new(pre)?.instantiate_async(store).await + FooPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`FooIndices::new`] and /// [`FooIndices::load`]. @@ -164,6 +169,19 @@ const _: () = { let indices = FooIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } pub async fn call_new<_T, _D>( &self, accessor: &wasmtime::component::Accessor<_T, _D>, diff --git a/crates/component-macro/tests/expanded/function-new_tracing_async.rs b/crates/component-macro/tests/expanded/function-new_tracing_async.rs index 1e6ece3e1d..31a5da2973 100644 --- a/crates/component-macro/tests/expanded/function-new_tracing_async.rs +++ b/crates/component-macro/tests/expanded/function-new_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> FooPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> FooPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct FooIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Foo::instantiate_async`] which only needs a +/// [`Foo::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`FooPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`FooPre::instantiate_async`] to +/// method then uses [`FooPre::instantiate`] to /// create a [`Foo`]. /// /// * If you've instantiated the instance yourself already @@ -143,17 +151,14 @@ const _: () = { } impl Foo { /// Convenience wrapper around [`FooPre::new`] and - /// [`FooPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`FooPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - FooPre::new(pre)?.instantiate_async(store).await + FooPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`FooIndices::new`] and /// [`FooIndices::load`]. @@ -164,6 +169,19 @@ const _: () = { let indices = FooIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } pub async fn call_new( &self, mut store: S, diff --git a/crates/component-macro/tests/expanded/host-world.rs b/crates/component-macro/tests/expanded/host-world.rs index bd8e1f6627..1b5a4077ee 100644 --- a/crates/component-macro/tests/expanded/host-world.rs +++ b/crates/component-macro/tests/expanded/host-world.rs @@ -52,6 +52,17 @@ impl<_T: 'static> Host_Pre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> Host_Pre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `host`. /// @@ -87,6 +98,11 @@ pub struct Host_Indices {} /// [`Component`]: wasmtime::component::Component /// [`Linker`]: wasmtime::component::Linker pub struct Host_ {} +pub trait Host_ImportsWithStore: wasmtime::component::HasData {} +impl<_T: ?Sized> Host_ImportsWithStore for _T +where + _T: wasmtime::component::HasData, +{} pub trait Host_Imports { fn foo(&mut self) -> (); } @@ -146,12 +162,25 @@ const _: () = { let indices = Host_Indices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`Host_Pre::new`] and + /// [`Host_Pre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + Host_Pre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker_imports( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: Host_ImportsWithStore, for<'a> D::Data<'a>: Host_Imports, T: 'static, { @@ -172,7 +201,7 @@ const _: () = { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: Host_ImportsWithStore, for<'a> D::Data<'a>: Host_Imports, T: 'static, { diff --git a/crates/component-macro/tests/expanded/host-world_async.rs b/crates/component-macro/tests/expanded/host-world_async.rs index a49be2444b..8bad79ba6b 100644 --- a/crates/component-macro/tests/expanded/host-world_async.rs +++ b/crates/component-macro/tests/expanded/host-world_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> Host_Pre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> Host_Pre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct Host_Indices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Host_::instantiate_async`] which only needs a +/// [`Host_::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`Host_Pre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`Host_Pre::instantiate_async`] to +/// method then uses [`Host_Pre::instantiate`] to /// create a [`Host_`]. /// /// * If you've instantiated the instance yourself already @@ -90,13 +98,17 @@ pub struct Host_Indices {} /// [`Component`]: wasmtime::component::Component /// [`Linker`]: wasmtime::component::Linker pub struct Host_ {} -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] +pub trait Host_ImportsWithStore: wasmtime::component::HasData + Send {} +impl<_T: ?Sized> Host_ImportsWithStore for _T +where + _T: wasmtime::component::HasData + Send, +{} pub trait Host_Imports: Send { - async fn foo(&mut self) -> (); + fn foo(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host_Imports + ?Sized + Send> Host_Imports for &mut _T { - async fn foo(&mut self) -> () { - Host_Imports::foo(*self).await + fn foo(&mut self) -> impl ::core::future::Future + Send { + async move { Host_Imports::foo(*self).await } } } const _: () = { @@ -132,17 +144,14 @@ const _: () = { } impl Host_ { /// Convenience wrapper around [`Host_Pre::new`] and - /// [`Host_Pre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`Host_Pre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - Host_Pre::new(pre)?.instantiate_async(store).await + Host_Pre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`Host_Indices::new`] and /// [`Host_Indices::load`]. @@ -153,12 +162,25 @@ const _: () = { let indices = Host_Indices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`Host_Pre::new`] and + /// [`Host_Pre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + Host_Pre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker_imports( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: Host_ImportsWithStore, for<'a> D::Data<'a>: Host_Imports, T: 'static + Send, { @@ -181,7 +203,7 @@ const _: () = { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: Host_ImportsWithStore + Send, for<'a> D::Data<'a>: Host_Imports + Send, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/host-world_concurrent.rs b/crates/component-macro/tests/expanded/host-world_concurrent.rs index 356c47fb0c..4e01b30ad8 100644 --- a/crates/component-macro/tests/expanded/host-world_concurrent.rs +++ b/crates/component-macro/tests/expanded/host-world_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> Host_Pre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> Host_Pre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct Host_Indices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Host_::instantiate_async`] which only needs a +/// [`Host_::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`Host_Pre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`Host_Pre::instantiate_async`] to +/// method then uses [`Host_Pre::instantiate`] to /// create a [`Host_`]. /// /// * If you've instantiated the instance yourself already @@ -90,15 +98,11 @@ pub struct Host_Indices {} /// [`Component`]: wasmtime::component::Component /// [`Linker`]: wasmtime::component::Linker pub struct Host_ {} -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] -pub trait Host_ImportsConcurrent: wasmtime::component::HasData + Send { +pub trait Host_ImportsWithStore: wasmtime::component::HasData + Send { fn foo( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host_Imports: Send {} impl<_T: Host_Imports + ?Sized + Send> Host_Imports for &mut _T {} const _: () = { @@ -134,17 +138,14 @@ const _: () = { } impl Host_ { /// Convenience wrapper around [`Host_Pre::new`] and - /// [`Host_Pre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`Host_Pre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - Host_Pre::new(pre)?.instantiate_async(store).await + Host_Pre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`Host_Indices::new`] and /// [`Host_Indices::load`]. @@ -155,12 +156,25 @@ const _: () = { let indices = Host_Indices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`Host_Pre::new`] and + /// [`Host_Pre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + Host_Pre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker_imports( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: Host_ImportsConcurrent, + D: Host_ImportsWithStore, for<'a> D::Data<'a>: Host_Imports, T: 'static + Send, { @@ -171,7 +185,7 @@ const _: () = { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::foo(accessor).await; + let r = ::foo(accessor).await; Ok(r) }) }, @@ -183,7 +197,7 @@ const _: () = { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: Host_ImportsConcurrent + Send, + D: Host_ImportsWithStore + Send, for<'a> D::Data<'a>: Host_Imports + Send, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/host-world_tracing_async.rs b/crates/component-macro/tests/expanded/host-world_tracing_async.rs index 2c8edeff06..768b4565a1 100644 --- a/crates/component-macro/tests/expanded/host-world_tracing_async.rs +++ b/crates/component-macro/tests/expanded/host-world_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> Host_Pre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> Host_Pre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct Host_Indices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Host_::instantiate_async`] which only needs a +/// [`Host_::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`Host_Pre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`Host_Pre::instantiate_async`] to +/// method then uses [`Host_Pre::instantiate`] to /// create a [`Host_`]. /// /// * If you've instantiated the instance yourself already @@ -90,13 +98,17 @@ pub struct Host_Indices {} /// [`Component`]: wasmtime::component::Component /// [`Linker`]: wasmtime::component::Linker pub struct Host_ {} -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] +pub trait Host_ImportsWithStore: wasmtime::component::HasData + Send {} +impl<_T: ?Sized> Host_ImportsWithStore for _T +where + _T: wasmtime::component::HasData + Send, +{} pub trait Host_Imports: Send { - async fn foo(&mut self) -> (); + fn foo(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host_Imports + ?Sized + Send> Host_Imports for &mut _T { - async fn foo(&mut self) -> () { - Host_Imports::foo(*self).await + fn foo(&mut self) -> impl ::core::future::Future + Send { + async move { Host_Imports::foo(*self).await } } } const _: () = { @@ -132,17 +144,14 @@ const _: () = { } impl Host_ { /// Convenience wrapper around [`Host_Pre::new`] and - /// [`Host_Pre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`Host_Pre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - Host_Pre::new(pre)?.instantiate_async(store).await + Host_Pre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`Host_Indices::new`] and /// [`Host_Indices::load`]. @@ -153,12 +162,25 @@ const _: () = { let indices = Host_Indices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`Host_Pre::new`] and + /// [`Host_Pre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + Host_Pre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker_imports( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: Host_ImportsWithStore, for<'a> D::Data<'a>: Host_Imports, T: 'static + Send, { @@ -194,7 +216,7 @@ const _: () = { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: Host_ImportsWithStore + Send, for<'a> D::Data<'a>: Host_Imports + Send, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/integers.rs b/crates/component-macro/tests/expanded/integers.rs index 716d17095d..00550dada7 100644 --- a/crates/component-macro/tests/expanded/integers.rs +++ b/crates/component-macro/tests/expanded/integers.rs @@ -52,6 +52,17 @@ impl<_T: 'static> TheWorldPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `the-world`. /// @@ -146,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::integers::HostWithStore, for<'a> D::Data<'a>: foo::foo::integers::Host, T: 'static, { @@ -169,6 +193,11 @@ pub mod foo { pub mod integers { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn a1(&mut self, x: u8) -> (); fn a2(&mut self, x: i8) -> (); @@ -270,7 +299,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -519,7 +548,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/integers` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/integers_async.rs b/crates/component-macro/tests/expanded/integers_async.rs index f005311207..db511f4b77 100644 --- a/crates/component-macro/tests/expanded/integers_async.rs +++ b/crates/component-macro/tests/expanded/integers_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::integers::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::integers::Host + Send, T: 'static + Send, { @@ -175,17 +193,45 @@ pub mod foo { pub mod integers { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn a1(&mut self, x: u8) -> (); - async fn a2(&mut self, x: i8) -> (); - async fn a3(&mut self, x: u16) -> (); - async fn a4(&mut self, x: i16) -> (); - async fn a5(&mut self, x: u32) -> (); - async fn a6(&mut self, x: i32) -> (); - async fn a7(&mut self, x: u64) -> (); - async fn a8(&mut self, x: i64) -> (); - async fn a9( + fn a1( + &mut self, + x: u8, + ) -> impl ::core::future::Future + Send; + fn a2( + &mut self, + x: i8, + ) -> impl ::core::future::Future + Send; + fn a3( + &mut self, + x: u16, + ) -> impl ::core::future::Future + Send; + fn a4( + &mut self, + x: i16, + ) -> impl ::core::future::Future + Send; + fn a5( + &mut self, + x: u32, + ) -> impl ::core::future::Future + Send; + fn a6( + &mut self, + x: i32, + ) -> impl ::core::future::Future + Send; + fn a7( + &mut self, + x: u64, + ) -> impl ::core::future::Future + Send; + fn a8( + &mut self, + x: i64, + ) -> impl ::core::future::Future + Send; + fn a9( &mut self, p1: u8, p2: i8, @@ -195,43 +241,69 @@ pub mod foo { p6: i32, p7: u64, p8: i64, - ) -> (); - async fn r1(&mut self) -> u8; - async fn r2(&mut self) -> i8; - async fn r3(&mut self) -> u16; - async fn r4(&mut self) -> i16; - async fn r5(&mut self) -> u32; - async fn r6(&mut self) -> i32; - async fn r7(&mut self) -> u64; - async fn r8(&mut self) -> i64; - async fn pair_ret(&mut self) -> (i64, u8); + ) -> impl ::core::future::Future + Send; + fn r1(&mut self) -> impl ::core::future::Future + Send; + fn r2(&mut self) -> impl ::core::future::Future + Send; + fn r3(&mut self) -> impl ::core::future::Future + Send; + fn r4(&mut self) -> impl ::core::future::Future + Send; + fn r5(&mut self) -> impl ::core::future::Future + Send; + fn r6(&mut self) -> impl ::core::future::Future + Send; + fn r7(&mut self) -> impl ::core::future::Future + Send; + fn r8(&mut self) -> impl ::core::future::Future + Send; + fn pair_ret( + &mut self, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn a1(&mut self, x: u8) -> () { - Host::a1(*self, x).await + fn a1( + &mut self, + x: u8, + ) -> impl ::core::future::Future + Send { + async move { Host::a1(*self, x).await } } - async fn a2(&mut self, x: i8) -> () { - Host::a2(*self, x).await + fn a2( + &mut self, + x: i8, + ) -> impl ::core::future::Future + Send { + async move { Host::a2(*self, x).await } } - async fn a3(&mut self, x: u16) -> () { - Host::a3(*self, x).await + fn a3( + &mut self, + x: u16, + ) -> impl ::core::future::Future + Send { + async move { Host::a3(*self, x).await } } - async fn a4(&mut self, x: i16) -> () { - Host::a4(*self, x).await + fn a4( + &mut self, + x: i16, + ) -> impl ::core::future::Future + Send { + async move { Host::a4(*self, x).await } } - async fn a5(&mut self, x: u32) -> () { - Host::a5(*self, x).await + fn a5( + &mut self, + x: u32, + ) -> impl ::core::future::Future + Send { + async move { Host::a5(*self, x).await } } - async fn a6(&mut self, x: i32) -> () { - Host::a6(*self, x).await + fn a6( + &mut self, + x: i32, + ) -> impl ::core::future::Future + Send { + async move { Host::a6(*self, x).await } } - async fn a7(&mut self, x: u64) -> () { - Host::a7(*self, x).await + fn a7( + &mut self, + x: u64, + ) -> impl ::core::future::Future + Send { + async move { Host::a7(*self, x).await } } - async fn a8(&mut self, x: i64) -> () { - Host::a8(*self, x).await + fn a8( + &mut self, + x: i64, + ) -> impl ::core::future::Future + Send { + async move { Host::a8(*self, x).await } } - async fn a9( + fn a9( &mut self, p1: u8, p2: i8, @@ -241,35 +313,37 @@ pub mod foo { p6: i32, p7: u64, p8: i64, - ) -> () { - Host::a9(*self, p1, p2, p3, p4, p5, p6, p7, p8).await + ) -> impl ::core::future::Future + Send { + async move { Host::a9(*self, p1, p2, p3, p4, p5, p6, p7, p8).await } } - async fn r1(&mut self) -> u8 { - Host::r1(*self).await + fn r1(&mut self) -> impl ::core::future::Future + Send { + async move { Host::r1(*self).await } } - async fn r2(&mut self) -> i8 { - Host::r2(*self).await + fn r2(&mut self) -> impl ::core::future::Future + Send { + async move { Host::r2(*self).await } } - async fn r3(&mut self) -> u16 { - Host::r3(*self).await + fn r3(&mut self) -> impl ::core::future::Future + Send { + async move { Host::r3(*self).await } } - async fn r4(&mut self) -> i16 { - Host::r4(*self).await + fn r4(&mut self) -> impl ::core::future::Future + Send { + async move { Host::r4(*self).await } } - async fn r5(&mut self) -> u32 { - Host::r5(*self).await + fn r5(&mut self) -> impl ::core::future::Future + Send { + async move { Host::r5(*self).await } } - async fn r6(&mut self) -> i32 { - Host::r6(*self).await + fn r6(&mut self) -> impl ::core::future::Future + Send { + async move { Host::r6(*self).await } } - async fn r7(&mut self) -> u64 { - Host::r7(*self).await + fn r7(&mut self) -> impl ::core::future::Future + Send { + async move { Host::r7(*self).await } } - async fn r8(&mut self) -> i64 { - Host::r8(*self).await + fn r8(&mut self) -> impl ::core::future::Future + Send { + async move { Host::r8(*self).await } } - async fn pair_ret(&mut self) -> (i64, u8) { - Host::pair_ret(*self).await + fn pair_ret( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::pair_ret(*self).await } } } pub fn add_to_linker( @@ -277,7 +351,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -563,7 +637,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/integers` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/integers_concurrent.rs b/crates/component-macro/tests/expanded/integers_concurrent.rs index d6e9e0cf77..b4013def35 100644 --- a/crates/component-macro/tests/expanded/integers_concurrent.rs +++ b/crates/component-macro/tests/expanded/integers_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::integers::HostConcurrent + Send, + D: foo::foo::integers::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::integers::Host + Send, T: 'static + Send, { @@ -175,56 +193,39 @@ pub mod foo { pub mod integers { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn a1( accessor: &wasmtime::component::Accessor, x: u8, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn a2( accessor: &wasmtime::component::Accessor, x: i8, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn a3( accessor: &wasmtime::component::Accessor, x: u16, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn a4( accessor: &wasmtime::component::Accessor, x: i16, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn a5( accessor: &wasmtime::component::Accessor, x: u32, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn a6( accessor: &wasmtime::component::Accessor, x: i32, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn a7( accessor: &wasmtime::component::Accessor, x: u64, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn a8( accessor: &wasmtime::component::Accessor, x: i64, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn a9( accessor: &wasmtime::component::Accessor, p1: u8, @@ -235,56 +236,35 @@ pub mod foo { p6: i32, p7: u64, p8: i64, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn r1( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn r2( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn r3( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn r4( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn r5( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn r6( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn r7( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn r8( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn pair_ret( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -292,7 +272,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -302,7 +282,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (u8,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::a1(accessor, arg0).await; + let r = ::a1(accessor, arg0).await; Ok(r) }) }, @@ -312,7 +292,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (i8,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::a2(accessor, arg0).await; + let r = ::a2(accessor, arg0).await; Ok(r) }) }, @@ -322,7 +302,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (u16,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::a3(accessor, arg0).await; + let r = ::a3(accessor, arg0).await; Ok(r) }) }, @@ -332,7 +312,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (i16,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::a4(accessor, arg0).await; + let r = ::a4(accessor, arg0).await; Ok(r) }) }, @@ -342,7 +322,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (u32,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::a5(accessor, arg0).await; + let r = ::a5(accessor, arg0).await; Ok(r) }) }, @@ -352,7 +332,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (i32,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::a6(accessor, arg0).await; + let r = ::a6(accessor, arg0).await; Ok(r) }) }, @@ -362,7 +342,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (u64,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::a7(accessor, arg0).await; + let r = ::a7(accessor, arg0).await; Ok(r) }) }, @@ -372,7 +352,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (i64,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::a8(accessor, arg0).await; + let r = ::a8(accessor, arg0).await; Ok(r) }) }, @@ -394,7 +374,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::a9( + let r = ::a9( accessor, arg0, arg1, @@ -415,7 +395,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::r1(accessor).await; + let r = ::r1(accessor).await; Ok((r,)) }) }, @@ -425,7 +405,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::r2(accessor).await; + let r = ::r2(accessor).await; Ok((r,)) }) }, @@ -435,7 +415,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::r3(accessor).await; + let r = ::r3(accessor).await; Ok((r,)) }) }, @@ -445,7 +425,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::r4(accessor).await; + let r = ::r4(accessor).await; Ok((r,)) }) }, @@ -455,7 +435,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::r5(accessor).await; + let r = ::r5(accessor).await; Ok((r,)) }) }, @@ -465,7 +445,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::r6(accessor).await; + let r = ::r6(accessor).await; Ok((r,)) }) }, @@ -475,7 +455,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::r7(accessor).await; + let r = ::r7(accessor).await; Ok((r,)) }) }, @@ -485,7 +465,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::r8(accessor).await; + let r = ::r8(accessor).await; Ok((r,)) }) }, @@ -495,7 +475,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::pair_ret(accessor).await; + let r = ::pair_ret(accessor).await; Ok((r,)) }) }, diff --git a/crates/component-macro/tests/expanded/integers_tracing_async.rs b/crates/component-macro/tests/expanded/integers_tracing_async.rs index 445e948d92..e74b1ead83 100644 --- a/crates/component-macro/tests/expanded/integers_tracing_async.rs +++ b/crates/component-macro/tests/expanded/integers_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::integers::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::integers::Host + Send, T: 'static + Send, { @@ -175,17 +193,45 @@ pub mod foo { pub mod integers { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn a1(&mut self, x: u8) -> (); - async fn a2(&mut self, x: i8) -> (); - async fn a3(&mut self, x: u16) -> (); - async fn a4(&mut self, x: i16) -> (); - async fn a5(&mut self, x: u32) -> (); - async fn a6(&mut self, x: i32) -> (); - async fn a7(&mut self, x: u64) -> (); - async fn a8(&mut self, x: i64) -> (); - async fn a9( + fn a1( + &mut self, + x: u8, + ) -> impl ::core::future::Future + Send; + fn a2( + &mut self, + x: i8, + ) -> impl ::core::future::Future + Send; + fn a3( + &mut self, + x: u16, + ) -> impl ::core::future::Future + Send; + fn a4( + &mut self, + x: i16, + ) -> impl ::core::future::Future + Send; + fn a5( + &mut self, + x: u32, + ) -> impl ::core::future::Future + Send; + fn a6( + &mut self, + x: i32, + ) -> impl ::core::future::Future + Send; + fn a7( + &mut self, + x: u64, + ) -> impl ::core::future::Future + Send; + fn a8( + &mut self, + x: i64, + ) -> impl ::core::future::Future + Send; + fn a9( &mut self, p1: u8, p2: i8, @@ -195,43 +241,69 @@ pub mod foo { p6: i32, p7: u64, p8: i64, - ) -> (); - async fn r1(&mut self) -> u8; - async fn r2(&mut self) -> i8; - async fn r3(&mut self) -> u16; - async fn r4(&mut self) -> i16; - async fn r5(&mut self) -> u32; - async fn r6(&mut self) -> i32; - async fn r7(&mut self) -> u64; - async fn r8(&mut self) -> i64; - async fn pair_ret(&mut self) -> (i64, u8); + ) -> impl ::core::future::Future + Send; + fn r1(&mut self) -> impl ::core::future::Future + Send; + fn r2(&mut self) -> impl ::core::future::Future + Send; + fn r3(&mut self) -> impl ::core::future::Future + Send; + fn r4(&mut self) -> impl ::core::future::Future + Send; + fn r5(&mut self) -> impl ::core::future::Future + Send; + fn r6(&mut self) -> impl ::core::future::Future + Send; + fn r7(&mut self) -> impl ::core::future::Future + Send; + fn r8(&mut self) -> impl ::core::future::Future + Send; + fn pair_ret( + &mut self, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn a1(&mut self, x: u8) -> () { - Host::a1(*self, x).await + fn a1( + &mut self, + x: u8, + ) -> impl ::core::future::Future + Send { + async move { Host::a1(*self, x).await } } - async fn a2(&mut self, x: i8) -> () { - Host::a2(*self, x).await + fn a2( + &mut self, + x: i8, + ) -> impl ::core::future::Future + Send { + async move { Host::a2(*self, x).await } } - async fn a3(&mut self, x: u16) -> () { - Host::a3(*self, x).await + fn a3( + &mut self, + x: u16, + ) -> impl ::core::future::Future + Send { + async move { Host::a3(*self, x).await } } - async fn a4(&mut self, x: i16) -> () { - Host::a4(*self, x).await + fn a4( + &mut self, + x: i16, + ) -> impl ::core::future::Future + Send { + async move { Host::a4(*self, x).await } } - async fn a5(&mut self, x: u32) -> () { - Host::a5(*self, x).await + fn a5( + &mut self, + x: u32, + ) -> impl ::core::future::Future + Send { + async move { Host::a5(*self, x).await } } - async fn a6(&mut self, x: i32) -> () { - Host::a6(*self, x).await + fn a6( + &mut self, + x: i32, + ) -> impl ::core::future::Future + Send { + async move { Host::a6(*self, x).await } } - async fn a7(&mut self, x: u64) -> () { - Host::a7(*self, x).await + fn a7( + &mut self, + x: u64, + ) -> impl ::core::future::Future + Send { + async move { Host::a7(*self, x).await } } - async fn a8(&mut self, x: i64) -> () { - Host::a8(*self, x).await + fn a8( + &mut self, + x: i64, + ) -> impl ::core::future::Future + Send { + async move { Host::a8(*self, x).await } } - async fn a9( + fn a9( &mut self, p1: u8, p2: i8, @@ -241,35 +313,37 @@ pub mod foo { p6: i32, p7: u64, p8: i64, - ) -> () { - Host::a9(*self, p1, p2, p3, p4, p5, p6, p7, p8).await + ) -> impl ::core::future::Future + Send { + async move { Host::a9(*self, p1, p2, p3, p4, p5, p6, p7, p8).await } } - async fn r1(&mut self) -> u8 { - Host::r1(*self).await + fn r1(&mut self) -> impl ::core::future::Future + Send { + async move { Host::r1(*self).await } } - async fn r2(&mut self) -> i8 { - Host::r2(*self).await + fn r2(&mut self) -> impl ::core::future::Future + Send { + async move { Host::r2(*self).await } } - async fn r3(&mut self) -> u16 { - Host::r3(*self).await + fn r3(&mut self) -> impl ::core::future::Future + Send { + async move { Host::r3(*self).await } } - async fn r4(&mut self) -> i16 { - Host::r4(*self).await + fn r4(&mut self) -> impl ::core::future::Future + Send { + async move { Host::r4(*self).await } } - async fn r5(&mut self) -> u32 { - Host::r5(*self).await + fn r5(&mut self) -> impl ::core::future::Future + Send { + async move { Host::r5(*self).await } } - async fn r6(&mut self) -> i32 { - Host::r6(*self).await + fn r6(&mut self) -> impl ::core::future::Future + Send { + async move { Host::r6(*self).await } } - async fn r7(&mut self) -> u64 { - Host::r7(*self).await + fn r7(&mut self) -> impl ::core::future::Future + Send { + async move { Host::r7(*self).await } } - async fn r8(&mut self) -> i64 { - Host::r8(*self).await + fn r8(&mut self) -> impl ::core::future::Future + Send { + async move { Host::r8(*self).await } } - async fn pair_ret(&mut self) -> (i64, u8) { - Host::pair_ret(*self).await + fn pair_ret( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::pair_ret(*self).await } } } pub fn add_to_linker( @@ -277,7 +351,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -828,7 +902,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/integers` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/lists.rs b/crates/component-macro/tests/expanded/lists.rs index 6b788792ae..508bbab6c7 100644 --- a/crates/component-macro/tests/expanded/lists.rs +++ b/crates/component-macro/tests/expanded/lists.rs @@ -52,6 +52,17 @@ impl<_T: 'static> TheListsPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> TheListsPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `the-lists`. /// @@ -144,12 +155,25 @@ const _: () = { let indices = TheListsIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheListsPre::new`] and + /// [`TheListsPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheListsPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::lists::HostWithStore, for<'a> D::Data<'a>: foo::foo::lists::Host, T: 'static, { @@ -347,6 +371,11 @@ pub mod foo { >::ALIGN32 ); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn list_u8_param( &mut self, @@ -608,7 +637,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -1217,7 +1246,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/lists` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/lists_async.rs b/crates/component-macro/tests/expanded/lists_async.rs index b438646b99..ce393e867c 100644 --- a/crates/component-macro/tests/expanded/lists_async.rs +++ b/crates/component-macro/tests/expanded/lists_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheListsPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheListsPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheListsIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheLists::instantiate_async`] which only needs a +/// [`TheLists::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheListsPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheListsPre::instantiate_async`] to +/// method then uses [`TheListsPre::instantiate`] to /// create a [`TheLists`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl TheLists { /// Convenience wrapper around [`TheListsPre::new`] and - /// [`TheListsPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheListsPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheListsPre::new(pre)?.instantiate_async(store).await + TheListsPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheListsIndices::new`] and /// [`TheListsIndices::load`]. @@ -150,12 +155,25 @@ const _: () = { let indices = TheListsIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheListsPre::new`] and + /// [`TheListsPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheListsPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::lists::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::lists::Host + Send, T: 'static + Send, { @@ -353,301 +371,373 @@ pub mod foo { >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn list_u8_param( + fn list_u8_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_u16_param( + ) -> impl ::core::future::Future + Send; + fn list_u16_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_u32_param( + ) -> impl ::core::future::Future + Send; + fn list_u32_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_u64_param( + ) -> impl ::core::future::Future + Send; + fn list_u64_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_s8_param( + ) -> impl ::core::future::Future + Send; + fn list_s8_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_s16_param( + ) -> impl ::core::future::Future + Send; + fn list_s16_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_s32_param( + ) -> impl ::core::future::Future + Send; + fn list_s32_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_s64_param( + ) -> impl ::core::future::Future + Send; + fn list_s64_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_f32_param( + ) -> impl ::core::future::Future + Send; + fn list_f32_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_f64_param( + ) -> impl ::core::future::Future + Send; + fn list_f64_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_u8_ret( + ) -> impl ::core::future::Future + Send; + fn list_u8_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_u16_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_u16_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_u32_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_u32_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_u64_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_u64_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_s8_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_s8_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_s16_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_s16_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_s32_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_s32_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_s64_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_s64_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_f32_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_f32_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_f64_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_f64_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn tuple_list( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn tuple_list( &mut self, x: wasmtime::component::__internal::Vec<(u8, i8)>, - ) -> wasmtime::component::__internal::Vec<(i64, u32)>; - async fn string_list_arg( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec<(i64, u32)>, + > + Send; + fn string_list_arg( &mut self, a: wasmtime::component::__internal::Vec< wasmtime::component::__internal::String, >, - ) -> (); - async fn string_list_ret( + ) -> impl ::core::future::Future + Send; + fn string_list_ret( &mut self, - ) -> wasmtime::component::__internal::Vec< - wasmtime::component::__internal::String, - >; - async fn tuple_string_list( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + > + Send; + fn tuple_string_list( &mut self, x: wasmtime::component::__internal::Vec< (u8, wasmtime::component::__internal::String), >, - ) -> wasmtime::component::__internal::Vec< - (wasmtime::component::__internal::String, u8), - >; - async fn string_list( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + (wasmtime::component::__internal::String, u8), + >, + > + Send; + fn string_list( &mut self, x: wasmtime::component::__internal::Vec< wasmtime::component::__internal::String, >, - ) -> wasmtime::component::__internal::Vec< - wasmtime::component::__internal::String, - >; - async fn record_list( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + > + Send; + fn record_list( &mut self, x: wasmtime::component::__internal::Vec, - ) -> wasmtime::component::__internal::Vec; - async fn record_list_reverse( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn record_list_reverse( &mut self, x: wasmtime::component::__internal::Vec, - ) -> wasmtime::component::__internal::Vec; - async fn variant_list( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn variant_list( &mut self, x: wasmtime::component::__internal::Vec, - ) -> wasmtime::component::__internal::Vec; - async fn load_store_everything( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn load_store_everything( &mut self, a: LoadStoreAllSizes, - ) -> LoadStoreAllSizes; + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn list_u8_param( + fn list_u8_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_u8_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_u8_param(*self, x).await } } - async fn list_u16_param( + fn list_u16_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_u16_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_u16_param(*self, x).await } } - async fn list_u32_param( + fn list_u32_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_u32_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_u32_param(*self, x).await } } - async fn list_u64_param( + fn list_u64_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_u64_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_u64_param(*self, x).await } } - async fn list_s8_param( + fn list_s8_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_s8_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_s8_param(*self, x).await } } - async fn list_s16_param( + fn list_s16_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_s16_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_s16_param(*self, x).await } } - async fn list_s32_param( + fn list_s32_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_s32_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_s32_param(*self, x).await } } - async fn list_s64_param( + fn list_s64_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_s64_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_s64_param(*self, x).await } } - async fn list_f32_param( + fn list_f32_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_f32_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_f32_param(*self, x).await } } - async fn list_f64_param( + fn list_f64_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_f64_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_f64_param(*self, x).await } } - async fn list_u8_ret( + fn list_u8_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_u8_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_u8_ret(*self).await } } - async fn list_u16_ret( + fn list_u16_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_u16_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_u16_ret(*self).await } } - async fn list_u32_ret( + fn list_u32_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_u32_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_u32_ret(*self).await } } - async fn list_u64_ret( + fn list_u64_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_u64_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_u64_ret(*self).await } } - async fn list_s8_ret( + fn list_s8_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_s8_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_s8_ret(*self).await } } - async fn list_s16_ret( + fn list_s16_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_s16_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_s16_ret(*self).await } } - async fn list_s32_ret( + fn list_s32_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_s32_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_s32_ret(*self).await } } - async fn list_s64_ret( + fn list_s64_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_s64_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_s64_ret(*self).await } } - async fn list_f32_ret( + fn list_f32_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_f32_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_f32_ret(*self).await } } - async fn list_f64_ret( + fn list_f64_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_f64_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_f64_ret(*self).await } } - async fn tuple_list( + fn tuple_list( &mut self, x: wasmtime::component::__internal::Vec<(u8, i8)>, - ) -> wasmtime::component::__internal::Vec<(i64, u32)> { - Host::tuple_list(*self, x).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec<(i64, u32)>, + > + Send { + async move { Host::tuple_list(*self, x).await } } - async fn string_list_arg( + fn string_list_arg( &mut self, a: wasmtime::component::__internal::Vec< wasmtime::component::__internal::String, >, - ) -> () { - Host::string_list_arg(*self, a).await + ) -> impl ::core::future::Future + Send { + async move { Host::string_list_arg(*self, a).await } } - async fn string_list_ret( + fn string_list_ret( &mut self, - ) -> wasmtime::component::__internal::Vec< - wasmtime::component::__internal::String, - > { - Host::string_list_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + > + Send { + async move { Host::string_list_ret(*self).await } } - async fn tuple_string_list( + fn tuple_string_list( &mut self, x: wasmtime::component::__internal::Vec< (u8, wasmtime::component::__internal::String), >, - ) -> wasmtime::component::__internal::Vec< - (wasmtime::component::__internal::String, u8), - > { - Host::tuple_string_list(*self, x).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + (wasmtime::component::__internal::String, u8), + >, + > + Send { + async move { Host::tuple_string_list(*self, x).await } } - async fn string_list( + fn string_list( &mut self, x: wasmtime::component::__internal::Vec< wasmtime::component::__internal::String, >, - ) -> wasmtime::component::__internal::Vec< - wasmtime::component::__internal::String, - > { - Host::string_list(*self, x).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + > + Send { + async move { Host::string_list(*self, x).await } } - async fn record_list( + fn record_list( &mut self, x: wasmtime::component::__internal::Vec, - ) -> wasmtime::component::__internal::Vec { - Host::record_list(*self, x).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::record_list(*self, x).await } } - async fn record_list_reverse( + fn record_list_reverse( &mut self, x: wasmtime::component::__internal::Vec, - ) -> wasmtime::component::__internal::Vec { - Host::record_list_reverse(*self, x).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::record_list_reverse(*self, x).await } } - async fn variant_list( + fn variant_list( &mut self, x: wasmtime::component::__internal::Vec, - ) -> wasmtime::component::__internal::Vec { - Host::variant_list(*self, x).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::variant_list(*self, x).await } } - async fn load_store_everything( + fn load_store_everything( &mut self, a: LoadStoreAllSizes, - ) -> LoadStoreAllSizes { - Host::load_store_everything(*self, a).await + ) -> impl ::core::future::Future + Send { + async move { Host::load_store_everything(*self, a).await } } } pub fn add_to_linker( @@ -655,7 +745,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -1322,7 +1412,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/lists` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/lists_concurrent.rs b/crates/component-macro/tests/expanded/lists_concurrent.rs index 4448cd4b0b..11324e1d83 100644 --- a/crates/component-macro/tests/expanded/lists_concurrent.rs +++ b/crates/component-macro/tests/expanded/lists_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheListsPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheListsPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheListsIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheLists::instantiate_async`] which only needs a +/// [`TheLists::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheListsPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheListsPre::instantiate_async`] to +/// method then uses [`TheListsPre::instantiate`] to /// create a [`TheLists`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl TheLists { /// Convenience wrapper around [`TheListsPre::new`] and - /// [`TheListsPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheListsPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheListsPre::new(pre)?.instantiate_async(store).await + TheListsPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheListsIndices::new`] and /// [`TheListsIndices::load`]. @@ -150,12 +155,25 @@ const _: () = { let indices = TheListsIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheListsPre::new`] and + /// [`TheListsPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheListsPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::lists::HostConcurrent + Send, + D: foo::foo::lists::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::lists::Host + Send, T: 'static + Send, { @@ -353,163 +371,116 @@ pub mod foo { >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn list_u8_param( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn list_u16_param( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn list_u32_param( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn list_u64_param( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn list_s8_param( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn list_s16_param( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn list_s32_param( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn list_s64_param( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn list_f32_param( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn list_f64_param( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn list_u8_ret( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::Vec, - > + Send - where - Self: Sized; + > + Send; fn list_u16_ret( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::Vec, - > + Send - where - Self: Sized; + > + Send; fn list_u32_ret( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::Vec, - > + Send - where - Self: Sized; + > + Send; fn list_u64_ret( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::Vec, - > + Send - where - Self: Sized; + > + Send; fn list_s8_ret( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::Vec, - > + Send - where - Self: Sized; + > + Send; fn list_s16_ret( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::Vec, - > + Send - where - Self: Sized; + > + Send; fn list_s32_ret( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::Vec, - > + Send - where - Self: Sized; + > + Send; fn list_s64_ret( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::Vec, - > + Send - where - Self: Sized; + > + Send; fn list_f32_ret( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::Vec, - > + Send - where - Self: Sized; + > + Send; fn list_f64_ret( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::Vec, - > + Send - where - Self: Sized; + > + Send; fn tuple_list( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec<(u8, i8)>, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::Vec<(i64, u32)>, - > + Send - where - Self: Sized; + > + Send; fn string_list_arg( accessor: &wasmtime::component::Accessor, a: wasmtime::component::__internal::Vec< wasmtime::component::__internal::String, >, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn string_list_ret( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::Vec< wasmtime::component::__internal::String, >, - > + Send - where - Self: Sized; + > + Send; fn tuple_string_list( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec< @@ -519,9 +490,7 @@ pub mod foo { Output = wasmtime::component::__internal::Vec< (wasmtime::component::__internal::String, u8), >, - > + Send - where - Self: Sized; + > + Send; fn string_list( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec< @@ -531,41 +500,30 @@ pub mod foo { Output = wasmtime::component::__internal::Vec< wasmtime::component::__internal::String, >, - > + Send - where - Self: Sized; + > + Send; fn record_list( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::Vec, - > + Send - where - Self: Sized; + > + Send; fn record_list_reverse( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::Vec, - > + Send - where - Self: Sized; + > + Send; fn variant_list( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::Vec, - > + Send - where - Self: Sized; + > + Send; fn load_store_everything( accessor: &wasmtime::component::Accessor, a: LoadStoreAllSizes, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -573,7 +531,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -586,7 +544,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_u8_param(accessor, arg0) + let r = ::list_u8_param(accessor, arg0) .await; Ok(r) }) @@ -600,7 +558,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_u16_param(accessor, arg0) + let r = ::list_u16_param(accessor, arg0) .await; Ok(r) }) @@ -614,7 +572,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_u32_param(accessor, arg0) + let r = ::list_u32_param(accessor, arg0) .await; Ok(r) }) @@ -628,7 +586,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_u64_param(accessor, arg0) + let r = ::list_u64_param(accessor, arg0) .await; Ok(r) }) @@ -642,7 +600,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_s8_param(accessor, arg0) + let r = ::list_s8_param(accessor, arg0) .await; Ok(r) }) @@ -656,7 +614,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_s16_param(accessor, arg0) + let r = ::list_s16_param(accessor, arg0) .await; Ok(r) }) @@ -670,7 +628,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_s32_param(accessor, arg0) + let r = ::list_s32_param(accessor, arg0) .await; Ok(r) }) @@ -684,7 +642,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_s64_param(accessor, arg0) + let r = ::list_s64_param(accessor, arg0) .await; Ok(r) }) @@ -698,7 +656,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_f32_param(accessor, arg0) + let r = ::list_f32_param(accessor, arg0) .await; Ok(r) }) @@ -712,7 +670,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_f64_param(accessor, arg0) + let r = ::list_f64_param(accessor, arg0) .await; Ok(r) }) @@ -723,7 +681,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_u8_ret(accessor).await; + let r = ::list_u8_ret(accessor).await; Ok((r,)) }) }, @@ -733,7 +691,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_u16_ret(accessor).await; + let r = ::list_u16_ret(accessor).await; Ok((r,)) }) }, @@ -743,7 +701,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_u32_ret(accessor).await; + let r = ::list_u32_ret(accessor).await; Ok((r,)) }) }, @@ -753,7 +711,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_u64_ret(accessor).await; + let r = ::list_u64_ret(accessor).await; Ok((r,)) }) }, @@ -763,7 +721,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_s8_ret(accessor).await; + let r = ::list_s8_ret(accessor).await; Ok((r,)) }) }, @@ -773,7 +731,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_s16_ret(accessor).await; + let r = ::list_s16_ret(accessor).await; Ok((r,)) }) }, @@ -783,7 +741,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_s32_ret(accessor).await; + let r = ::list_s32_ret(accessor).await; Ok((r,)) }) }, @@ -793,7 +751,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_s64_ret(accessor).await; + let r = ::list_s64_ret(accessor).await; Ok((r,)) }) }, @@ -803,7 +761,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_f32_ret(accessor).await; + let r = ::list_f32_ret(accessor).await; Ok((r,)) }) }, @@ -813,7 +771,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_f64_ret(accessor).await; + let r = ::list_f64_ret(accessor).await; Ok((r,)) }) }, @@ -826,7 +784,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::tuple_list(accessor, arg0) + let r = ::tuple_list(accessor, arg0) .await; Ok((r,)) }) @@ -846,10 +804,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::string_list_arg( - accessor, - arg0, - ) + let r = ::string_list_arg(accessor, arg0) .await; Ok(r) }) @@ -860,7 +815,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::string_list_ret(accessor) + let r = ::string_list_ret(accessor) .await; Ok((r,)) }) @@ -880,7 +835,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::tuple_string_list( + let r = ::tuple_string_list( accessor, arg0, ) @@ -903,7 +858,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::string_list(accessor, arg0) + let r = ::string_list(accessor, arg0) .await; Ok((r,)) }) @@ -917,7 +872,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::record_list(accessor, arg0) + let r = ::record_list(accessor, arg0) .await; Ok((r,)) }) @@ -931,7 +886,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::record_list_reverse( + let r = ::record_list_reverse( accessor, arg0, ) @@ -948,7 +903,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::variant_list(accessor, arg0) + let r = ::variant_list(accessor, arg0) .await; Ok((r,)) }) @@ -962,7 +917,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::load_store_everything( + let r = ::load_store_everything( accessor, arg0, ) diff --git a/crates/component-macro/tests/expanded/lists_tracing_async.rs b/crates/component-macro/tests/expanded/lists_tracing_async.rs index 56a59e82dd..485434acbb 100644 --- a/crates/component-macro/tests/expanded/lists_tracing_async.rs +++ b/crates/component-macro/tests/expanded/lists_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheListsPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheListsPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheListsIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheLists::instantiate_async`] which only needs a +/// [`TheLists::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheListsPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheListsPre::instantiate_async`] to +/// method then uses [`TheListsPre::instantiate`] to /// create a [`TheLists`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl TheLists { /// Convenience wrapper around [`TheListsPre::new`] and - /// [`TheListsPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheListsPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheListsPre::new(pre)?.instantiate_async(store).await + TheListsPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheListsIndices::new`] and /// [`TheListsIndices::load`]. @@ -150,12 +155,25 @@ const _: () = { let indices = TheListsIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheListsPre::new`] and + /// [`TheListsPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheListsPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::lists::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::lists::Host + Send, T: 'static + Send, { @@ -353,301 +371,373 @@ pub mod foo { >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn list_u8_param( + fn list_u8_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_u16_param( + ) -> impl ::core::future::Future + Send; + fn list_u16_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_u32_param( + ) -> impl ::core::future::Future + Send; + fn list_u32_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_u64_param( + ) -> impl ::core::future::Future + Send; + fn list_u64_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_s8_param( + ) -> impl ::core::future::Future + Send; + fn list_s8_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_s16_param( + ) -> impl ::core::future::Future + Send; + fn list_s16_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_s32_param( + ) -> impl ::core::future::Future + Send; + fn list_s32_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_s64_param( + ) -> impl ::core::future::Future + Send; + fn list_s64_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_f32_param( + ) -> impl ::core::future::Future + Send; + fn list_f32_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_f64_param( + ) -> impl ::core::future::Future + Send; + fn list_f64_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> (); - async fn list_u8_ret( + ) -> impl ::core::future::Future + Send; + fn list_u8_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_u16_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_u16_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_u32_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_u32_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_u64_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_u64_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_s8_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_s8_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_s16_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_s16_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_s32_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_s32_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_s64_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_s64_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_f32_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_f32_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn list_f64_ret( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn list_f64_ret( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn tuple_list( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn tuple_list( &mut self, x: wasmtime::component::__internal::Vec<(u8, i8)>, - ) -> wasmtime::component::__internal::Vec<(i64, u32)>; - async fn string_list_arg( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec<(i64, u32)>, + > + Send; + fn string_list_arg( &mut self, a: wasmtime::component::__internal::Vec< wasmtime::component::__internal::String, >, - ) -> (); - async fn string_list_ret( + ) -> impl ::core::future::Future + Send; + fn string_list_ret( &mut self, - ) -> wasmtime::component::__internal::Vec< - wasmtime::component::__internal::String, - >; - async fn tuple_string_list( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + > + Send; + fn tuple_string_list( &mut self, x: wasmtime::component::__internal::Vec< (u8, wasmtime::component::__internal::String), >, - ) -> wasmtime::component::__internal::Vec< - (wasmtime::component::__internal::String, u8), - >; - async fn string_list( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + (wasmtime::component::__internal::String, u8), + >, + > + Send; + fn string_list( &mut self, x: wasmtime::component::__internal::Vec< wasmtime::component::__internal::String, >, - ) -> wasmtime::component::__internal::Vec< - wasmtime::component::__internal::String, - >; - async fn record_list( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + > + Send; + fn record_list( &mut self, x: wasmtime::component::__internal::Vec, - ) -> wasmtime::component::__internal::Vec; - async fn record_list_reverse( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn record_list_reverse( &mut self, x: wasmtime::component::__internal::Vec, - ) -> wasmtime::component::__internal::Vec; - async fn variant_list( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn variant_list( &mut self, x: wasmtime::component::__internal::Vec, - ) -> wasmtime::component::__internal::Vec; - async fn load_store_everything( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn load_store_everything( &mut self, a: LoadStoreAllSizes, - ) -> LoadStoreAllSizes; + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn list_u8_param( + fn list_u8_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_u8_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_u8_param(*self, x).await } } - async fn list_u16_param( + fn list_u16_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_u16_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_u16_param(*self, x).await } } - async fn list_u32_param( + fn list_u32_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_u32_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_u32_param(*self, x).await } } - async fn list_u64_param( + fn list_u64_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_u64_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_u64_param(*self, x).await } } - async fn list_s8_param( + fn list_s8_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_s8_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_s8_param(*self, x).await } } - async fn list_s16_param( + fn list_s16_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_s16_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_s16_param(*self, x).await } } - async fn list_s32_param( + fn list_s32_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_s32_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_s32_param(*self, x).await } } - async fn list_s64_param( + fn list_s64_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_s64_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_s64_param(*self, x).await } } - async fn list_f32_param( + fn list_f32_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_f32_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_f32_param(*self, x).await } } - async fn list_f64_param( + fn list_f64_param( &mut self, x: wasmtime::component::__internal::Vec, - ) -> () { - Host::list_f64_param(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_f64_param(*self, x).await } } - async fn list_u8_ret( + fn list_u8_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_u8_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_u8_ret(*self).await } } - async fn list_u16_ret( + fn list_u16_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_u16_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_u16_ret(*self).await } } - async fn list_u32_ret( + fn list_u32_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_u32_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_u32_ret(*self).await } } - async fn list_u64_ret( + fn list_u64_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_u64_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_u64_ret(*self).await } } - async fn list_s8_ret( + fn list_s8_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_s8_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_s8_ret(*self).await } } - async fn list_s16_ret( + fn list_s16_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_s16_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_s16_ret(*self).await } } - async fn list_s32_ret( + fn list_s32_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_s32_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_s32_ret(*self).await } } - async fn list_s64_ret( + fn list_s64_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_s64_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_s64_ret(*self).await } } - async fn list_f32_ret( + fn list_f32_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_f32_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_f32_ret(*self).await } } - async fn list_f64_ret( + fn list_f64_ret( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::list_f64_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::list_f64_ret(*self).await } } - async fn tuple_list( + fn tuple_list( &mut self, x: wasmtime::component::__internal::Vec<(u8, i8)>, - ) -> wasmtime::component::__internal::Vec<(i64, u32)> { - Host::tuple_list(*self, x).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec<(i64, u32)>, + > + Send { + async move { Host::tuple_list(*self, x).await } } - async fn string_list_arg( + fn string_list_arg( &mut self, a: wasmtime::component::__internal::Vec< wasmtime::component::__internal::String, >, - ) -> () { - Host::string_list_arg(*self, a).await + ) -> impl ::core::future::Future + Send { + async move { Host::string_list_arg(*self, a).await } } - async fn string_list_ret( + fn string_list_ret( &mut self, - ) -> wasmtime::component::__internal::Vec< - wasmtime::component::__internal::String, - > { - Host::string_list_ret(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + > + Send { + async move { Host::string_list_ret(*self).await } } - async fn tuple_string_list( + fn tuple_string_list( &mut self, x: wasmtime::component::__internal::Vec< (u8, wasmtime::component::__internal::String), >, - ) -> wasmtime::component::__internal::Vec< - (wasmtime::component::__internal::String, u8), - > { - Host::tuple_string_list(*self, x).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + (wasmtime::component::__internal::String, u8), + >, + > + Send { + async move { Host::tuple_string_list(*self, x).await } } - async fn string_list( + fn string_list( &mut self, x: wasmtime::component::__internal::Vec< wasmtime::component::__internal::String, >, - ) -> wasmtime::component::__internal::Vec< - wasmtime::component::__internal::String, - > { - Host::string_list(*self, x).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + > + Send { + async move { Host::string_list(*self, x).await } } - async fn record_list( + fn record_list( &mut self, x: wasmtime::component::__internal::Vec, - ) -> wasmtime::component::__internal::Vec { - Host::record_list(*self, x).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::record_list(*self, x).await } } - async fn record_list_reverse( + fn record_list_reverse( &mut self, x: wasmtime::component::__internal::Vec, - ) -> wasmtime::component::__internal::Vec { - Host::record_list_reverse(*self, x).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::record_list_reverse(*self, x).await } } - async fn variant_list( + fn variant_list( &mut self, x: wasmtime::component::__internal::Vec, - ) -> wasmtime::component::__internal::Vec { - Host::variant_list(*self, x).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::variant_list(*self, x).await } } - async fn load_store_everything( + fn load_store_everything( &mut self, a: LoadStoreAllSizes, - ) -> LoadStoreAllSizes { - Host::load_store_everything(*self, a).await + ) -> impl ::core::future::Future + Send { + async move { Host::load_store_everything(*self, a).await } } } pub fn add_to_linker( @@ -655,7 +745,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -1753,7 +1843,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/lists` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/many-arguments.rs b/crates/component-macro/tests/expanded/many-arguments.rs index e6746d916e..b686d0838a 100644 --- a/crates/component-macro/tests/expanded/many-arguments.rs +++ b/crates/component-macro/tests/expanded/many-arguments.rs @@ -52,6 +52,17 @@ impl<_T: 'static> TheWorldPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `the-world`. /// @@ -146,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::manyarg::HostWithStore, for<'a> D::Data<'a>: foo::foo::manyarg::Host, T: 'static, { @@ -250,6 +274,11 @@ pub mod foo { 4 == < BigStruct as wasmtime::component::ComponentType >::ALIGN32 ); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn many_args( &mut self, @@ -321,7 +350,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -531,7 +560,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/manyarg` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/many-arguments_async.rs b/crates/component-macro/tests/expanded/many-arguments_async.rs index 2d85a9e345..05ca37301e 100644 --- a/crates/component-macro/tests/expanded/many-arguments_async.rs +++ b/crates/component-macro/tests/expanded/many-arguments_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::manyarg::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::manyarg::Host + Send, T: 'static + Send, { @@ -256,9 +274,13 @@ pub mod foo { 4 == < BigStruct as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn many_args( + fn many_args( &mut self, a1: u64, a2: u64, @@ -276,11 +298,14 @@ pub mod foo { a14: u64, a15: u64, a16: u64, - ) -> (); - async fn big_argument(&mut self, x: BigStruct) -> (); + ) -> impl ::core::future::Future + Send; + fn big_argument( + &mut self, + x: BigStruct, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn many_args( + fn many_args( &mut self, a1: u64, a2: u64, @@ -298,30 +323,35 @@ pub mod foo { a14: u64, a15: u64, a16: u64, - ) -> () { - Host::many_args( - *self, - a1, - a2, - a3, - a4, - a5, - a6, - a7, - a8, - a9, - a10, - a11, - a12, - a13, - a14, - a15, - a16, - ) - .await + ) -> impl ::core::future::Future + Send { + async move { + Host::many_args( + *self, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + a10, + a11, + a12, + a13, + a14, + a15, + a16, + ) + .await + } } - async fn big_argument(&mut self, x: BigStruct) -> () { - Host::big_argument(*self, x).await + fn big_argument( + &mut self, + x: BigStruct, + ) -> impl ::core::future::Future + Send { + async move { Host::big_argument(*self, x).await } } } pub fn add_to_linker( @@ -329,7 +359,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -544,7 +574,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/manyarg` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/many-arguments_concurrent.rs b/crates/component-macro/tests/expanded/many-arguments_concurrent.rs index ee12d1cc90..46c44fb96a 100644 --- a/crates/component-macro/tests/expanded/many-arguments_concurrent.rs +++ b/crates/component-macro/tests/expanded/many-arguments_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::manyarg::HostConcurrent + Send, + D: foo::foo::manyarg::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::manyarg::Host + Send, T: 'static + Send, { @@ -256,8 +274,7 @@ pub mod foo { 4 == < BigStruct as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn many_args( accessor: &wasmtime::component::Accessor, a1: u64, @@ -276,17 +293,12 @@ pub mod foo { a14: u64, a15: u64, a16: u64, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn big_argument( accessor: &wasmtime::component::Accessor, x: BigStruct, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -294,7 +306,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -341,7 +353,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::many_args( + let r = ::many_args( accessor, arg0, arg1, @@ -373,7 +385,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::big_argument(accessor, arg0) + let r = ::big_argument(accessor, arg0) .await; Ok(r) }) diff --git a/crates/component-macro/tests/expanded/many-arguments_tracing_async.rs b/crates/component-macro/tests/expanded/many-arguments_tracing_async.rs index 3fad0a514b..106c4784cb 100644 --- a/crates/component-macro/tests/expanded/many-arguments_tracing_async.rs +++ b/crates/component-macro/tests/expanded/many-arguments_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::manyarg::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::manyarg::Host + Send, T: 'static + Send, { @@ -256,9 +274,13 @@ pub mod foo { 4 == < BigStruct as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn many_args( + fn many_args( &mut self, a1: u64, a2: u64, @@ -276,11 +298,14 @@ pub mod foo { a14: u64, a15: u64, a16: u64, - ) -> (); - async fn big_argument(&mut self, x: BigStruct) -> (); + ) -> impl ::core::future::Future + Send; + fn big_argument( + &mut self, + x: BigStruct, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn many_args( + fn many_args( &mut self, a1: u64, a2: u64, @@ -298,30 +323,35 @@ pub mod foo { a14: u64, a15: u64, a16: u64, - ) -> () { - Host::many_args( - *self, - a1, - a2, - a3, - a4, - a5, - a6, - a7, - a8, - a9, - a10, - a11, - a12, - a13, - a14, - a15, - a16, - ) - .await + ) -> impl ::core::future::Future + Send { + async move { + Host::many_args( + *self, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + a10, + a11, + a12, + a13, + a14, + a15, + a16, + ) + .await + } } - async fn big_argument(&mut self, x: BigStruct) -> () { - Host::big_argument(*self, x).await + fn big_argument( + &mut self, + x: BigStruct, + ) -> impl ::core::future::Future + Send { + async move { Host::big_argument(*self, x).await } } } pub fn add_to_linker( @@ -329,7 +359,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -587,7 +617,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/manyarg` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/multi-return_async.rs b/crates/component-macro/tests/expanded/multi-return_async.rs deleted file mode 100644 index cad8821bf2..0000000000 --- a/crates/component-macro/tests/expanded/multi-return_async.rs +++ /dev/null @@ -1,521 +0,0 @@ -/// Auto-generated bindings for a pre-instantiated version of a -/// component which implements the world `the-world`. -/// -/// This structure is created through [`TheWorldPre::new`] which -/// takes a [`InstancePre`](wasmtime::component::InstancePre) that -/// has been created through a [`Linker`](wasmtime::component::Linker). -/// -/// For more information see [`TheWorld`] as well. -pub struct TheWorldPre { - instance_pre: wasmtime::component::InstancePre, - indices: TheWorldIndices, -} -impl Clone for TheWorldPre { - fn clone(&self) -> Self { - Self { - instance_pre: self.instance_pre.clone(), - indices: self.indices.clone(), - } - } -} -impl<_T> TheWorldPre<_T> { - /// Creates a new copy of `TheWorldPre` bindings which can then - /// be used to instantiate into a particular store. - /// - /// This method may fail if the component behind `instance_pre` - /// does not have the required exports. - pub fn new( - instance_pre: wasmtime::component::InstancePre<_T>, - ) -> wasmtime::Result { - let indices = TheWorldIndices::new(instance_pre.component())?; - Ok(Self { instance_pre, indices }) - } - pub fn engine(&self) -> &wasmtime::Engine { - self.instance_pre.engine() - } - pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { - &self.instance_pre - } - /// Instantiates a new instance of [`TheWorld`] within the - /// `store` provided. - /// - /// This function will use `self` as the pre-instantiated - /// instance to perform instantiation. Afterwards the preloaded - /// indices in `self` are used to lookup all exports on the - /// resulting instance. - pub async fn instantiate_async( - &self, - mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { - let mut store = store.as_context_mut(); - let instance = self.instance_pre.instantiate_async(&mut store).await?; - self.indices.load(&mut store, &instance) - } -} -/// Auto-generated bindings for index of the exports of -/// `the-world`. -/// -/// This is an implementation detail of [`TheWorldPre`] and can -/// be constructed if needed as well. -/// -/// For more information see [`TheWorld`] as well. -#[derive(Clone)] -pub struct TheWorldIndices { - interface0: exports::foo::foo::multi_return::GuestIndices, -} -/// Auto-generated bindings for an instance a component which -/// implements the world `the-world`. -/// -/// This structure can be created through a number of means -/// depending on your requirements and what you have on hand: -/// -/// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a -/// [`Store`], [`Component`], and [`Linker`]. -/// -/// * Alternatively you can create a [`TheWorldPre`] ahead of -/// time with a [`Component`] to front-load string lookups -/// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to -/// create a [`TheWorld`]. -/// -/// * If you've instantiated the instance yourself already -/// then you can use [`TheWorld::new`]. -/// -/// * You can also access the guts of instantiation through -/// [`TheWorldIndices::new_instance`] followed -/// by [`TheWorldIndices::load`] to crate an instance of this -/// type. -/// -/// These methods are all equivalent to one another and move -/// around the tradeoff of what work is performed when. -/// -/// [`Store`]: wasmtime::Store -/// [`Component`]: wasmtime::component::Component -/// [`Linker`]: wasmtime::component::Linker -pub struct TheWorld { - interface0: exports::foo::foo::multi_return::Guest, -} -const _: () = { - #[allow(unused_imports)] - use wasmtime::component::__internal::anyhow; - impl TheWorldIndices { - /// Creates a new copy of `TheWorldIndices` bindings which can then - /// be used to instantiate into a particular store. - /// - /// This method may fail if the component does not have the - /// required exports. - pub fn new( - component: &wasmtime::component::Component, - ) -> wasmtime::Result { - let _component = component; - let interface0 = exports::foo::foo::multi_return::GuestIndices::new( - _component, - )?; - Ok(TheWorldIndices { interface0 }) - } - /// Creates a new instance of [`TheWorldIndices`] from an - /// instantiated component. - /// - /// This method of creating a [`TheWorld`] will perform string - /// lookups for all exports when this method is called. This - /// will only succeed if the provided instance matches the - /// requirements of [`TheWorld`]. - pub fn new_instance( - mut store: impl wasmtime::AsContextMut, - instance: &wasmtime::component::Instance, - ) -> wasmtime::Result { - let _instance = instance; - let interface0 = exports::foo::foo::multi_return::GuestIndices::new_instance( - &mut store, - _instance, - )?; - Ok(TheWorldIndices { interface0 }) - } - /// Uses the indices stored in `self` to load an instance - /// of [`TheWorld`] from the instance provided. - /// - /// Note that at this time this method will additionally - /// perform type-checks of all exports. - pub fn load( - &self, - mut store: impl wasmtime::AsContextMut, - instance: &wasmtime::component::Instance, - ) -> wasmtime::Result { - let _instance = instance; - let interface0 = self.interface0.load(&mut store, &_instance)?; - Ok(TheWorld { interface0 }) - } - } - impl TheWorld { - /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( - mut store: impl wasmtime::AsContextMut, - component: &wasmtime::component::Component, - linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { - let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await - } - /// Convenience wrapper around [`TheWorldIndices::new_instance`] and - /// [`TheWorldIndices::load`]. - pub fn new( - mut store: impl wasmtime::AsContextMut, - instance: &wasmtime::component::Instance, - ) -> wasmtime::Result { - let indices = TheWorldIndices::new_instance(&mut store, instance)?; - indices.load(store, instance) - } - pub fn add_to_linker( - linker: &mut wasmtime::component::Linker, - get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, - ) -> wasmtime::Result<()> - where - T: Send, - U: foo::foo::multi_return::Host + Send, - { - foo::foo::multi_return::add_to_linker(linker, get)?; - Ok(()) - } - pub fn foo_foo_multi_return(&self) -> &exports::foo::foo::multi_return::Guest { - &self.interface0 - } - } -}; -pub mod foo { - pub mod foo { - #[allow(clippy::all)] - pub mod multi_return { - #[allow(unused_imports)] - use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send { - async fn mra(&mut self) -> (); - async fn mrb(&mut self) -> (); - async fn mrc(&mut self) -> u32; - async fn mrd(&mut self) -> u32; - async fn mre(&mut self) -> (u32, f32); - } - pub trait GetHost< - T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { - type Host: Host + Send; - } - impl GetHost for F - where - F: Fn(T) -> O + Send + Sync + Copy + 'static, - O: Host + Send, - { - type Host = O; - } - pub fn add_to_linker_get_host< - T, - G: for<'a> GetHost<&'a mut T, Host: Host + Send>, - >( - linker: &mut wasmtime::component::Linker, - host_getter: G, - ) -> wasmtime::Result<()> - where - T: Send, - { - let mut inst = linker.instance("foo:foo/multi-return")?; - inst.func_wrap_async( - "mra", - move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { - wasmtime::component::__internal::Box::new(async move { - let host = &mut host_getter(caller.data_mut()); - let r = Host::mra(host).await; - Ok(r) - }) - }, - )?; - inst.func_wrap_async( - "mrb", - move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { - wasmtime::component::__internal::Box::new(async move { - let host = &mut host_getter(caller.data_mut()); - let r = Host::mrb(host).await; - Ok(r) - }) - }, - )?; - inst.func_wrap_async( - "mrc", - move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { - wasmtime::component::__internal::Box::new(async move { - let host = &mut host_getter(caller.data_mut()); - let r = Host::mrc(host).await; - Ok((r,)) - }) - }, - )?; - inst.func_wrap_async( - "mrd", - move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { - wasmtime::component::__internal::Box::new(async move { - let host = &mut host_getter(caller.data_mut()); - let r = Host::mrd(host).await; - Ok((r,)) - }) - }, - )?; - inst.func_wrap_async( - "mre", - move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { - wasmtime::component::__internal::Box::new(async move { - let host = &mut host_getter(caller.data_mut()); - let r = Host::mre(host).await; - Ok(r) - }) - }, - )?; - Ok(()) - } - pub fn add_to_linker( - linker: &mut wasmtime::component::Linker, - get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, - ) -> wasmtime::Result<()> - where - U: Host + Send, - T: Send, - { - add_to_linker_get_host(linker, get) - } - impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn mra(&mut self) -> () { - Host::mra(*self).await - } - async fn mrb(&mut self) -> () { - Host::mrb(*self).await - } - async fn mrc(&mut self) -> u32 { - Host::mrc(*self).await - } - async fn mrd(&mut self) -> u32 { - Host::mrd(*self).await - } - async fn mre(&mut self) -> (u32, f32) { - Host::mre(*self).await - } - } - } - } -} -pub mod exports { - pub mod foo { - pub mod foo { - #[allow(clippy::all)] - pub mod multi_return { - #[allow(unused_imports)] - use wasmtime::component::__internal::{anyhow, Box}; - pub struct Guest { - mra: wasmtime::component::Func, - mrb: wasmtime::component::Func, - mrc: wasmtime::component::Func, - mrd: wasmtime::component::Func, - mre: wasmtime::component::Func, - } - #[derive(Clone)] - pub struct GuestIndices { - mra: wasmtime::component::ComponentExportIndex, - mrb: wasmtime::component::ComponentExportIndex, - mrc: wasmtime::component::ComponentExportIndex, - mrd: wasmtime::component::ComponentExportIndex, - mre: wasmtime::component::ComponentExportIndex, - } - impl GuestIndices { - /// Constructor for [`GuestIndices`] which takes a - /// [`Component`](wasmtime::component::Component) as input and can be executed - /// before instantiation. - /// - /// This constructor can be used to front-load string lookups to find exports - /// within a component. - pub fn new( - component: &wasmtime::component::Component, - ) -> wasmtime::Result { - let (_, instance) = component - .export_index(None, "foo:foo/multi-return") - .ok_or_else(|| { - anyhow::anyhow!( - "no exported instance named `foo:foo/multi-return`" - ) - })?; - Self::_new(|name| { - component.export_index(Some(&instance), name).map(|p| p.1) - }) - } - /// This constructor is similar to [`GuestIndices::new`] except that it - /// performs string lookups after instantiation time. - pub fn new_instance( - mut store: impl wasmtime::AsContextMut, - instance: &wasmtime::component::Instance, - ) -> wasmtime::Result { - let instance_export = instance - .get_export(&mut store, None, "foo:foo/multi-return") - .ok_or_else(|| { - anyhow::anyhow!( - "no exported instance named `foo:foo/multi-return`" - ) - })?; - Self::_new(|name| { - instance.get_export(&mut store, Some(&instance_export), name) - }) - } - fn _new( - mut lookup: impl FnMut( - &str, - ) -> Option, - ) -> wasmtime::Result { - let mut lookup = move |name| { - lookup(name) - .ok_or_else(|| { - anyhow::anyhow!( - "instance export `foo:foo/multi-return` does \ - not have export `{name}`" - ) - }) - }; - let _ = &mut lookup; - let mra = lookup("mra")?; - let mrb = lookup("mrb")?; - let mrc = lookup("mrc")?; - let mrd = lookup("mrd")?; - let mre = lookup("mre")?; - Ok(GuestIndices { - mra, - mrb, - mrc, - mrd, - mre, - }) - } - pub fn load( - &self, - mut store: impl wasmtime::AsContextMut, - instance: &wasmtime::component::Instance, - ) -> wasmtime::Result { - let mut store = store.as_context_mut(); - let _ = &mut store; - let _instance = instance; - let mra = *_instance - .get_typed_func::<(), ()>(&mut store, &self.mra)? - .func(); - let mrb = *_instance - .get_typed_func::<(), ()>(&mut store, &self.mrb)? - .func(); - let mrc = *_instance - .get_typed_func::<(), (u32,)>(&mut store, &self.mrc)? - .func(); - let mrd = *_instance - .get_typed_func::<(), (u32,)>(&mut store, &self.mrd)? - .func(); - let mre = *_instance - .get_typed_func::<(), (u32, f32)>(&mut store, &self.mre)? - .func(); - Ok(Guest { mra, mrb, mrc, mrd, mre }) - } - } - impl Guest { - pub async fn call_mra( - &self, - mut store: S, - ) -> wasmtime::Result<()> - where - ::Data: Send, - { - let callee = unsafe { - wasmtime::component::TypedFunc::< - (), - (), - >::new_unchecked(self.mra) - }; - let () = callee.call_async(store.as_context_mut(), ()).await?; - callee.post_return_async(store.as_context_mut()).await?; - Ok(()) - } - pub async fn call_mrb( - &self, - mut store: S, - ) -> wasmtime::Result<()> - where - ::Data: Send, - { - let callee = unsafe { - wasmtime::component::TypedFunc::< - (), - (), - >::new_unchecked(self.mrb) - }; - let () = callee.call_async(store.as_context_mut(), ()).await?; - callee.post_return_async(store.as_context_mut()).await?; - Ok(()) - } - pub async fn call_mrc( - &self, - mut store: S, - ) -> wasmtime::Result - where - ::Data: Send, - { - let callee = unsafe { - wasmtime::component::TypedFunc::< - (), - (u32,), - >::new_unchecked(self.mrc) - }; - let (ret0,) = callee - .call_async(store.as_context_mut(), ()) - .await?; - callee.post_return_async(store.as_context_mut()).await?; - Ok(ret0) - } - pub async fn call_mrd( - &self, - mut store: S, - ) -> wasmtime::Result - where - ::Data: Send, - { - let callee = unsafe { - wasmtime::component::TypedFunc::< - (), - (u32,), - >::new_unchecked(self.mrd) - }; - let (ret0,) = callee - .call_async(store.as_context_mut(), ()) - .await?; - callee.post_return_async(store.as_context_mut()).await?; - Ok(ret0) - } - pub async fn call_mre( - &self, - mut store: S, - ) -> wasmtime::Result<(u32, f32)> - where - ::Data: Send, - { - let callee = unsafe { - wasmtime::component::TypedFunc::< - (), - (u32, f32), - >::new_unchecked(self.mre) - }; - let (ret0, ret1) = callee - .call_async(store.as_context_mut(), ()) - .await?; - callee.post_return_async(store.as_context_mut()).await?; - Ok((ret0, ret1)) - } - } - } - } - } -} diff --git a/crates/component-macro/tests/expanded/multi-return_concurrent.rs b/crates/component-macro/tests/expanded/multi-return_concurrent.rs deleted file mode 100644 index fb578d7464..0000000000 --- a/crates/component-macro/tests/expanded/multi-return_concurrent.rs +++ /dev/null @@ -1,767 +0,0 @@ -/// Auto-generated bindings for a pre-instantiated version of a -/// component which implements the world `the-world`. -/// -/// This structure is created through [`TheWorldPre::new`] which -/// takes a [`InstancePre`](wasmtime::component::InstancePre) that -/// has been created through a [`Linker`](wasmtime::component::Linker). -/// -/// For more information see [`TheWorld`] as well. -pub struct TheWorldPre { - instance_pre: wasmtime::component::InstancePre, - indices: TheWorldIndices, -} -impl Clone for TheWorldPre { - fn clone(&self) -> Self { - Self { - instance_pre: self.instance_pre.clone(), - indices: self.indices.clone(), - } - } -} -impl<_T> TheWorldPre<_T> { - /// Creates a new copy of `TheWorldPre` bindings which can then - /// be used to instantiate into a particular store. - /// - /// This method may fail if the component behind `instance_pre` - /// does not have the required exports. - pub fn new( - instance_pre: wasmtime::component::InstancePre<_T>, - ) -> wasmtime::Result { - let indices = TheWorldIndices::new(instance_pre.component())?; - Ok(Self { instance_pre, indices }) - } - pub fn engine(&self) -> &wasmtime::Engine { - self.instance_pre.engine() - } - pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { - &self.instance_pre - } - /// Instantiates a new instance of [`TheWorld`] within the - /// `store` provided. - /// - /// This function will use `self` as the pre-instantiated - /// instance to perform instantiation. Afterwards the preloaded - /// indices in `self` are used to lookup all exports on the - /// resulting instance. - pub async fn instantiate_async( - &self, - mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { - let mut store = store.as_context_mut(); - let instance = self.instance_pre.instantiate_async(&mut store).await?; - self.indices.load(&mut store, &instance) - } -} -/// Auto-generated bindings for index of the exports of -/// `the-world`. -/// -/// This is an implementation detail of [`TheWorldPre`] and can -/// be constructed if needed as well. -/// -/// For more information see [`TheWorld`] as well. -#[derive(Clone)] -pub struct TheWorldIndices { - interface0: exports::foo::foo::multi_return::GuestIndices, -} -/// Auto-generated bindings for an instance a component which -/// implements the world `the-world`. -/// -/// This structure can be created through a number of means -/// depending on your requirements and what you have on hand: -/// -/// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a -/// [`Store`], [`Component`], and [`Linker`]. -/// -/// * Alternatively you can create a [`TheWorldPre`] ahead of -/// time with a [`Component`] to front-load string lookups -/// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to -/// create a [`TheWorld`]. -/// -/// * If you've instantiated the instance yourself already -/// then you can use [`TheWorld::new`]. -/// -/// * You can also access the guts of instantiation through -/// [`TheWorldIndices::new_instance`] followed -/// by [`TheWorldIndices::load`] to crate an instance of this -/// type. -/// -/// These methods are all equivalent to one another and move -/// around the tradeoff of what work is performed when. -/// -/// [`Store`]: wasmtime::Store -/// [`Component`]: wasmtime::component::Component -/// [`Linker`]: wasmtime::component::Linker -pub struct TheWorld { - interface0: exports::foo::foo::multi_return::Guest, -} -const _: () = { - #[allow(unused_imports)] - use wasmtime::component::__internal::anyhow; - impl TheWorldIndices { - /// Creates a new copy of `TheWorldIndices` bindings which can then - /// be used to instantiate into a particular store. - /// - /// This method may fail if the component does not have the - /// required exports. - pub fn new( - component: &wasmtime::component::Component, - ) -> wasmtime::Result { - let _component = component; - let interface0 = exports::foo::foo::multi_return::GuestIndices::new( - _component, - )?; - Ok(TheWorldIndices { interface0 }) - } - /// Creates a new instance of [`TheWorldIndices`] from an - /// instantiated component. - /// - /// This method of creating a [`TheWorld`] will perform string - /// lookups for all exports when this method is called. This - /// will only succeed if the provided instance matches the - /// requirements of [`TheWorld`]. - pub fn new_instance( - mut store: impl wasmtime::AsContextMut, - instance: &wasmtime::component::Instance, - ) -> wasmtime::Result { - let _instance = instance; - let interface0 = exports::foo::foo::multi_return::GuestIndices::new_instance( - &mut store, - _instance, - )?; - Ok(TheWorldIndices { interface0 }) - } - /// Uses the indices stored in `self` to load an instance - /// of [`TheWorld`] from the instance provided. - /// - /// Note that at this time this method will additionally - /// perform type-checks of all exports. - pub fn load( - &self, - mut store: impl wasmtime::AsContextMut, - instance: &wasmtime::component::Instance, - ) -> wasmtime::Result { - let _instance = instance; - let interface0 = self.interface0.load(&mut store, &_instance)?; - Ok(TheWorld { interface0 }) - } - } - impl TheWorld { - /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( - mut store: impl wasmtime::AsContextMut, - component: &wasmtime::component::Component, - linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { - let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await - } - /// Convenience wrapper around [`TheWorldIndices::new_instance`] and - /// [`TheWorldIndices::load`]. - pub fn new( - mut store: impl wasmtime::AsContextMut, - instance: &wasmtime::component::Instance, - ) -> wasmtime::Result { - let indices = TheWorldIndices::new_instance(&mut store, instance)?; - indices.load(store, instance) - } - pub fn add_to_linker( - linker: &mut wasmtime::component::Linker, - get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, - ) -> wasmtime::Result<()> - where - T: Send + 'static, - U: foo::foo::multi_return::Host + Send, - { - foo::foo::multi_return::add_to_linker(linker, get)?; - Ok(()) - } - pub fn foo_foo_multi_return(&self) -> &exports::foo::foo::multi_return::Guest { - &self.interface0 - } - } -}; -pub mod foo { - pub mod foo { - #[allow(clippy::all)] - pub mod multi_return { - #[allow(unused_imports)] - use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send { - fn mra( - accessor: &mut wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send + Sync - where - Self: Sized; - fn mrb( - accessor: &mut wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send + Sync - where - Self: Sized; - fn mrc( - accessor: &mut wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send + Sync - where - Self: Sized; - fn mrd( - accessor: &mut wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send + Sync - where - Self: Sized; - fn mre( - accessor: &mut wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send + Sync - where - Self: Sized; - } - struct State { - host: *mut u8, - store: *mut u8, - spawned: Vec, - } - thread_local! { - static STATE : wasmtime::component::__internal::RefCell < Option < State - >> = wasmtime::component::__internal::RefCell::new(None); - } - struct ResetState(Option); - impl Drop for ResetState { - fn drop(&mut self) { - STATE - .with(|v| { - *v.borrow_mut() = self.0.take(); - }) - } - } - fn get_host_and_store() -> (*mut u8, *mut u8) { - STATE - .with(|v| { - v - .borrow() - .as_ref() - .map(|State { host, store, .. }| (*host, *store)) - }) - .unwrap() - } - fn spawn_task(task: wasmtime::component::__internal::Spawned) { - STATE.with(|v| v.borrow_mut().as_mut().unwrap().spawned.push(task)); - } - fn poll_with_state< - T, - G: for<'a> GetHost<&'a mut T>, - F: wasmtime::component::__internal::Future + ?Sized, - >( - getter: G, - store: wasmtime::VMStoreRawPtr, - cx: &mut wasmtime::component::__internal::Context, - future: wasmtime::component::__internal::Pin<&mut F>, - ) -> wasmtime::component::__internal::Poll { - use wasmtime::component::__internal::{SpawnedInner, mem, DerefMut, Poll}; - let mut store_cx = unsafe { - wasmtime::StoreContextMut::new(&mut *store.0.as_ptr().cast()) - }; - let (result, spawned) = { - let host = &mut getter(store_cx.data_mut()); - let old = STATE - .with(|v| { - v - .replace( - Some(State { - host: (host as *mut G::Host).cast(), - store: store.0.as_ptr().cast(), - spawned: Vec::new(), - }), - ) - }); - let _reset = ResetState(old); - (future.poll(cx), STATE.with(|v| v.take()).unwrap().spawned) - }; - for spawned in spawned { - store_cx - .spawn( - wasmtime::component::__internal::poll_fn(move |cx| { - let mut spawned = spawned.try_lock().unwrap(); - let inner = mem::replace( - DerefMut::deref_mut(&mut spawned), - SpawnedInner::Aborted, - ); - if let SpawnedInner::Unpolled(mut future) - | SpawnedInner::Polled { mut future, .. } = inner { - let result = poll_with_state( - getter, - store, - cx, - future.as_mut(), - ); - *DerefMut::deref_mut(&mut spawned) = SpawnedInner::Polled { - future, - waker: cx.waker().clone(), - }; - result - } else { - Poll::Ready(Ok(())) - } - }), - ) - } - result - } - pub trait GetHost< - T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { - type Host: Host + Send; - } - impl GetHost for F - where - F: Fn(T) -> O + Send + Sync + Copy + 'static, - O: Host + Send, - { - type Host = O; - } - pub fn add_to_linker_get_host< - T, - G: for<'a> GetHost<&'a mut T, Host: Host + Send>, - >( - linker: &mut wasmtime::component::Linker, - host_getter: G, - ) -> wasmtime::Result<()> - where - T: Send + 'static, - { - let mut inst = linker.instance("foo:foo/multi-return")?; - inst.func_wrap_concurrent( - "mra", - move |caller: wasmtime::StoreContextMut<'_, T>, (): ()| { - let mut accessor = unsafe { - wasmtime::component::Accessor::< - T, - _, - >::new(get_host_and_store, spawn_task) - }; - let mut future = wasmtime::component::__internal::Box::pin(async move { - let r = ::mra(&mut accessor).await; - Ok(r) - }); - let store = wasmtime::VMStoreRawPtr(caller.traitobj()); - wasmtime::component::__internal::Box::pin( - wasmtime::component::__internal::poll_fn(move |cx| poll_with_state( - host_getter, - store, - cx, - future.as_mut(), - )), - ) - }, - )?; - inst.func_wrap_concurrent( - "mrb", - move |caller: wasmtime::StoreContextMut<'_, T>, (): ()| { - let mut accessor = unsafe { - wasmtime::component::Accessor::< - T, - _, - >::new(get_host_and_store, spawn_task) - }; - let mut future = wasmtime::component::__internal::Box::pin(async move { - let r = ::mrb(&mut accessor).await; - Ok(r) - }); - let store = wasmtime::VMStoreRawPtr(caller.traitobj()); - wasmtime::component::__internal::Box::pin( - wasmtime::component::__internal::poll_fn(move |cx| poll_with_state( - host_getter, - store, - cx, - future.as_mut(), - )), - ) - }, - )?; - inst.func_wrap_concurrent( - "mrc", - move |caller: wasmtime::StoreContextMut<'_, T>, (): ()| { - let mut accessor = unsafe { - wasmtime::component::Accessor::< - T, - _, - >::new(get_host_and_store, spawn_task) - }; - let mut future = wasmtime::component::__internal::Box::pin(async move { - let r = ::mrc(&mut accessor).await; - Ok((r,)) - }); - let store = wasmtime::VMStoreRawPtr(caller.traitobj()); - wasmtime::component::__internal::Box::pin( - wasmtime::component::__internal::poll_fn(move |cx| poll_with_state( - host_getter, - store, - cx, - future.as_mut(), - )), - ) - }, - )?; - inst.func_wrap_concurrent( - "mrd", - move |caller: wasmtime::StoreContextMut<'_, T>, (): ()| { - let mut accessor = unsafe { - wasmtime::component::Accessor::< - T, - _, - >::new(get_host_and_store, spawn_task) - }; - let mut future = wasmtime::component::__internal::Box::pin(async move { - let r = ::mrd(&mut accessor).await; - Ok((r,)) - }); - let store = wasmtime::VMStoreRawPtr(caller.traitobj()); - wasmtime::component::__internal::Box::pin( - wasmtime::component::__internal::poll_fn(move |cx| poll_with_state( - host_getter, - store, - cx, - future.as_mut(), - )), - ) - }, - )?; - inst.func_wrap_concurrent( - "mre", - move |caller: wasmtime::StoreContextMut<'_, T>, (): ()| { - let mut accessor = unsafe { - wasmtime::component::Accessor::< - T, - _, - >::new(get_host_and_store, spawn_task) - }; - let mut future = wasmtime::component::__internal::Box::pin(async move { - let r = ::mre(&mut accessor).await; - Ok(r) - }); - let store = wasmtime::VMStoreRawPtr(caller.traitobj()); - wasmtime::component::__internal::Box::pin( - wasmtime::component::__internal::poll_fn(move |cx| poll_with_state( - host_getter, - store, - cx, - future.as_mut(), - )), - ) - }, - )?; - Ok(()) - } - pub fn add_to_linker( - linker: &mut wasmtime::component::Linker, - get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, - ) -> wasmtime::Result<()> - where - U: Host + Send, - T: Send + 'static, - { - add_to_linker_get_host(linker, get) - } - impl<_T: Host + Send> Host for &mut _T { - async fn mra( - accessor: &mut wasmtime::component::Accessor, - ) -> () { - struct Task {} - impl wasmtime::component::AccessorTask - for Task { - async fn run( - self, - accessor: &mut wasmtime::component::Accessor, - ) -> () { - ::mra(accessor).await - } - } - accessor.forward(|v| *v, Task {}).await - } - async fn mrb( - accessor: &mut wasmtime::component::Accessor, - ) -> () { - struct Task {} - impl wasmtime::component::AccessorTask - for Task { - async fn run( - self, - accessor: &mut wasmtime::component::Accessor, - ) -> () { - ::mrb(accessor).await - } - } - accessor.forward(|v| *v, Task {}).await - } - async fn mrc( - accessor: &mut wasmtime::component::Accessor, - ) -> u32 { - struct Task {} - impl< - T: 'static, - U: Host, - > wasmtime::component::AccessorTask for Task { - async fn run( - self, - accessor: &mut wasmtime::component::Accessor, - ) -> u32 { - ::mrc(accessor).await - } - } - accessor.forward(|v| *v, Task {}).await - } - async fn mrd( - accessor: &mut wasmtime::component::Accessor, - ) -> u32 { - struct Task {} - impl< - T: 'static, - U: Host, - > wasmtime::component::AccessorTask for Task { - async fn run( - self, - accessor: &mut wasmtime::component::Accessor, - ) -> u32 { - ::mrd(accessor).await - } - } - accessor.forward(|v| *v, Task {}).await - } - async fn mre( - accessor: &mut wasmtime::component::Accessor, - ) -> (u32, f32) { - struct Task {} - impl< - T: 'static, - U: Host, - > wasmtime::component::AccessorTask for Task { - async fn run( - self, - accessor: &mut wasmtime::component::Accessor, - ) -> (u32, f32) { - ::mre(accessor).await - } - } - accessor.forward(|v| *v, Task {}).await - } - } - } - } -} -pub mod exports { - pub mod foo { - pub mod foo { - #[allow(clippy::all)] - pub mod multi_return { - #[allow(unused_imports)] - use wasmtime::component::__internal::{anyhow, Box}; - pub struct Guest { - mra: wasmtime::component::Func, - mrb: wasmtime::component::Func, - mrc: wasmtime::component::Func, - mrd: wasmtime::component::Func, - mre: wasmtime::component::Func, - } - #[derive(Clone)] - pub struct GuestIndices { - mra: wasmtime::component::ComponentExportIndex, - mrb: wasmtime::component::ComponentExportIndex, - mrc: wasmtime::component::ComponentExportIndex, - mrd: wasmtime::component::ComponentExportIndex, - mre: wasmtime::component::ComponentExportIndex, - } - impl GuestIndices { - /// Constructor for [`GuestIndices`] which takes a - /// [`Component`](wasmtime::component::Component) as input and can be executed - /// before instantiation. - /// - /// This constructor can be used to front-load string lookups to find exports - /// within a component. - pub fn new( - component: &wasmtime::component::Component, - ) -> wasmtime::Result { - let (_, instance) = component - .export_index(None, "foo:foo/multi-return") - .ok_or_else(|| { - anyhow::anyhow!( - "no exported instance named `foo:foo/multi-return`" - ) - })?; - Self::_new(|name| { - component.export_index(Some(&instance), name).map(|p| p.1) - }) - } - /// This constructor is similar to [`GuestIndices::new`] except that it - /// performs string lookups after instantiation time. - pub fn new_instance( - mut store: impl wasmtime::AsContextMut, - instance: &wasmtime::component::Instance, - ) -> wasmtime::Result { - let instance_export = instance - .get_export(&mut store, None, "foo:foo/multi-return") - .ok_or_else(|| { - anyhow::anyhow!( - "no exported instance named `foo:foo/multi-return`" - ) - })?; - Self::_new(|name| { - instance.get_export(&mut store, Some(&instance_export), name) - }) - } - fn _new( - mut lookup: impl FnMut( - &str, - ) -> Option, - ) -> wasmtime::Result { - let mut lookup = move |name| { - lookup(name) - .ok_or_else(|| { - anyhow::anyhow!( - "instance export `foo:foo/multi-return` does \ - not have export `{name}`" - ) - }) - }; - let _ = &mut lookup; - let mra = lookup("mra")?; - let mrb = lookup("mrb")?; - let mrc = lookup("mrc")?; - let mrd = lookup("mrd")?; - let mre = lookup("mre")?; - Ok(GuestIndices { - mra, - mrb, - mrc, - mrd, - mre, - }) - } - pub fn load( - &self, - mut store: impl wasmtime::AsContextMut, - instance: &wasmtime::component::Instance, - ) -> wasmtime::Result { - let mut store = store.as_context_mut(); - let _ = &mut store; - let _instance = instance; - let mra = *_instance - .get_typed_func::<(), ()>(&mut store, &self.mra)? - .func(); - let mrb = *_instance - .get_typed_func::<(), ()>(&mut store, &self.mrb)? - .func(); - let mrc = *_instance - .get_typed_func::<(), (u32,)>(&mut store, &self.mrc)? - .func(); - let mrd = *_instance - .get_typed_func::<(), (u32,)>(&mut store, &self.mrd)? - .func(); - let mre = *_instance - .get_typed_func::<(), (u32, f32)>(&mut store, &self.mre)? - .func(); - Ok(Guest { mra, mrb, mrc, mrd, mre }) - } - } - impl Guest { - pub async fn call_mra( - &self, - mut store: S, - ) -> wasmtime::Result> - where - ::Data: Send, - { - let callee = unsafe { - wasmtime::component::TypedFunc::< - (), - (), - >::new_unchecked(self.mra) - }; - let promise = callee - .call_concurrent(store.as_context_mut(), ()) - .await?; - Ok(promise) - } - pub async fn call_mrb( - &self, - mut store: S, - ) -> wasmtime::Result> - where - ::Data: Send, - { - let callee = unsafe { - wasmtime::component::TypedFunc::< - (), - (), - >::new_unchecked(self.mrb) - }; - let promise = callee - .call_concurrent(store.as_context_mut(), ()) - .await?; - Ok(promise) - } - pub async fn call_mrc( - &self, - mut store: S, - ) -> wasmtime::Result> - where - ::Data: Send, - { - let callee = unsafe { - wasmtime::component::TypedFunc::< - (), - (u32,), - >::new_unchecked(self.mrc) - }; - let promise = callee - .call_concurrent(store.as_context_mut(), ()) - .await?; - Ok(promise.map(|(v,)| v)) - } - pub async fn call_mrd( - &self, - mut store: S, - ) -> wasmtime::Result> - where - ::Data: Send, - { - let callee = unsafe { - wasmtime::component::TypedFunc::< - (), - (u32,), - >::new_unchecked(self.mrd) - }; - let promise = callee - .call_concurrent(store.as_context_mut(), ()) - .await?; - Ok(promise.map(|(v,)| v)) - } - pub async fn call_mre( - &self, - mut store: S, - ) -> wasmtime::Result> - where - ::Data: Send, - { - let callee = unsafe { - wasmtime::component::TypedFunc::< - (), - (u32, f32), - >::new_unchecked(self.mre) - }; - let promise = callee - .call_concurrent(store.as_context_mut(), ()) - .await?; - Ok(promise) - } - } - } - } - } -} diff --git a/crates/component-macro/tests/expanded/multi-return_tracing_async.rs b/crates/component-macro/tests/expanded/multi-return_tracing_async.rs deleted file mode 100644 index 5f667313aa..0000000000 --- a/crates/component-macro/tests/expanded/multi-return_tracing_async.rs +++ /dev/null @@ -1,635 +0,0 @@ -/// Auto-generated bindings for a pre-instantiated version of a -/// component which implements the world `the-world`. -/// -/// This structure is created through [`TheWorldPre::new`] which -/// takes a [`InstancePre`](wasmtime::component::InstancePre) that -/// has been created through a [`Linker`](wasmtime::component::Linker). -/// -/// For more information see [`TheWorld`] as well. -pub struct TheWorldPre { - instance_pre: wasmtime::component::InstancePre, - indices: TheWorldIndices, -} -impl Clone for TheWorldPre { - fn clone(&self) -> Self { - Self { - instance_pre: self.instance_pre.clone(), - indices: self.indices.clone(), - } - } -} -impl<_T> TheWorldPre<_T> { - /// Creates a new copy of `TheWorldPre` bindings which can then - /// be used to instantiate into a particular store. - /// - /// This method may fail if the component behind `instance_pre` - /// does not have the required exports. - pub fn new( - instance_pre: wasmtime::component::InstancePre<_T>, - ) -> wasmtime::Result { - let indices = TheWorldIndices::new(instance_pre.component())?; - Ok(Self { instance_pre, indices }) - } - pub fn engine(&self) -> &wasmtime::Engine { - self.instance_pre.engine() - } - pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { - &self.instance_pre - } - /// Instantiates a new instance of [`TheWorld`] within the - /// `store` provided. - /// - /// This function will use `self` as the pre-instantiated - /// instance to perform instantiation. Afterwards the preloaded - /// indices in `self` are used to lookup all exports on the - /// resulting instance. - pub async fn instantiate_async( - &self, - mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { - let mut store = store.as_context_mut(); - let instance = self.instance_pre.instantiate_async(&mut store).await?; - self.indices.load(&mut store, &instance) - } -} -/// Auto-generated bindings for index of the exports of -/// `the-world`. -/// -/// This is an implementation detail of [`TheWorldPre`] and can -/// be constructed if needed as well. -/// -/// For more information see [`TheWorld`] as well. -#[derive(Clone)] -pub struct TheWorldIndices { - interface0: exports::foo::foo::multi_return::GuestIndices, -} -/// Auto-generated bindings for an instance a component which -/// implements the world `the-world`. -/// -/// This structure can be created through a number of means -/// depending on your requirements and what you have on hand: -/// -/// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a -/// [`Store`], [`Component`], and [`Linker`]. -/// -/// * Alternatively you can create a [`TheWorldPre`] ahead of -/// time with a [`Component`] to front-load string lookups -/// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to -/// create a [`TheWorld`]. -/// -/// * If you've instantiated the instance yourself already -/// then you can use [`TheWorld::new`]. -/// -/// * You can also access the guts of instantiation through -/// [`TheWorldIndices::new_instance`] followed -/// by [`TheWorldIndices::load`] to crate an instance of this -/// type. -/// -/// These methods are all equivalent to one another and move -/// around the tradeoff of what work is performed when. -/// -/// [`Store`]: wasmtime::Store -/// [`Component`]: wasmtime::component::Component -/// [`Linker`]: wasmtime::component::Linker -pub struct TheWorld { - interface0: exports::foo::foo::multi_return::Guest, -} -const _: () = { - #[allow(unused_imports)] - use wasmtime::component::__internal::anyhow; - impl TheWorldIndices { - /// Creates a new copy of `TheWorldIndices` bindings which can then - /// be used to instantiate into a particular store. - /// - /// This method may fail if the component does not have the - /// required exports. - pub fn new( - component: &wasmtime::component::Component, - ) -> wasmtime::Result { - let _component = component; - let interface0 = exports::foo::foo::multi_return::GuestIndices::new( - _component, - )?; - Ok(TheWorldIndices { interface0 }) - } - /// Creates a new instance of [`TheWorldIndices`] from an - /// instantiated component. - /// - /// This method of creating a [`TheWorld`] will perform string - /// lookups for all exports when this method is called. This - /// will only succeed if the provided instance matches the - /// requirements of [`TheWorld`]. - pub fn new_instance( - mut store: impl wasmtime::AsContextMut, - instance: &wasmtime::component::Instance, - ) -> wasmtime::Result { - let _instance = instance; - let interface0 = exports::foo::foo::multi_return::GuestIndices::new_instance( - &mut store, - _instance, - )?; - Ok(TheWorldIndices { interface0 }) - } - /// Uses the indices stored in `self` to load an instance - /// of [`TheWorld`] from the instance provided. - /// - /// Note that at this time this method will additionally - /// perform type-checks of all exports. - pub fn load( - &self, - mut store: impl wasmtime::AsContextMut, - instance: &wasmtime::component::Instance, - ) -> wasmtime::Result { - let _instance = instance; - let interface0 = self.interface0.load(&mut store, &_instance)?; - Ok(TheWorld { interface0 }) - } - } - impl TheWorld { - /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( - mut store: impl wasmtime::AsContextMut, - component: &wasmtime::component::Component, - linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { - let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await - } - /// Convenience wrapper around [`TheWorldIndices::new_instance`] and - /// [`TheWorldIndices::load`]. - pub fn new( - mut store: impl wasmtime::AsContextMut, - instance: &wasmtime::component::Instance, - ) -> wasmtime::Result { - let indices = TheWorldIndices::new_instance(&mut store, instance)?; - indices.load(store, instance) - } - pub fn add_to_linker( - linker: &mut wasmtime::component::Linker, - get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, - ) -> wasmtime::Result<()> - where - T: Send, - U: foo::foo::multi_return::Host + Send, - { - foo::foo::multi_return::add_to_linker(linker, get)?; - Ok(()) - } - pub fn foo_foo_multi_return(&self) -> &exports::foo::foo::multi_return::Guest { - &self.interface0 - } - } -}; -pub mod foo { - pub mod foo { - #[allow(clippy::all)] - pub mod multi_return { - #[allow(unused_imports)] - use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send { - async fn mra(&mut self) -> (); - async fn mrb(&mut self) -> (); - async fn mrc(&mut self) -> u32; - async fn mrd(&mut self) -> u32; - async fn mre(&mut self) -> (u32, f32); - } - pub trait GetHost< - T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { - type Host: Host + Send; - } - impl GetHost for F - where - F: Fn(T) -> O + Send + Sync + Copy + 'static, - O: Host + Send, - { - type Host = O; - } - pub fn add_to_linker_get_host< - T, - G: for<'a> GetHost<&'a mut T, Host: Host + Send>, - >( - linker: &mut wasmtime::component::Linker, - host_getter: G, - ) -> wasmtime::Result<()> - where - T: Send, - { - let mut inst = linker.instance("foo:foo/multi-return")?; - inst.func_wrap_async( - "mra", - move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { - use tracing::Instrument; - let span = tracing::span!( - tracing::Level::TRACE, "wit-bindgen import", module = - "multi-return", function = "mra", - ); - wasmtime::component::__internal::Box::new( - async move { - tracing::event!(tracing::Level::TRACE, "call"); - let host = &mut host_getter(caller.data_mut()); - let r = Host::mra(host).await; - tracing::event!( - tracing::Level::TRACE, result = tracing::field::debug(& r), - "return" - ); - Ok(r) - } - .instrument(span), - ) - }, - )?; - inst.func_wrap_async( - "mrb", - move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { - use tracing::Instrument; - let span = tracing::span!( - tracing::Level::TRACE, "wit-bindgen import", module = - "multi-return", function = "mrb", - ); - wasmtime::component::__internal::Box::new( - async move { - tracing::event!(tracing::Level::TRACE, "call"); - let host = &mut host_getter(caller.data_mut()); - let r = Host::mrb(host).await; - tracing::event!( - tracing::Level::TRACE, result = tracing::field::debug(& r), - "return" - ); - Ok(r) - } - .instrument(span), - ) - }, - )?; - inst.func_wrap_async( - "mrc", - move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { - use tracing::Instrument; - let span = tracing::span!( - tracing::Level::TRACE, "wit-bindgen import", module = - "multi-return", function = "mrc", - ); - wasmtime::component::__internal::Box::new( - async move { - tracing::event!(tracing::Level::TRACE, "call"); - let host = &mut host_getter(caller.data_mut()); - let r = Host::mrc(host).await; - tracing::event!( - tracing::Level::TRACE, result = tracing::field::debug(& r), - "return" - ); - Ok((r,)) - } - .instrument(span), - ) - }, - )?; - inst.func_wrap_async( - "mrd", - move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { - use tracing::Instrument; - let span = tracing::span!( - tracing::Level::TRACE, "wit-bindgen import", module = - "multi-return", function = "mrd", - ); - wasmtime::component::__internal::Box::new( - async move { - tracing::event!(tracing::Level::TRACE, "call"); - let host = &mut host_getter(caller.data_mut()); - let r = Host::mrd(host).await; - tracing::event!( - tracing::Level::TRACE, result = tracing::field::debug(& r), - "return" - ); - Ok((r,)) - } - .instrument(span), - ) - }, - )?; - inst.func_wrap_async( - "mre", - move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { - use tracing::Instrument; - let span = tracing::span!( - tracing::Level::TRACE, "wit-bindgen import", module = - "multi-return", function = "mre", - ); - wasmtime::component::__internal::Box::new( - async move { - tracing::event!(tracing::Level::TRACE, "call"); - let host = &mut host_getter(caller.data_mut()); - let r = Host::mre(host).await; - tracing::event!( - tracing::Level::TRACE, result = tracing::field::debug(& r), - "return" - ); - Ok(r) - } - .instrument(span), - ) - }, - )?; - Ok(()) - } - pub fn add_to_linker( - linker: &mut wasmtime::component::Linker, - get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, - ) -> wasmtime::Result<()> - where - U: Host + Send, - T: Send, - { - add_to_linker_get_host(linker, get) - } - impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn mra(&mut self) -> () { - Host::mra(*self).await - } - async fn mrb(&mut self) -> () { - Host::mrb(*self).await - } - async fn mrc(&mut self) -> u32 { - Host::mrc(*self).await - } - async fn mrd(&mut self) -> u32 { - Host::mrd(*self).await - } - async fn mre(&mut self) -> (u32, f32) { - Host::mre(*self).await - } - } - } - } -} -pub mod exports { - pub mod foo { - pub mod foo { - #[allow(clippy::all)] - pub mod multi_return { - #[allow(unused_imports)] - use wasmtime::component::__internal::{anyhow, Box}; - pub struct Guest { - mra: wasmtime::component::Func, - mrb: wasmtime::component::Func, - mrc: wasmtime::component::Func, - mrd: wasmtime::component::Func, - mre: wasmtime::component::Func, - } - #[derive(Clone)] - pub struct GuestIndices { - mra: wasmtime::component::ComponentExportIndex, - mrb: wasmtime::component::ComponentExportIndex, - mrc: wasmtime::component::ComponentExportIndex, - mrd: wasmtime::component::ComponentExportIndex, - mre: wasmtime::component::ComponentExportIndex, - } - impl GuestIndices { - /// Constructor for [`GuestIndices`] which takes a - /// [`Component`](wasmtime::component::Component) as input and can be executed - /// before instantiation. - /// - /// This constructor can be used to front-load string lookups to find exports - /// within a component. - pub fn new( - component: &wasmtime::component::Component, - ) -> wasmtime::Result { - let (_, instance) = component - .export_index(None, "foo:foo/multi-return") - .ok_or_else(|| { - anyhow::anyhow!( - "no exported instance named `foo:foo/multi-return`" - ) - })?; - Self::_new(|name| { - component.export_index(Some(&instance), name).map(|p| p.1) - }) - } - /// This constructor is similar to [`GuestIndices::new`] except that it - /// performs string lookups after instantiation time. - pub fn new_instance( - mut store: impl wasmtime::AsContextMut, - instance: &wasmtime::component::Instance, - ) -> wasmtime::Result { - let instance_export = instance - .get_export(&mut store, None, "foo:foo/multi-return") - .ok_or_else(|| { - anyhow::anyhow!( - "no exported instance named `foo:foo/multi-return`" - ) - })?; - Self::_new(|name| { - instance.get_export(&mut store, Some(&instance_export), name) - }) - } - fn _new( - mut lookup: impl FnMut( - &str, - ) -> Option, - ) -> wasmtime::Result { - let mut lookup = move |name| { - lookup(name) - .ok_or_else(|| { - anyhow::anyhow!( - "instance export `foo:foo/multi-return` does \ - not have export `{name}`" - ) - }) - }; - let _ = &mut lookup; - let mra = lookup("mra")?; - let mrb = lookup("mrb")?; - let mrc = lookup("mrc")?; - let mrd = lookup("mrd")?; - let mre = lookup("mre")?; - Ok(GuestIndices { - mra, - mrb, - mrc, - mrd, - mre, - }) - } - pub fn load( - &self, - mut store: impl wasmtime::AsContextMut, - instance: &wasmtime::component::Instance, - ) -> wasmtime::Result { - let mut store = store.as_context_mut(); - let _ = &mut store; - let _instance = instance; - let mra = *_instance - .get_typed_func::<(), ()>(&mut store, &self.mra)? - .func(); - let mrb = *_instance - .get_typed_func::<(), ()>(&mut store, &self.mrb)? - .func(); - let mrc = *_instance - .get_typed_func::<(), (u32,)>(&mut store, &self.mrc)? - .func(); - let mrd = *_instance - .get_typed_func::<(), (u32,)>(&mut store, &self.mrd)? - .func(); - let mre = *_instance - .get_typed_func::<(), (u32, f32)>(&mut store, &self.mre)? - .func(); - Ok(Guest { mra, mrb, mrc, mrd, mre }) - } - } - impl Guest { - pub async fn call_mra( - &self, - mut store: S, - ) -> wasmtime::Result<()> - where - ::Data: Send, - { - use tracing::Instrument; - let span = tracing::span!( - tracing::Level::TRACE, "wit-bindgen export", module = - "foo:foo/multi-return", function = "mra", - ); - let callee = unsafe { - wasmtime::component::TypedFunc::< - (), - (), - >::new_unchecked(self.mra) - }; - let () = callee - .call_async(store.as_context_mut(), ()) - .instrument(span.clone()) - .await?; - callee - .post_return_async(store.as_context_mut()) - .instrument(span) - .await?; - Ok(()) - } - pub async fn call_mrb( - &self, - mut store: S, - ) -> wasmtime::Result<()> - where - ::Data: Send, - { - use tracing::Instrument; - let span = tracing::span!( - tracing::Level::TRACE, "wit-bindgen export", module = - "foo:foo/multi-return", function = "mrb", - ); - let callee = unsafe { - wasmtime::component::TypedFunc::< - (), - (), - >::new_unchecked(self.mrb) - }; - let () = callee - .call_async(store.as_context_mut(), ()) - .instrument(span.clone()) - .await?; - callee - .post_return_async(store.as_context_mut()) - .instrument(span) - .await?; - Ok(()) - } - pub async fn call_mrc( - &self, - mut store: S, - ) -> wasmtime::Result - where - ::Data: Send, - { - use tracing::Instrument; - let span = tracing::span!( - tracing::Level::TRACE, "wit-bindgen export", module = - "foo:foo/multi-return", function = "mrc", - ); - let callee = unsafe { - wasmtime::component::TypedFunc::< - (), - (u32,), - >::new_unchecked(self.mrc) - }; - let (ret0,) = callee - .call_async(store.as_context_mut(), ()) - .instrument(span.clone()) - .await?; - callee - .post_return_async(store.as_context_mut()) - .instrument(span) - .await?; - Ok(ret0) - } - pub async fn call_mrd( - &self, - mut store: S, - ) -> wasmtime::Result - where - ::Data: Send, - { - use tracing::Instrument; - let span = tracing::span!( - tracing::Level::TRACE, "wit-bindgen export", module = - "foo:foo/multi-return", function = "mrd", - ); - let callee = unsafe { - wasmtime::component::TypedFunc::< - (), - (u32,), - >::new_unchecked(self.mrd) - }; - let (ret0,) = callee - .call_async(store.as_context_mut(), ()) - .instrument(span.clone()) - .await?; - callee - .post_return_async(store.as_context_mut()) - .instrument(span) - .await?; - Ok(ret0) - } - pub async fn call_mre( - &self, - mut store: S, - ) -> wasmtime::Result<(u32, f32)> - where - ::Data: Send, - { - use tracing::Instrument; - let span = tracing::span!( - tracing::Level::TRACE, "wit-bindgen export", module = - "foo:foo/multi-return", function = "mre", - ); - let callee = unsafe { - wasmtime::component::TypedFunc::< - (), - (u32, f32), - >::new_unchecked(self.mre) - }; - let (ret0, ret1) = callee - .call_async(store.as_context_mut(), ()) - .instrument(span.clone()) - .await?; - callee - .post_return_async(store.as_context_mut()) - .instrument(span) - .await?; - Ok((ret0, ret1)) - } - } - } - } - } -} diff --git a/crates/component-macro/tests/expanded/multiversion.rs b/crates/component-macro/tests/expanded/multiversion.rs index fa52a865b3..90622deb90 100644 --- a/crates/component-macro/tests/expanded/multiversion.rs +++ b/crates/component-macro/tests/expanded/multiversion.rs @@ -52,6 +52,17 @@ impl<_T: 'static> FooPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> FooPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `foo`. /// @@ -151,12 +162,25 @@ const _: () = { let indices = FooIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: my::dep0_1_0::a::HostWithStore + my::dep0_2_0::a::HostWithStore, for<'a> D::Data<'a>: my::dep0_1_0::a::Host + my::dep0_2_0::a::Host, T: 'static, { @@ -178,6 +202,11 @@ pub mod my { pub mod a { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn x(&mut self) -> (); } @@ -191,7 +220,7 @@ pub mod my { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -213,6 +242,11 @@ pub mod my { pub mod a { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn x(&mut self) -> (); } @@ -226,7 +260,7 @@ pub mod my { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -283,7 +317,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `my:dep/a@0.1.0` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; @@ -362,7 +396,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `my:dep/a@0.2.0` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/multiversion_async.rs b/crates/component-macro/tests/expanded/multiversion_async.rs index c693975de2..9fb0b0b247 100644 --- a/crates/component-macro/tests/expanded/multiversion_async.rs +++ b/crates/component-macro/tests/expanded/multiversion_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> FooPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> FooPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -74,13 +82,13 @@ pub struct FooIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Foo::instantiate_async`] which only needs a +/// [`Foo::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`FooPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`FooPre::instantiate_async`] to +/// method then uses [`FooPre::instantiate`] to /// create a [`Foo`]. /// /// * If you've instantiated the instance yourself already @@ -136,17 +144,14 @@ const _: () = { } impl Foo { /// Convenience wrapper around [`FooPre::new`] and - /// [`FooPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`FooPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - FooPre::new(pre)?.instantiate_async(store).await + FooPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`FooIndices::new`] and /// [`FooIndices::load`]. @@ -157,12 +162,25 @@ const _: () = { let indices = FooIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: my::dep0_1_0::a::HostWithStore + my::dep0_2_0::a::HostWithStore + Send, for<'a> D::Data<'a>: my::dep0_1_0::a::Host + my::dep0_2_0::a::Host + Send, T: 'static + Send, { @@ -184,13 +202,17 @@ pub mod my { pub mod a { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn x(&mut self) -> (); + fn x(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn x(&mut self) -> () { - Host::x(*self).await + fn x(&mut self) -> impl ::core::future::Future + Send { + async move { Host::x(*self).await } } } pub fn add_to_linker( @@ -198,7 +220,7 @@ pub mod my { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -222,13 +244,17 @@ pub mod my { pub mod a { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn x(&mut self) -> (); + fn x(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn x(&mut self) -> () { - Host::x(*self).await + fn x(&mut self) -> impl ::core::future::Future + Send { + async move { Host::x(*self).await } } } pub fn add_to_linker( @@ -236,7 +262,7 @@ pub mod my { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -295,7 +321,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `my:dep/a@0.1.0` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; @@ -377,7 +403,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `my:dep/a@0.2.0` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/multiversion_concurrent.rs b/crates/component-macro/tests/expanded/multiversion_concurrent.rs index 2a7ea3a991..866e8184a1 100644 --- a/crates/component-macro/tests/expanded/multiversion_concurrent.rs +++ b/crates/component-macro/tests/expanded/multiversion_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> FooPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> FooPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -74,13 +82,13 @@ pub struct FooIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Foo::instantiate_async`] which only needs a +/// [`Foo::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`FooPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`FooPre::instantiate_async`] to +/// method then uses [`FooPre::instantiate`] to /// create a [`Foo`]. /// /// * If you've instantiated the instance yourself already @@ -136,17 +144,14 @@ const _: () = { } impl Foo { /// Convenience wrapper around [`FooPre::new`] and - /// [`FooPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`FooPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - FooPre::new(pre)?.instantiate_async(store).await + FooPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`FooIndices::new`] and /// [`FooIndices::load`]. @@ -157,12 +162,25 @@ const _: () = { let indices = FooIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: my::dep0_1_0::a::HostConcurrent + my::dep0_2_0::a::HostConcurrent + Send, + D: my::dep0_1_0::a::HostWithStore + my::dep0_2_0::a::HostWithStore + Send, for<'a> D::Data<'a>: my::dep0_1_0::a::Host + my::dep0_2_0::a::Host + Send, T: 'static + Send, { @@ -184,15 +202,11 @@ pub mod my { pub mod a { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn x( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -200,7 +214,7 @@ pub mod my { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -210,7 +224,7 @@ pub mod my { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::x(accessor).await; + let r = ::x(accessor).await; Ok(r) }) }, @@ -224,15 +238,11 @@ pub mod my { pub mod a { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn x( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -240,7 +250,7 @@ pub mod my { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -250,7 +260,7 @@ pub mod my { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::x(accessor).await; + let r = ::x(accessor).await; Ok(r) }) }, diff --git a/crates/component-macro/tests/expanded/multiversion_tracing_async.rs b/crates/component-macro/tests/expanded/multiversion_tracing_async.rs index 3c77715234..e2ce390bf0 100644 --- a/crates/component-macro/tests/expanded/multiversion_tracing_async.rs +++ b/crates/component-macro/tests/expanded/multiversion_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> FooPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> FooPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -74,13 +82,13 @@ pub struct FooIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Foo::instantiate_async`] which only needs a +/// [`Foo::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`FooPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`FooPre::instantiate_async`] to +/// method then uses [`FooPre::instantiate`] to /// create a [`Foo`]. /// /// * If you've instantiated the instance yourself already @@ -136,17 +144,14 @@ const _: () = { } impl Foo { /// Convenience wrapper around [`FooPre::new`] and - /// [`FooPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`FooPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - FooPre::new(pre)?.instantiate_async(store).await + FooPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`FooIndices::new`] and /// [`FooIndices::load`]. @@ -157,12 +162,25 @@ const _: () = { let indices = FooIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: my::dep0_1_0::a::HostWithStore + my::dep0_2_0::a::HostWithStore + Send, for<'a> D::Data<'a>: my::dep0_1_0::a::Host + my::dep0_2_0::a::Host + Send, T: 'static + Send, { @@ -184,13 +202,17 @@ pub mod my { pub mod a { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn x(&mut self) -> (); + fn x(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn x(&mut self) -> () { - Host::x(*self).await + fn x(&mut self) -> impl ::core::future::Future + Send { + async move { Host::x(*self).await } } } pub fn add_to_linker( @@ -198,7 +220,7 @@ pub mod my { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -235,13 +257,17 @@ pub mod my { pub mod a { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn x(&mut self) -> (); + fn x(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn x(&mut self) -> () { - Host::x(*self).await + fn x(&mut self) -> impl ::core::future::Future + Send { + async move { Host::x(*self).await } } } pub fn add_to_linker( @@ -249,7 +275,7 @@ pub mod my { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -321,7 +347,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `my:dep/a@0.1.0` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; @@ -414,7 +440,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `my:dep/a@0.2.0` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/path1.rs b/crates/component-macro/tests/expanded/path1.rs index a3cf71191f..23b059eb7f 100644 --- a/crates/component-macro/tests/expanded/path1.rs +++ b/crates/component-macro/tests/expanded/path1.rs @@ -52,6 +52,17 @@ impl<_T: 'static> Path1Pre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> Path1Pre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `path1`. /// @@ -138,12 +149,25 @@ const _: () = { let indices = Path1Indices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`Path1Pre::new`] and + /// [`Path1Pre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + Path1Pre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: paths::path1::test::HostWithStore, for<'a> D::Data<'a>: paths::path1::test::Host, T: 'static, { @@ -158,6 +182,11 @@ pub mod paths { pub mod test { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host {} impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( @@ -165,7 +194,7 @@ pub mod paths { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { diff --git a/crates/component-macro/tests/expanded/path1_async.rs b/crates/component-macro/tests/expanded/path1_async.rs index 16051de501..23b059eb7f 100644 --- a/crates/component-macro/tests/expanded/path1_async.rs +++ b/crates/component-macro/tests/expanded/path1_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> Path1Pre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> Path1Pre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct Path1Indices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Path1::instantiate_async`] which only needs a +/// [`Path1::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`Path1Pre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`Path1Pre::instantiate_async`] to +/// method then uses [`Path1Pre::instantiate`] to /// create a [`Path1`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Path1 { /// Convenience wrapper around [`Path1Pre::new`] and - /// [`Path1Pre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`Path1Pre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - Path1Pre::new(pre)?.instantiate_async(store).await + Path1Pre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`Path1Indices::new`] and /// [`Path1Indices::load`]. @@ -144,14 +149,27 @@ const _: () = { let indices = Path1Indices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`Path1Pre::new`] and + /// [`Path1Pre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + Path1Pre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, - for<'a> D::Data<'a>: paths::path1::test::Host + Send, - T: 'static + Send, + D: paths::path1::test::HostWithStore, + for<'a> D::Data<'a>: paths::path1::test::Host, + T: 'static, { paths::path1::test::add_to_linker::(linker, host_getter)?; Ok(()) @@ -164,17 +182,21 @@ pub mod paths { pub mod test { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("paths:path1/test")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/path1_concurrent.rs b/crates/component-macro/tests/expanded/path1_concurrent.rs index 16051de501..23b059eb7f 100644 --- a/crates/component-macro/tests/expanded/path1_concurrent.rs +++ b/crates/component-macro/tests/expanded/path1_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> Path1Pre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> Path1Pre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct Path1Indices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Path1::instantiate_async`] which only needs a +/// [`Path1::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`Path1Pre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`Path1Pre::instantiate_async`] to +/// method then uses [`Path1Pre::instantiate`] to /// create a [`Path1`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Path1 { /// Convenience wrapper around [`Path1Pre::new`] and - /// [`Path1Pre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`Path1Pre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - Path1Pre::new(pre)?.instantiate_async(store).await + Path1Pre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`Path1Indices::new`] and /// [`Path1Indices::load`]. @@ -144,14 +149,27 @@ const _: () = { let indices = Path1Indices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`Path1Pre::new`] and + /// [`Path1Pre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + Path1Pre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, - for<'a> D::Data<'a>: paths::path1::test::Host + Send, - T: 'static + Send, + D: paths::path1::test::HostWithStore, + for<'a> D::Data<'a>: paths::path1::test::Host, + T: 'static, { paths::path1::test::add_to_linker::(linker, host_getter)?; Ok(()) @@ -164,17 +182,21 @@ pub mod paths { pub mod test { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("paths:path1/test")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/path1_tracing_async.rs b/crates/component-macro/tests/expanded/path1_tracing_async.rs index 16051de501..23b059eb7f 100644 --- a/crates/component-macro/tests/expanded/path1_tracing_async.rs +++ b/crates/component-macro/tests/expanded/path1_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> Path1Pre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> Path1Pre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct Path1Indices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Path1::instantiate_async`] which only needs a +/// [`Path1::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`Path1Pre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`Path1Pre::instantiate_async`] to +/// method then uses [`Path1Pre::instantiate`] to /// create a [`Path1`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Path1 { /// Convenience wrapper around [`Path1Pre::new`] and - /// [`Path1Pre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`Path1Pre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - Path1Pre::new(pre)?.instantiate_async(store).await + Path1Pre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`Path1Indices::new`] and /// [`Path1Indices::load`]. @@ -144,14 +149,27 @@ const _: () = { let indices = Path1Indices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`Path1Pre::new`] and + /// [`Path1Pre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + Path1Pre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, - for<'a> D::Data<'a>: paths::path1::test::Host + Send, - T: 'static + Send, + D: paths::path1::test::HostWithStore, + for<'a> D::Data<'a>: paths::path1::test::Host, + T: 'static, { paths::path1::test::add_to_linker::(linker, host_getter)?; Ok(()) @@ -164,17 +182,21 @@ pub mod paths { pub mod test { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("paths:path1/test")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/path2.rs b/crates/component-macro/tests/expanded/path2.rs index b70e5355c3..597c2d702e 100644 --- a/crates/component-macro/tests/expanded/path2.rs +++ b/crates/component-macro/tests/expanded/path2.rs @@ -52,6 +52,17 @@ impl<_T: 'static> Path2Pre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> Path2Pre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `path2`. /// @@ -138,12 +149,25 @@ const _: () = { let indices = Path2Indices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`Path2Pre::new`] and + /// [`Path2Pre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + Path2Pre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: paths::path2::test::HostWithStore, for<'a> D::Data<'a>: paths::path2::test::Host, T: 'static, { @@ -158,6 +182,11 @@ pub mod paths { pub mod test { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host {} impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( @@ -165,7 +194,7 @@ pub mod paths { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { diff --git a/crates/component-macro/tests/expanded/path2_async.rs b/crates/component-macro/tests/expanded/path2_async.rs index 9aa8ec6056..597c2d702e 100644 --- a/crates/component-macro/tests/expanded/path2_async.rs +++ b/crates/component-macro/tests/expanded/path2_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> Path2Pre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> Path2Pre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct Path2Indices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Path2::instantiate_async`] which only needs a +/// [`Path2::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`Path2Pre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`Path2Pre::instantiate_async`] to +/// method then uses [`Path2Pre::instantiate`] to /// create a [`Path2`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Path2 { /// Convenience wrapper around [`Path2Pre::new`] and - /// [`Path2Pre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`Path2Pre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - Path2Pre::new(pre)?.instantiate_async(store).await + Path2Pre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`Path2Indices::new`] and /// [`Path2Indices::load`]. @@ -144,14 +149,27 @@ const _: () = { let indices = Path2Indices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`Path2Pre::new`] and + /// [`Path2Pre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + Path2Pre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, - for<'a> D::Data<'a>: paths::path2::test::Host + Send, - T: 'static + Send, + D: paths::path2::test::HostWithStore, + for<'a> D::Data<'a>: paths::path2::test::Host, + T: 'static, { paths::path2::test::add_to_linker::(linker, host_getter)?; Ok(()) @@ -164,17 +182,21 @@ pub mod paths { pub mod test { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("paths:path2/test")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/path2_concurrent.rs b/crates/component-macro/tests/expanded/path2_concurrent.rs index 9aa8ec6056..597c2d702e 100644 --- a/crates/component-macro/tests/expanded/path2_concurrent.rs +++ b/crates/component-macro/tests/expanded/path2_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> Path2Pre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> Path2Pre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct Path2Indices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Path2::instantiate_async`] which only needs a +/// [`Path2::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`Path2Pre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`Path2Pre::instantiate_async`] to +/// method then uses [`Path2Pre::instantiate`] to /// create a [`Path2`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Path2 { /// Convenience wrapper around [`Path2Pre::new`] and - /// [`Path2Pre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`Path2Pre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - Path2Pre::new(pre)?.instantiate_async(store).await + Path2Pre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`Path2Indices::new`] and /// [`Path2Indices::load`]. @@ -144,14 +149,27 @@ const _: () = { let indices = Path2Indices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`Path2Pre::new`] and + /// [`Path2Pre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + Path2Pre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, - for<'a> D::Data<'a>: paths::path2::test::Host + Send, - T: 'static + Send, + D: paths::path2::test::HostWithStore, + for<'a> D::Data<'a>: paths::path2::test::Host, + T: 'static, { paths::path2::test::add_to_linker::(linker, host_getter)?; Ok(()) @@ -164,17 +182,21 @@ pub mod paths { pub mod test { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("paths:path2/test")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/path2_tracing_async.rs b/crates/component-macro/tests/expanded/path2_tracing_async.rs index 9aa8ec6056..597c2d702e 100644 --- a/crates/component-macro/tests/expanded/path2_tracing_async.rs +++ b/crates/component-macro/tests/expanded/path2_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> Path2Pre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> Path2Pre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct Path2Indices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Path2::instantiate_async`] which only needs a +/// [`Path2::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`Path2Pre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`Path2Pre::instantiate_async`] to +/// method then uses [`Path2Pre::instantiate`] to /// create a [`Path2`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Path2 { /// Convenience wrapper around [`Path2Pre::new`] and - /// [`Path2Pre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`Path2Pre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - Path2Pre::new(pre)?.instantiate_async(store).await + Path2Pre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`Path2Indices::new`] and /// [`Path2Indices::load`]. @@ -144,14 +149,27 @@ const _: () = { let indices = Path2Indices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`Path2Pre::new`] and + /// [`Path2Pre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + Path2Pre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, - for<'a> D::Data<'a>: paths::path2::test::Host + Send, - T: 'static + Send, + D: paths::path2::test::HostWithStore, + for<'a> D::Data<'a>: paths::path2::test::Host, + T: 'static, { paths::path2::test::add_to_linker::(linker, host_getter)?; Ok(()) @@ -164,17 +182,21 @@ pub mod paths { pub mod test { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("paths:path2/test")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/records.rs b/crates/component-macro/tests/expanded/records.rs index 452b611bc4..95a917474c 100644 --- a/crates/component-macro/tests/expanded/records.rs +++ b/crates/component-macro/tests/expanded/records.rs @@ -52,6 +52,17 @@ impl<_T: 'static> TheWorldPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `the-world`. /// @@ -146,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::records::HostWithStore, for<'a> D::Data<'a>: foo::foo::records::Host, T: 'static, { @@ -324,6 +348,11 @@ pub mod foo { 4 == < TupleTypedef2 as wasmtime::component::ComponentType >::ALIGN32 ); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn tuple_arg(&mut self, x: (char, u32)) -> (); fn tuple_result(&mut self) -> (char, u32); @@ -377,7 +406,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -736,7 +765,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/records` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/records_async.rs b/crates/component-macro/tests/expanded/records_async.rs index 9e98f20eb2..ff0a1c48c0 100644 --- a/crates/component-macro/tests/expanded/records_async.rs +++ b/crates/component-macro/tests/expanded/records_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::records::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::records::Host + Send, T: 'static + Send, { @@ -330,53 +348,113 @@ pub mod foo { 4 == < TupleTypedef2 as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn tuple_arg(&mut self, x: (char, u32)) -> (); - async fn tuple_result(&mut self) -> (char, u32); - async fn empty_arg(&mut self, x: Empty) -> (); - async fn empty_result(&mut self) -> Empty; - async fn scalar_arg(&mut self, x: Scalars) -> (); - async fn scalar_result(&mut self) -> Scalars; - async fn flags_arg(&mut self, x: ReallyFlags) -> (); - async fn flags_result(&mut self) -> ReallyFlags; - async fn aggregate_arg(&mut self, x: Aggregates) -> (); - async fn aggregate_result(&mut self) -> Aggregates; - async fn typedef_inout(&mut self, e: TupleTypedef2) -> i32; + fn tuple_arg( + &mut self, + x: (char, u32), + ) -> impl ::core::future::Future + Send; + fn tuple_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn empty_arg( + &mut self, + x: Empty, + ) -> impl ::core::future::Future + Send; + fn empty_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn scalar_arg( + &mut self, + x: Scalars, + ) -> impl ::core::future::Future + Send; + fn scalar_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn flags_arg( + &mut self, + x: ReallyFlags, + ) -> impl ::core::future::Future + Send; + fn flags_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn aggregate_arg( + &mut self, + x: Aggregates, + ) -> impl ::core::future::Future + Send; + fn aggregate_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn typedef_inout( + &mut self, + e: TupleTypedef2, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn tuple_arg(&mut self, x: (char, u32)) -> () { - Host::tuple_arg(*self, x).await + fn tuple_arg( + &mut self, + x: (char, u32), + ) -> impl ::core::future::Future + Send { + async move { Host::tuple_arg(*self, x).await } } - async fn tuple_result(&mut self) -> (char, u32) { - Host::tuple_result(*self).await + fn tuple_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::tuple_result(*self).await } } - async fn empty_arg(&mut self, x: Empty) -> () { - Host::empty_arg(*self, x).await + fn empty_arg( + &mut self, + x: Empty, + ) -> impl ::core::future::Future + Send { + async move { Host::empty_arg(*self, x).await } } - async fn empty_result(&mut self) -> Empty { - Host::empty_result(*self).await + fn empty_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::empty_result(*self).await } } - async fn scalar_arg(&mut self, x: Scalars) -> () { - Host::scalar_arg(*self, x).await + fn scalar_arg( + &mut self, + x: Scalars, + ) -> impl ::core::future::Future + Send { + async move { Host::scalar_arg(*self, x).await } } - async fn scalar_result(&mut self) -> Scalars { - Host::scalar_result(*self).await + fn scalar_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::scalar_result(*self).await } } - async fn flags_arg(&mut self, x: ReallyFlags) -> () { - Host::flags_arg(*self, x).await + fn flags_arg( + &mut self, + x: ReallyFlags, + ) -> impl ::core::future::Future + Send { + async move { Host::flags_arg(*self, x).await } } - async fn flags_result(&mut self) -> ReallyFlags { - Host::flags_result(*self).await + fn flags_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::flags_result(*self).await } } - async fn aggregate_arg(&mut self, x: Aggregates) -> () { - Host::aggregate_arg(*self, x).await + fn aggregate_arg( + &mut self, + x: Aggregates, + ) -> impl ::core::future::Future + Send { + async move { Host::aggregate_arg(*self, x).await } } - async fn aggregate_result(&mut self) -> Aggregates { - Host::aggregate_result(*self).await + fn aggregate_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::aggregate_result(*self).await } } - async fn typedef_inout(&mut self, e: TupleTypedef2) -> i32 { - Host::typedef_inout(*self, e).await + fn typedef_inout( + &mut self, + e: TupleTypedef2, + ) -> impl ::core::future::Future + Send { + async move { Host::typedef_inout(*self, e).await } } } pub fn add_to_linker( @@ -384,7 +462,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -765,7 +843,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/records` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/records_concurrent.rs b/crates/component-macro/tests/expanded/records_concurrent.rs index 8f00b4cd51..f372fca3f5 100644 --- a/crates/component-macro/tests/expanded/records_concurrent.rs +++ b/crates/component-macro/tests/expanded/records_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::records::HostConcurrent + Send, + D: foo::foo::records::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::records::Host + Send, T: 'static + Send, { @@ -330,71 +348,47 @@ pub mod foo { 4 == < TupleTypedef2 as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn tuple_arg( accessor: &wasmtime::component::Accessor, x: (char, u32), - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn tuple_result( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn empty_arg( accessor: &wasmtime::component::Accessor, x: Empty, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn empty_result( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn scalar_arg( accessor: &wasmtime::component::Accessor, x: Scalars, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn scalar_result( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn flags_arg( accessor: &wasmtime::component::Accessor, x: ReallyFlags, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn flags_result( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn aggregate_arg( accessor: &wasmtime::component::Accessor, x: Aggregates, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn aggregate_result( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn typedef_inout( accessor: &wasmtime::component::Accessor, e: TupleTypedef2, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -402,7 +396,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -415,7 +409,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::tuple_arg(accessor, arg0) + let r = ::tuple_arg(accessor, arg0) .await; Ok(r) }) @@ -426,7 +420,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::tuple_result(accessor).await; + let r = ::tuple_result(accessor).await; Ok((r,)) }) }, @@ -436,7 +430,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (Empty,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::empty_arg(accessor, arg0) + let r = ::empty_arg(accessor, arg0) .await; Ok(r) }) @@ -447,7 +441,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::empty_result(accessor).await; + let r = ::empty_result(accessor).await; Ok((r,)) }) }, @@ -460,7 +454,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::scalar_arg(accessor, arg0) + let r = ::scalar_arg(accessor, arg0) .await; Ok(r) }) @@ -471,7 +465,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::scalar_result(accessor).await; + let r = ::scalar_result(accessor).await; Ok((r,)) }) }, @@ -484,7 +478,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::flags_arg(accessor, arg0) + let r = ::flags_arg(accessor, arg0) .await; Ok(r) }) @@ -495,7 +489,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::flags_result(accessor).await; + let r = ::flags_result(accessor).await; Ok((r,)) }) }, @@ -508,7 +502,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::aggregate_arg(accessor, arg0) + let r = ::aggregate_arg(accessor, arg0) .await; Ok(r) }) @@ -519,7 +513,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::aggregate_result(accessor) + let r = ::aggregate_result(accessor) .await; Ok((r,)) }) @@ -533,7 +527,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::typedef_inout(accessor, arg0) + let r = ::typedef_inout(accessor, arg0) .await; Ok((r,)) }) diff --git a/crates/component-macro/tests/expanded/records_tracing_async.rs b/crates/component-macro/tests/expanded/records_tracing_async.rs index 9697d0bcc5..7f67a75f4f 100644 --- a/crates/component-macro/tests/expanded/records_tracing_async.rs +++ b/crates/component-macro/tests/expanded/records_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::records::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::records::Host + Send, T: 'static + Send, { @@ -330,53 +348,113 @@ pub mod foo { 4 == < TupleTypedef2 as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn tuple_arg(&mut self, x: (char, u32)) -> (); - async fn tuple_result(&mut self) -> (char, u32); - async fn empty_arg(&mut self, x: Empty) -> (); - async fn empty_result(&mut self) -> Empty; - async fn scalar_arg(&mut self, x: Scalars) -> (); - async fn scalar_result(&mut self) -> Scalars; - async fn flags_arg(&mut self, x: ReallyFlags) -> (); - async fn flags_result(&mut self) -> ReallyFlags; - async fn aggregate_arg(&mut self, x: Aggregates) -> (); - async fn aggregate_result(&mut self) -> Aggregates; - async fn typedef_inout(&mut self, e: TupleTypedef2) -> i32; + fn tuple_arg( + &mut self, + x: (char, u32), + ) -> impl ::core::future::Future + Send; + fn tuple_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn empty_arg( + &mut self, + x: Empty, + ) -> impl ::core::future::Future + Send; + fn empty_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn scalar_arg( + &mut self, + x: Scalars, + ) -> impl ::core::future::Future + Send; + fn scalar_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn flags_arg( + &mut self, + x: ReallyFlags, + ) -> impl ::core::future::Future + Send; + fn flags_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn aggregate_arg( + &mut self, + x: Aggregates, + ) -> impl ::core::future::Future + Send; + fn aggregate_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn typedef_inout( + &mut self, + e: TupleTypedef2, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn tuple_arg(&mut self, x: (char, u32)) -> () { - Host::tuple_arg(*self, x).await + fn tuple_arg( + &mut self, + x: (char, u32), + ) -> impl ::core::future::Future + Send { + async move { Host::tuple_arg(*self, x).await } } - async fn tuple_result(&mut self) -> (char, u32) { - Host::tuple_result(*self).await + fn tuple_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::tuple_result(*self).await } } - async fn empty_arg(&mut self, x: Empty) -> () { - Host::empty_arg(*self, x).await + fn empty_arg( + &mut self, + x: Empty, + ) -> impl ::core::future::Future + Send { + async move { Host::empty_arg(*self, x).await } } - async fn empty_result(&mut self) -> Empty { - Host::empty_result(*self).await + fn empty_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::empty_result(*self).await } } - async fn scalar_arg(&mut self, x: Scalars) -> () { - Host::scalar_arg(*self, x).await + fn scalar_arg( + &mut self, + x: Scalars, + ) -> impl ::core::future::Future + Send { + async move { Host::scalar_arg(*self, x).await } } - async fn scalar_result(&mut self) -> Scalars { - Host::scalar_result(*self).await + fn scalar_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::scalar_result(*self).await } } - async fn flags_arg(&mut self, x: ReallyFlags) -> () { - Host::flags_arg(*self, x).await + fn flags_arg( + &mut self, + x: ReallyFlags, + ) -> impl ::core::future::Future + Send { + async move { Host::flags_arg(*self, x).await } } - async fn flags_result(&mut self) -> ReallyFlags { - Host::flags_result(*self).await + fn flags_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::flags_result(*self).await } } - async fn aggregate_arg(&mut self, x: Aggregates) -> () { - Host::aggregate_arg(*self, x).await + fn aggregate_arg( + &mut self, + x: Aggregates, + ) -> impl ::core::future::Future + Send { + async move { Host::aggregate_arg(*self, x).await } } - async fn aggregate_result(&mut self) -> Aggregates { - Host::aggregate_result(*self).await + fn aggregate_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::aggregate_result(*self).await } } - async fn typedef_inout(&mut self, e: TupleTypedef2) -> i32 { - Host::typedef_inout(*self, e).await + fn typedef_inout( + &mut self, + e: TupleTypedef2, + ) -> impl ::core::future::Future + Send { + async move { Host::typedef_inout(*self, e).await } } } pub fn add_to_linker( @@ -384,7 +462,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -926,7 +1004,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/records` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/rename.rs b/crates/component-macro/tests/expanded/rename.rs index 6bc86474a6..7345a2013f 100644 --- a/crates/component-macro/tests/expanded/rename.rs +++ b/crates/component-macro/tests/expanded/rename.rs @@ -52,6 +52,17 @@ impl<_T: 'static> NeptunePre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> NeptunePre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `neptune`. /// @@ -138,12 +149,25 @@ const _: () = { let indices = NeptuneIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`NeptunePre::new`] and + /// [`NeptunePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + NeptunePre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::green::HostWithStore + foo::foo::red::HostWithStore, for<'a> D::Data<'a>: foo::foo::green::Host + foo::foo::red::Host, T: 'static, { @@ -164,6 +188,11 @@ pub mod foo { assert!(4 == < Thing as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Thing as wasmtime::component::ComponentType >::ALIGN32); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host {} impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( @@ -171,7 +200,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -188,6 +217,11 @@ pub mod foo { assert!(4 == < Thing as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Thing as wasmtime::component::ComponentType >::ALIGN32); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn foo(&mut self) -> Thing; } @@ -201,7 +235,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { diff --git a/crates/component-macro/tests/expanded/rename_async.rs b/crates/component-macro/tests/expanded/rename_async.rs index 6b206d0146..c17861447a 100644 --- a/crates/component-macro/tests/expanded/rename_async.rs +++ b/crates/component-macro/tests/expanded/rename_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> NeptunePre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> NeptunePre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct NeptuneIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Neptune::instantiate_async`] which only needs a +/// [`Neptune::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`NeptunePre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`NeptunePre::instantiate_async`] to +/// method then uses [`NeptunePre::instantiate`] to /// create a [`Neptune`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Neptune { /// Convenience wrapper around [`NeptunePre::new`] and - /// [`NeptunePre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`NeptunePre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - NeptunePre::new(pre)?.instantiate_async(store).await + NeptunePre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`NeptuneIndices::new`] and /// [`NeptuneIndices::load`]. @@ -144,12 +149,25 @@ const _: () = { let indices = NeptuneIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`NeptunePre::new`] and + /// [`NeptunePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + NeptunePre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::green::HostWithStore + foo::foo::red::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::green::Host + foo::foo::red::Host + Send, T: 'static + Send, { @@ -170,17 +188,21 @@ pub mod foo { assert!(4 == < Thing as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Thing as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/green")?; Ok(()) @@ -195,13 +217,17 @@ pub mod foo { assert!(4 == < Thing as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Thing as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn foo(&mut self) -> Thing; + fn foo(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn foo(&mut self) -> Thing { - Host::foo(*self).await + fn foo(&mut self) -> impl ::core::future::Future + Send { + async move { Host::foo(*self).await } } } pub fn add_to_linker( @@ -209,7 +235,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/rename_concurrent.rs b/crates/component-macro/tests/expanded/rename_concurrent.rs index 971f1d88a3..976fb32856 100644 --- a/crates/component-macro/tests/expanded/rename_concurrent.rs +++ b/crates/component-macro/tests/expanded/rename_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> NeptunePre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> NeptunePre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct NeptuneIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Neptune::instantiate_async`] which only needs a +/// [`Neptune::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`NeptunePre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`NeptunePre::instantiate_async`] to +/// method then uses [`NeptunePre::instantiate`] to /// create a [`Neptune`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Neptune { /// Convenience wrapper around [`NeptunePre::new`] and - /// [`NeptunePre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`NeptunePre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - NeptunePre::new(pre)?.instantiate_async(store).await + NeptunePre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`NeptuneIndices::new`] and /// [`NeptuneIndices::load`]. @@ -144,12 +149,25 @@ const _: () = { let indices = NeptuneIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`NeptunePre::new`] and + /// [`NeptunePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + NeptunePre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::red::HostConcurrent + Send, + D: foo::foo::green::HostWithStore + foo::foo::red::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::green::Host + foo::foo::red::Host + Send, T: 'static + Send, { @@ -170,17 +188,21 @@ pub mod foo { assert!(4 == < Thing as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Thing as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/green")?; Ok(()) @@ -195,15 +217,11 @@ pub mod foo { assert!(4 == < Thing as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Thing as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn foo( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -211,7 +229,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -221,7 +239,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::foo(accessor).await; + let r = ::foo(accessor).await; Ok((r,)) }) }, diff --git a/crates/component-macro/tests/expanded/rename_tracing_async.rs b/crates/component-macro/tests/expanded/rename_tracing_async.rs index e3f3084917..258c98c8c1 100644 --- a/crates/component-macro/tests/expanded/rename_tracing_async.rs +++ b/crates/component-macro/tests/expanded/rename_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> NeptunePre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> NeptunePre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct NeptuneIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Neptune::instantiate_async`] which only needs a +/// [`Neptune::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`NeptunePre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`NeptunePre::instantiate_async`] to +/// method then uses [`NeptunePre::instantiate`] to /// create a [`Neptune`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Neptune { /// Convenience wrapper around [`NeptunePre::new`] and - /// [`NeptunePre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`NeptunePre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - NeptunePre::new(pre)?.instantiate_async(store).await + NeptunePre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`NeptuneIndices::new`] and /// [`NeptuneIndices::load`]. @@ -144,12 +149,25 @@ const _: () = { let indices = NeptuneIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`NeptunePre::new`] and + /// [`NeptunePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + NeptunePre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::green::HostWithStore + foo::foo::red::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::green::Host + foo::foo::red::Host + Send, T: 'static + Send, { @@ -170,17 +188,21 @@ pub mod foo { assert!(4 == < Thing as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Thing as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/green")?; Ok(()) @@ -195,13 +217,17 @@ pub mod foo { assert!(4 == < Thing as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Thing as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn foo(&mut self) -> Thing; + fn foo(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn foo(&mut self) -> Thing { - Host::foo(*self).await + fn foo(&mut self) -> impl ::core::future::Future + Send { + async move { Host::foo(*self).await } } } pub fn add_to_linker( @@ -209,7 +235,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/resources-export.rs b/crates/component-macro/tests/expanded/resources-export.rs index 923bff1d8b..ce51dd2929 100644 --- a/crates/component-macro/tests/expanded/resources-export.rs +++ b/crates/component-macro/tests/expanded/resources-export.rs @@ -52,6 +52,17 @@ impl<_T: 'static> WPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> WPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `w`. /// @@ -174,12 +185,25 @@ const _: () = { let indices = WIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`WPre::new`] and + /// [`WPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + WPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::transitive_import::HostWithStore, for<'a> D::Data<'a>: foo::foo::transitive_import::Host, T: 'static, { @@ -213,6 +237,11 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum Y {} + pub trait HostYWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostYWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait HostY { fn drop( &mut self, @@ -227,6 +256,11 @@ pub mod foo { HostY::drop(*self, rep) } } + pub trait HostWithStore: wasmtime::component::HasData + HostYWithStore {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostYWithStore, + {} pub trait Host: HostY {} impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( @@ -234,7 +268,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { diff --git a/crates/component-macro/tests/expanded/resources-export_async.rs b/crates/component-macro/tests/expanded/resources-export_async.rs index 3e03748096..db25ccc4b7 100644 --- a/crates/component-macro/tests/expanded/resources-export_async.rs +++ b/crates/component-macro/tests/expanded/resources-export_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> WPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> WPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -76,13 +84,13 @@ pub struct WIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`W::instantiate_async`] which only needs a +/// [`W::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`WPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`WPre::instantiate_async`] to +/// method then uses [`WPre::instantiate`] to /// create a [`W`]. /// /// * If you've instantiated the instance yourself already @@ -159,17 +167,14 @@ const _: () = { } impl W { /// Convenience wrapper around [`WPre::new`] and - /// [`WPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`WPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - WPre::new(pre)?.instantiate_async(store).await + WPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`WIndices::new`] and /// [`WIndices::load`]. @@ -180,12 +185,25 @@ const _: () = { let indices = WIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`WPre::new`] and + /// [`WPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + WPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::transitive_import::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::transitive_import::Host + Send, T: 'static + Send, { @@ -219,12 +237,16 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum Y {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostY: Send { - async fn drop( + pub trait HostYWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostYWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait HostY { + fn drop( &mut self, rep: wasmtime::component::Resource, - ) -> wasmtime::Result<()>; + ) -> impl ::core::future::Future> + Send; } impl<_T: HostY + ?Sized + Send> HostY for &mut _T { async fn drop( @@ -234,15 +256,19 @@ pub mod foo { HostY::drop(*self, rep).await } } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostY {} + pub trait HostWithStore: wasmtime::component::HasData + HostYWithStore + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostYWithStore + Send, + {} + pub trait Host: HostY + Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/resources-export_concurrent.rs b/crates/component-macro/tests/expanded/resources-export_concurrent.rs index 85292a832f..4f1121af72 100644 --- a/crates/component-macro/tests/expanded/resources-export_concurrent.rs +++ b/crates/component-macro/tests/expanded/resources-export_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> WPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> WPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -76,13 +84,13 @@ pub struct WIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`W::instantiate_async`] which only needs a +/// [`W::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`WPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`WPre::instantiate_async`] to +/// method then uses [`WPre::instantiate`] to /// create a [`W`]. /// /// * If you've instantiated the instance yourself already @@ -159,17 +167,14 @@ const _: () = { } impl W { /// Convenience wrapper around [`WPre::new`] and - /// [`WPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`WPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - WPre::new(pre)?.instantiate_async(store).await + WPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`WIndices::new`] and /// [`WIndices::load`]. @@ -180,12 +185,25 @@ const _: () = { let indices = WIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`WPre::new`] and + /// [`WPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + WPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::transitive_import::HostConcurrent + Send, + D: foo::foo::transitive_import::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::transitive_import::Host + Send, T: 'static + Send, { @@ -219,8 +237,7 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum Y {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostYConcurrent: wasmtime::component::HasData + Send { + pub trait HostYWithStore: wasmtime::component::HasData { fn drop( accessor: &wasmtime::component::Accessor, rep: wasmtime::component::Resource, @@ -228,20 +245,21 @@ pub mod foo { where Self: Sized; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostY: Send {} + pub trait HostY {} impl<_T: HostY + ?Sized + Send> HostY for &mut _T {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send + HostYConcurrent {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostY {} + pub trait HostWithStore: wasmtime::component::HasData + HostYWithStore + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostYWithStore + Send, + {} + pub trait Host: HostY + Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -252,7 +270,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, rep| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - HostYConcurrent::drop( + HostYWithStore::drop( accessor, wasmtime::component::Resource::new_own(rep), ) diff --git a/crates/component-macro/tests/expanded/resources-export_tracing_async.rs b/crates/component-macro/tests/expanded/resources-export_tracing_async.rs index a4d0569a8f..2bdaf403ca 100644 --- a/crates/component-macro/tests/expanded/resources-export_tracing_async.rs +++ b/crates/component-macro/tests/expanded/resources-export_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> WPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> WPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -76,13 +84,13 @@ pub struct WIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`W::instantiate_async`] which only needs a +/// [`W::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`WPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`WPre::instantiate_async`] to +/// method then uses [`WPre::instantiate`] to /// create a [`W`]. /// /// * If you've instantiated the instance yourself already @@ -159,17 +167,14 @@ const _: () = { } impl W { /// Convenience wrapper around [`WPre::new`] and - /// [`WPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`WPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - WPre::new(pre)?.instantiate_async(store).await + WPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`WIndices::new`] and /// [`WIndices::load`]. @@ -180,12 +185,25 @@ const _: () = { let indices = WIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`WPre::new`] and + /// [`WPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + WPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::transitive_import::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::transitive_import::Host + Send, T: 'static + Send, { @@ -219,12 +237,16 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum Y {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostY: Send { - async fn drop( + pub trait HostYWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostYWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait HostY { + fn drop( &mut self, rep: wasmtime::component::Resource, - ) -> wasmtime::Result<()>; + ) -> impl ::core::future::Future> + Send; } impl<_T: HostY + ?Sized + Send> HostY for &mut _T { async fn drop( @@ -234,15 +256,19 @@ pub mod foo { HostY::drop(*self, rep).await } } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostY {} + pub trait HostWithStore: wasmtime::component::HasData + HostYWithStore + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostYWithStore + Send, + {} + pub trait Host: HostY + Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/resources-import.rs b/crates/component-macro/tests/expanded/resources-import.rs index e881f9880e..db190c95c6 100644 --- a/crates/component-macro/tests/expanded/resources-import.rs +++ b/crates/component-macro/tests/expanded/resources-import.rs @@ -1,4 +1,9 @@ pub enum WorldResource {} +pub trait HostWorldResourceWithStore: wasmtime::component::HasData {} +impl<_T: ?Sized> HostWorldResourceWithStore for _T +where + _T: wasmtime::component::HasData, +{} pub trait HostWorldResource { fn new(&mut self) -> wasmtime::component::Resource; fn foo(&mut self, self_: wasmtime::component::Resource) -> (); @@ -79,6 +84,17 @@ impl<_T: 'static> TheWorldPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `the-world`. /// @@ -120,6 +136,11 @@ pub struct TheWorld { interface1: exports::foo::foo::uses_resource_transitively::Guest, some_world_func2: wasmtime::component::Func, } +pub trait TheWorldImportsWithStore: wasmtime::component::HasData + HostWorldResourceWithStore {} +impl<_T: ?Sized> TheWorldImportsWithStore for _T +where + _T: wasmtime::component::HasData + HostWorldResourceWithStore, +{} pub trait TheWorldImports: HostWorldResource { fn some_world_func(&mut self) -> wasmtime::component::Resource; } @@ -222,12 +243,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker_imports( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: TheWorldImportsWithStore, for<'a> D::Data<'a>: TheWorldImports, T: 'static, { @@ -289,7 +323,13 @@ const _: () = { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::resources::HostWithStore + + foo::foo::long_use_chain1::HostWithStore + + foo::foo::long_use_chain2::HostWithStore + + foo::foo::long_use_chain3::HostWithStore + + foo::foo::long_use_chain4::HostWithStore + + foo::foo::transitive_interface_with_resource::HostWithStore + + TheWorldImportsWithStore, for<'a> D::Data< 'a, >: foo::foo::resources::Host + foo::foo::long_use_chain1::Host @@ -338,6 +378,11 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum Bar {} + pub trait HostBarWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostBarWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait HostBar { fn new(&mut self) -> wasmtime::component::Resource; fn static_a(&mut self) -> u32; @@ -422,6 +467,11 @@ pub mod foo { 4 == < SomeHandle as wasmtime::component::ComponentType >::ALIGN32 ); }; + pub trait HostWithStore: wasmtime::component::HasData + HostBarWithStore {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostBarWithStore, + {} pub trait Host: HostBar { fn bar_own_arg(&mut self, x: wasmtime::component::Resource) -> (); fn bar_borrow_arg( @@ -585,7 +635,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -838,6 +888,11 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum A {} + pub trait HostAWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostAWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait HostA { fn drop( &mut self, @@ -852,6 +907,11 @@ pub mod foo { HostA::drop(*self, rep) } } + pub trait HostWithStore: wasmtime::component::HasData + HostAWithStore {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostAWithStore, + {} pub trait Host: HostA {} impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( @@ -859,7 +919,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -882,6 +942,11 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub type A = super::super::super::foo::foo::long_use_chain1::A; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host {} impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( @@ -889,7 +954,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -902,6 +967,11 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub type A = super::super::super::foo::foo::long_use_chain2::A; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host {} impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( @@ -909,7 +979,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -922,6 +992,11 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub type A = super::super::super::foo::foo::long_use_chain3::A; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn foo(&mut self) -> wasmtime::component::Resource; } @@ -935,7 +1010,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -956,6 +1031,11 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum Foo {} + pub trait HostFooWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostFooWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait HostFoo { fn drop( &mut self, @@ -970,6 +1050,11 @@ pub mod foo { HostFoo::drop(*self, rep) } } + pub trait HostWithStore: wasmtime::component::HasData + HostFooWithStore {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostFooWithStore, + {} pub trait Host: HostFoo {} impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( @@ -977,7 +1062,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -1038,7 +1123,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/uses-resource-transitively` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/resources-import_async.rs b/crates/component-macro/tests/expanded/resources-import_async.rs index d3d9439ccb..38a939c50c 100644 --- a/crates/component-macro/tests/expanded/resources-import_async.rs +++ b/crates/component-macro/tests/expanded/resources-import_async.rs @@ -1,23 +1,41 @@ pub enum WorldResource {} -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] +pub trait HostWorldResourceWithStore: wasmtime::component::HasData + Send {} +impl<_T: ?Sized> HostWorldResourceWithStore for _T +where + _T: wasmtime::component::HasData + Send, +{} pub trait HostWorldResource: Send { - async fn new(&mut self) -> wasmtime::component::Resource; - async fn foo(&mut self, self_: wasmtime::component::Resource) -> (); - async fn static_foo(&mut self) -> (); - async fn drop( + fn new( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send; + fn foo( + &mut self, + self_: wasmtime::component::Resource, + ) -> impl ::core::future::Future + Send; + fn static_foo(&mut self) -> impl ::core::future::Future + Send; + fn drop( &mut self, rep: wasmtime::component::Resource, - ) -> wasmtime::Result<()>; + ) -> impl ::core::future::Future> + Send; } impl<_T: HostWorldResource + ?Sized + Send> HostWorldResource for &mut _T { - async fn new(&mut self) -> wasmtime::component::Resource { - HostWorldResource::new(*self).await + fn new( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send { + async move { HostWorldResource::new(*self).await } } - async fn foo(&mut self, self_: wasmtime::component::Resource) -> () { - HostWorldResource::foo(*self, self_).await + fn foo( + &mut self, + self_: wasmtime::component::Resource, + ) -> impl ::core::future::Future + Send { + async move { HostWorldResource::foo(*self, self_).await } } - async fn static_foo(&mut self) -> () { - HostWorldResource::static_foo(*self).await + fn static_foo(&mut self) -> impl ::core::future::Future + Send { + async move { HostWorldResource::static_foo(*self).await } } async fn drop( &mut self, @@ -71,13 +89,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -102,13 +128,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -124,13 +150,25 @@ pub struct TheWorld { interface1: exports::foo::foo::uses_resource_transitively::Guest, some_world_func2: wasmtime::component::Func, } -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] -pub trait TheWorldImports: Send + HostWorldResource { - async fn some_world_func(&mut self) -> wasmtime::component::Resource; +pub trait TheWorldImportsWithStore: wasmtime::component::HasData + HostWorldResourceWithStore + Send {} +impl<_T: ?Sized> TheWorldImportsWithStore for _T +where + _T: wasmtime::component::HasData + HostWorldResourceWithStore + Send, +{} +pub trait TheWorldImports: HostWorldResource + Send { + fn some_world_func( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send; } impl<_T: TheWorldImports + ?Sized + Send> TheWorldImports for &mut _T { - async fn some_world_func(&mut self) -> wasmtime::component::Resource { - TheWorldImports::some_world_func(*self).await + fn some_world_func( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send { + async move { TheWorldImports::some_world_func(*self).await } } } const _: () = { @@ -209,17 +247,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -230,12 +265,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker_imports( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: TheWorldImportsWithStore, for<'a> D::Data<'a>: TheWorldImports, T: 'static + Send, { @@ -308,7 +356,13 @@ const _: () = { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::resources::HostWithStore + + foo::foo::long_use_chain1::HostWithStore + + foo::foo::long_use_chain2::HostWithStore + + foo::foo::long_use_chain3::HostWithStore + + foo::foo::long_use_chain4::HostWithStore + + foo::foo::transitive_interface_with_resource::HostWithStore + + TheWorldImportsWithStore + Send, for<'a> D::Data< 'a, >: foo::foo::resources::Host + foo::foo::long_use_chain1::Host @@ -361,31 +415,47 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum Bar {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostBarWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostBarWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait HostBar: Send { - async fn new(&mut self) -> wasmtime::component::Resource; - async fn static_a(&mut self) -> u32; - async fn method_a( + fn new( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send; + fn static_a( + &mut self, + ) -> impl ::core::future::Future + Send; + fn method_a( &mut self, self_: wasmtime::component::Resource, - ) -> u32; - async fn drop( + ) -> impl ::core::future::Future + Send; + fn drop( &mut self, rep: wasmtime::component::Resource, - ) -> wasmtime::Result<()>; + ) -> impl ::core::future::Future> + Send; } impl<_T: HostBar + ?Sized + Send> HostBar for &mut _T { - async fn new(&mut self) -> wasmtime::component::Resource { - HostBar::new(*self).await + fn new( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send { + async move { HostBar::new(*self).await } } - async fn static_a(&mut self) -> u32 { - HostBar::static_a(*self).await + fn static_a( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { HostBar::static_a(*self).await } } - async fn method_a( + fn method_a( &mut self, self_: wasmtime::component::Resource, - ) -> u32 { - HostBar::method_a(*self, self_).await + ) -> impl ::core::future::Future + Send { + async move { HostBar::method_a(*self, self_).await } } async fn drop( &mut self, @@ -449,173 +519,223 @@ pub mod foo { 4 == < SomeHandle as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostBar { - async fn bar_own_arg( + pub trait HostWithStore: wasmtime::component::HasData + HostBarWithStore + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostBarWithStore + Send, + {} + pub trait Host: HostBar + Send { + fn bar_own_arg( &mut self, x: wasmtime::component::Resource, - ) -> (); - async fn bar_borrow_arg( + ) -> impl ::core::future::Future + Send; + fn bar_borrow_arg( &mut self, x: wasmtime::component::Resource, - ) -> (); - async fn bar_result(&mut self) -> wasmtime::component::Resource; - async fn tuple_own_arg( + ) -> impl ::core::future::Future + Send; + fn bar_result( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send; + fn tuple_own_arg( &mut self, x: (wasmtime::component::Resource, u32), - ) -> (); - async fn tuple_borrow_arg( + ) -> impl ::core::future::Future + Send; + fn tuple_borrow_arg( &mut self, x: (wasmtime::component::Resource, u32), - ) -> (); - async fn tuple_result( + ) -> impl ::core::future::Future + Send; + fn tuple_result( &mut self, - ) -> (wasmtime::component::Resource, u32); - async fn option_own_arg( + ) -> impl ::core::future::Future< + Output = (wasmtime::component::Resource, u32), + > + Send; + fn option_own_arg( &mut self, x: Option>, - ) -> (); - async fn option_borrow_arg( + ) -> impl ::core::future::Future + Send; + fn option_borrow_arg( &mut self, x: Option>, - ) -> (); - async fn option_result( + ) -> impl ::core::future::Future + Send; + fn option_result( &mut self, - ) -> Option>; - async fn result_own_arg( + ) -> impl ::core::future::Future< + Output = Option>, + > + Send; + fn result_own_arg( &mut self, x: Result, ()>, - ) -> (); - async fn result_borrow_arg( + ) -> impl ::core::future::Future + Send; + fn result_borrow_arg( &mut self, x: Result, ()>, - ) -> (); - async fn result_result( + ) -> impl ::core::future::Future + Send; + fn result_result( &mut self, - ) -> Result, ()>; - async fn list_own_arg( + ) -> impl ::core::future::Future< + Output = Result, ()>, + > + Send; + fn list_own_arg( &mut self, x: wasmtime::component::__internal::Vec< wasmtime::component::Resource, >, - ) -> (); - async fn list_borrow_arg( + ) -> impl ::core::future::Future + Send; + fn list_borrow_arg( &mut self, x: wasmtime::component::__internal::Vec< wasmtime::component::Resource, >, - ) -> (); - async fn list_result( + ) -> impl ::core::future::Future + Send; + fn list_result( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + wasmtime::component::Resource, + >, + > + Send; + fn record_own_arg( + &mut self, + x: NestedOwn, + ) -> impl ::core::future::Future + Send; + fn record_borrow_arg( + &mut self, + x: NestedBorrow, + ) -> impl ::core::future::Future + Send; + fn record_result( &mut self, - ) -> wasmtime::component::__internal::Vec< - wasmtime::component::Resource, - >; - async fn record_own_arg(&mut self, x: NestedOwn) -> (); - async fn record_borrow_arg(&mut self, x: NestedBorrow) -> (); - async fn record_result(&mut self) -> NestedOwn; - async fn func_with_handle_typedef(&mut self, x: SomeHandle) -> (); + ) -> impl ::core::future::Future + Send; + fn func_with_handle_typedef( + &mut self, + x: SomeHandle, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn bar_own_arg( + fn bar_own_arg( &mut self, x: wasmtime::component::Resource, - ) -> () { - Host::bar_own_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::bar_own_arg(*self, x).await } } - async fn bar_borrow_arg( + fn bar_borrow_arg( &mut self, x: wasmtime::component::Resource, - ) -> () { - Host::bar_borrow_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::bar_borrow_arg(*self, x).await } } - async fn bar_result(&mut self) -> wasmtime::component::Resource { - Host::bar_result(*self).await + fn bar_result( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send { + async move { Host::bar_result(*self).await } } - async fn tuple_own_arg( + fn tuple_own_arg( &mut self, x: (wasmtime::component::Resource, u32), - ) -> () { - Host::tuple_own_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::tuple_own_arg(*self, x).await } } - async fn tuple_borrow_arg( + fn tuple_borrow_arg( &mut self, x: (wasmtime::component::Resource, u32), - ) -> () { - Host::tuple_borrow_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::tuple_borrow_arg(*self, x).await } } - async fn tuple_result( + fn tuple_result( &mut self, - ) -> (wasmtime::component::Resource, u32) { - Host::tuple_result(*self).await + ) -> impl ::core::future::Future< + Output = (wasmtime::component::Resource, u32), + > + Send { + async move { Host::tuple_result(*self).await } } - async fn option_own_arg( + fn option_own_arg( &mut self, x: Option>, - ) -> () { - Host::option_own_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::option_own_arg(*self, x).await } } - async fn option_borrow_arg( + fn option_borrow_arg( &mut self, x: Option>, - ) -> () { - Host::option_borrow_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::option_borrow_arg(*self, x).await } } - async fn option_result( + fn option_result( &mut self, - ) -> Option> { - Host::option_result(*self).await + ) -> impl ::core::future::Future< + Output = Option>, + > + Send { + async move { Host::option_result(*self).await } } - async fn result_own_arg( + fn result_own_arg( &mut self, x: Result, ()>, - ) -> () { - Host::result_own_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::result_own_arg(*self, x).await } } - async fn result_borrow_arg( + fn result_borrow_arg( &mut self, x: Result, ()>, - ) -> () { - Host::result_borrow_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::result_borrow_arg(*self, x).await } } - async fn result_result( + fn result_result( &mut self, - ) -> Result, ()> { - Host::result_result(*self).await + ) -> impl ::core::future::Future< + Output = Result, ()>, + > + Send { + async move { Host::result_result(*self).await } } - async fn list_own_arg( + fn list_own_arg( &mut self, x: wasmtime::component::__internal::Vec< wasmtime::component::Resource, >, - ) -> () { - Host::list_own_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_own_arg(*self, x).await } } - async fn list_borrow_arg( + fn list_borrow_arg( &mut self, x: wasmtime::component::__internal::Vec< wasmtime::component::Resource, >, - ) -> () { - Host::list_borrow_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_borrow_arg(*self, x).await } } - async fn list_result( + fn list_result( &mut self, - ) -> wasmtime::component::__internal::Vec< - wasmtime::component::Resource, - > { - Host::list_result(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + wasmtime::component::Resource, + >, + > + Send { + async move { Host::list_result(*self).await } } - async fn record_own_arg(&mut self, x: NestedOwn) -> () { - Host::record_own_arg(*self, x).await + fn record_own_arg( + &mut self, + x: NestedOwn, + ) -> impl ::core::future::Future + Send { + async move { Host::record_own_arg(*self, x).await } } - async fn record_borrow_arg(&mut self, x: NestedBorrow) -> () { - Host::record_borrow_arg(*self, x).await + fn record_borrow_arg( + &mut self, + x: NestedBorrow, + ) -> impl ::core::future::Future + Send { + async move { Host::record_borrow_arg(*self, x).await } } - async fn record_result(&mut self) -> NestedOwn { - Host::record_result(*self).await + fn record_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::record_result(*self).await } } - async fn func_with_handle_typedef(&mut self, x: SomeHandle) -> () { - Host::func_with_handle_typedef(*self, x).await + fn func_with_handle_typedef( + &mut self, + x: SomeHandle, + ) -> impl ::core::future::Future + Send { + async move { Host::func_with_handle_typedef(*self, x).await } } } pub fn add_to_linker( @@ -623,7 +743,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -923,12 +1043,16 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum A {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostA: Send { - async fn drop( + pub trait HostAWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostAWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait HostA { + fn drop( &mut self, rep: wasmtime::component::Resource, - ) -> wasmtime::Result<()>; + ) -> impl ::core::future::Future> + Send; } impl<_T: HostA + ?Sized + Send> HostA for &mut _T { async fn drop( @@ -938,15 +1062,19 @@ pub mod foo { HostA::drop(*self, rep).await } } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostA {} + pub trait HostWithStore: wasmtime::component::HasData + HostAWithStore + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostAWithStore + Send, + {} + pub trait Host: HostA + Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -972,17 +1100,21 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub type A = super::super::super::foo::foo::long_use_chain1::A; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/long-use-chain2")?; Ok(()) @@ -993,17 +1125,21 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub type A = super::super::super::foo::foo::long_use_chain2::A; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/long-use-chain3")?; Ok(()) @@ -1014,13 +1150,25 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub type A = super::super::super::foo::foo::long_use_chain3::A; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn foo(&mut self) -> wasmtime::component::Resource; + fn foo( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn foo(&mut self) -> wasmtime::component::Resource { - Host::foo(*self).await + fn foo( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send { + async move { Host::foo(*self).await } } } pub fn add_to_linker( @@ -1028,7 +1176,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -1051,12 +1199,16 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum Foo {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostFoo: Send { - async fn drop( + pub trait HostFooWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostFooWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait HostFoo { + fn drop( &mut self, rep: wasmtime::component::Resource, - ) -> wasmtime::Result<()>; + ) -> impl ::core::future::Future> + Send; } impl<_T: HostFoo + ?Sized + Send> HostFoo for &mut _T { async fn drop( @@ -1066,15 +1218,19 @@ pub mod foo { HostFoo::drop(*self, rep).await } } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostFoo {} + pub trait HostWithStore: wasmtime::component::HasData + HostFooWithStore + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostFooWithStore + Send, + {} + pub trait Host: HostFoo + Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -1138,7 +1294,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/uses-resource-transitively` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/resources-import_concurrent.rs b/crates/component-macro/tests/expanded/resources-import_concurrent.rs index c791182150..9567377ad2 100644 --- a/crates/component-macro/tests/expanded/resources-import_concurrent.rs +++ b/crates/component-macro/tests/expanded/resources-import_concurrent.rs @@ -1,32 +1,24 @@ pub enum WorldResource {} -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] -pub trait HostWorldResourceConcurrent: wasmtime::component::HasData + Send { +pub trait HostWorldResourceWithStore: wasmtime::component::HasData + Send { + fn drop( + accessor: &wasmtime::component::Accessor, + rep: wasmtime::component::Resource, + ) -> impl ::core::future::Future> + Send + where + Self: Sized; fn new( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::Resource, - > + Send - where - Self: Sized; + > + Send; fn foo( accessor: &wasmtime::component::Accessor, self_: wasmtime::component::Resource, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn static_foo( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; - fn drop( - accessor: &wasmtime::component::Accessor, - rep: wasmtime::component::Resource, - ) -> impl ::core::future::Future> + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait HostWorldResource: Send {} impl<_T: HostWorldResource + ?Sized + Send> HostWorldResource for &mut _T {} /// Auto-generated bindings for a pre-instantiated version of a @@ -74,13 +66,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -105,13 +105,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -127,18 +127,14 @@ pub struct TheWorld { interface1: exports::foo::foo::uses_resource_transitively::Guest, some_world_func2: wasmtime::component::Func, } -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] -pub trait TheWorldImportsConcurrent: wasmtime::component::HasData + Send + HostWorldResourceConcurrent { +pub trait TheWorldImportsWithStore: wasmtime::component::HasData + HostWorldResourceWithStore + Send { fn some_world_func( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::Resource, - > + Send - where - Self: Sized; + > + Send; } -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] -pub trait TheWorldImports: Send + HostWorldResource {} +pub trait TheWorldImports: HostWorldResource + Send {} impl<_T: TheWorldImports + ?Sized + Send> TheWorldImports for &mut _T {} const _: () = { #[allow(unused_imports)] @@ -216,17 +212,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -237,12 +230,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker_imports( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: TheWorldImportsConcurrent, + D: TheWorldImportsWithStore, for<'a> D::Data<'a>: TheWorldImports, T: 'static + Send, { @@ -254,7 +260,7 @@ const _: () = { move |caller: &wasmtime::component::Accessor, rep| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - HostWorldResourceConcurrent::drop( + HostWorldResourceWithStore::drop( accessor, wasmtime::component::Resource::new_own(rep), ) @@ -268,7 +274,7 @@ const _: () = { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::some_world_func( + let r = ::some_world_func( accessor, ) .await; @@ -282,7 +288,7 @@ const _: () = { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::new(accessor) + let r = ::new(accessor) .await; Ok((r,)) }) @@ -297,7 +303,7 @@ const _: () = { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::foo( + let r = ::foo( accessor, arg0, ) @@ -312,7 +318,7 @@ const _: () = { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::static_foo( + let r = ::static_foo( accessor, ) .await; @@ -327,11 +333,13 @@ const _: () = { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::resources::HostConcurrent - + foo::foo::long_use_chain1::HostConcurrent - + foo::foo::long_use_chain4::HostConcurrent - + foo::foo::transitive_interface_with_resource::HostConcurrent - + TheWorldImportsConcurrent + Send, + D: foo::foo::resources::HostWithStore + + foo::foo::long_use_chain1::HostWithStore + + foo::foo::long_use_chain2::HostWithStore + + foo::foo::long_use_chain3::HostWithStore + + foo::foo::long_use_chain4::HostWithStore + + foo::foo::transitive_interface_with_resource::HostWithStore + + TheWorldImportsWithStore + Send, for<'a> D::Data< 'a, >: foo::foo::resources::Host + foo::foo::long_use_chain1::Host @@ -384,34 +392,26 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum Bar {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostBarConcurrent: wasmtime::component::HasData + Send { + pub trait HostBarWithStore: wasmtime::component::HasData + Send { + fn drop( + accessor: &wasmtime::component::Accessor, + rep: wasmtime::component::Resource, + ) -> impl ::core::future::Future> + Send + where + Self: Sized; fn new( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::Resource, - > + Send - where - Self: Sized; + > + Send; fn static_a( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn method_a( accessor: &wasmtime::component::Accessor, self_: wasmtime::component::Resource, - ) -> impl ::core::future::Future + Send - where - Self: Sized; - fn drop( - accessor: &wasmtime::component::Accessor, - rep: wasmtime::component::Resource, - ) -> impl ::core::future::Future> + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait HostBar: Send {} impl<_T: HostBar + ?Sized + Send> HostBar for &mut _T {} #[derive(wasmtime::component::ComponentType)] @@ -469,142 +469,102 @@ pub mod foo { 4 == < SomeHandle as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send + HostBarConcurrent { + pub trait HostWithStore: wasmtime::component::HasData + HostBarWithStore + Send { fn bar_own_arg( accessor: &wasmtime::component::Accessor, x: wasmtime::component::Resource, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn bar_borrow_arg( accessor: &wasmtime::component::Accessor, x: wasmtime::component::Resource, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn bar_result( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::Resource, - > + Send - where - Self: Sized; + > + Send; fn tuple_own_arg( accessor: &wasmtime::component::Accessor, x: (wasmtime::component::Resource, u32), - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn tuple_borrow_arg( accessor: &wasmtime::component::Accessor, x: (wasmtime::component::Resource, u32), - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn tuple_result( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = (wasmtime::component::Resource, u32), - > + Send - where - Self: Sized; + > + Send; fn option_own_arg( accessor: &wasmtime::component::Accessor, x: Option>, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn option_borrow_arg( accessor: &wasmtime::component::Accessor, x: Option>, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn option_result( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = Option>, - > + Send - where - Self: Sized; + > + Send; fn result_own_arg( accessor: &wasmtime::component::Accessor, x: Result, ()>, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn result_borrow_arg( accessor: &wasmtime::component::Accessor, x: Result, ()>, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn result_result( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = Result, ()>, - > + Send - where - Self: Sized; + > + Send; fn list_own_arg( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec< wasmtime::component::Resource, >, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn list_borrow_arg( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::Vec< wasmtime::component::Resource, >, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn list_result( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::Vec< wasmtime::component::Resource, >, - > + Send - where - Self: Sized; + > + Send; fn record_own_arg( accessor: &wasmtime::component::Accessor, x: NestedOwn, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn record_borrow_arg( accessor: &wasmtime::component::Accessor, x: NestedBorrow, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn record_result( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn func_with_handle_typedef( accessor: &wasmtime::component::Accessor, x: SomeHandle, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostBar {} + pub trait Host: HostBar + Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -615,7 +575,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, rep| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - HostBarConcurrent::drop( + HostBarWithStore::drop( accessor, wasmtime::component::Resource::new_own(rep), ) @@ -628,7 +588,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::new(accessor).await; + let r = ::new(accessor).await; Ok((r,)) }) }, @@ -638,7 +598,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::static_a(accessor).await; + let r = ::static_a(accessor).await; Ok((r,)) }) }, @@ -651,7 +611,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::method_a(accessor, arg0) + let r = ::method_a(accessor, arg0) .await; Ok((r,)) }) @@ -665,7 +625,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::bar_own_arg(accessor, arg0) + let r = ::bar_own_arg(accessor, arg0) .await; Ok(r) }) @@ -679,7 +639,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::bar_borrow_arg(accessor, arg0) + let r = ::bar_borrow_arg(accessor, arg0) .await; Ok(r) }) @@ -690,7 +650,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::bar_result(accessor).await; + let r = ::bar_result(accessor).await; Ok((r,)) }) }, @@ -703,7 +663,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::tuple_own_arg(accessor, arg0) + let r = ::tuple_own_arg(accessor, arg0) .await; Ok(r) }) @@ -717,7 +677,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::tuple_borrow_arg( + let r = ::tuple_borrow_arg( accessor, arg0, ) @@ -731,7 +691,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::tuple_result(accessor).await; + let r = ::tuple_result(accessor).await; Ok((r,)) }) }, @@ -744,7 +704,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::option_own_arg(accessor, arg0) + let r = ::option_own_arg(accessor, arg0) .await; Ok(r) }) @@ -758,7 +718,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::option_borrow_arg( + let r = ::option_borrow_arg( accessor, arg0, ) @@ -772,7 +732,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::option_result(accessor).await; + let r = ::option_result(accessor).await; Ok((r,)) }) }, @@ -785,7 +745,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::result_own_arg(accessor, arg0) + let r = ::result_own_arg(accessor, arg0) .await; Ok(r) }) @@ -799,7 +759,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::result_borrow_arg( + let r = ::result_borrow_arg( accessor, arg0, ) @@ -813,7 +773,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::result_result(accessor).await; + let r = ::result_result(accessor).await; Ok((r,)) }) }, @@ -832,7 +792,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_own_arg(accessor, arg0) + let r = ::list_own_arg(accessor, arg0) .await; Ok(r) }) @@ -852,10 +812,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_borrow_arg( - accessor, - arg0, - ) + let r = ::list_borrow_arg(accessor, arg0) .await; Ok(r) }) @@ -866,7 +823,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::list_result(accessor).await; + let r = ::list_result(accessor).await; Ok((r,)) }) }, @@ -879,7 +836,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::record_own_arg(accessor, arg0) + let r = ::record_own_arg(accessor, arg0) .await; Ok(r) }) @@ -893,7 +850,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::record_borrow_arg( + let r = ::record_borrow_arg( accessor, arg0, ) @@ -907,7 +864,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::record_result(accessor).await; + let r = ::record_result(accessor).await; Ok((r,)) }) }, @@ -920,7 +877,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::func_with_handle_typedef( + let r = ::func_with_handle_typedef( accessor, arg0, ) @@ -937,8 +894,7 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum A {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostAConcurrent: wasmtime::component::HasData + Send { + pub trait HostAWithStore: wasmtime::component::HasData { fn drop( accessor: &wasmtime::component::Accessor, rep: wasmtime::component::Resource, @@ -946,20 +902,21 @@ pub mod foo { where Self: Sized; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostA: Send {} + pub trait HostA {} impl<_T: HostA + ?Sized + Send> HostA for &mut _T {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send + HostAConcurrent {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostA {} + pub trait HostWithStore: wasmtime::component::HasData + HostAWithStore + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostAWithStore + Send, + {} + pub trait Host: HostA + Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -970,7 +927,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, rep| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - HostAConcurrent::drop( + HostAWithStore::drop( accessor, wasmtime::component::Resource::new_own(rep), ) @@ -986,17 +943,21 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub type A = super::super::super::foo::foo::long_use_chain1::A; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/long-use-chain2")?; Ok(()) @@ -1007,17 +968,21 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub type A = super::super::super::foo::foo::long_use_chain2::A; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/long-use-chain3")?; Ok(()) @@ -1028,17 +993,13 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub type A = super::super::super::foo::foo::long_use_chain3::A; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn foo( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::Resource, - > + Send - where - Self: Sized; + > + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -1046,7 +1007,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -1056,7 +1017,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::foo(accessor).await; + let r = ::foo(accessor).await; Ok((r,)) }) }, @@ -1069,8 +1030,7 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum Foo {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostFooConcurrent: wasmtime::component::HasData + Send { + pub trait HostFooWithStore: wasmtime::component::HasData { fn drop( accessor: &wasmtime::component::Accessor, rep: wasmtime::component::Resource, @@ -1078,20 +1038,21 @@ pub mod foo { where Self: Sized; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostFoo: Send {} + pub trait HostFoo {} impl<_T: HostFoo + ?Sized + Send> HostFoo for &mut _T {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send + HostFooConcurrent {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostFoo {} + pub trait HostWithStore: wasmtime::component::HasData + HostFooWithStore + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostFooWithStore + Send, + {} + pub trait Host: HostFoo + Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -1103,7 +1064,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, rep| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - HostFooConcurrent::drop( + HostFooWithStore::drop( accessor, wasmtime::component::Resource::new_own(rep), ) @@ -1156,7 +1117,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/uses-resource-transitively` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/resources-import_tracing_async.rs b/crates/component-macro/tests/expanded/resources-import_tracing_async.rs index 631e3b8419..662d8773cb 100644 --- a/crates/component-macro/tests/expanded/resources-import_tracing_async.rs +++ b/crates/component-macro/tests/expanded/resources-import_tracing_async.rs @@ -1,23 +1,41 @@ pub enum WorldResource {} -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] +pub trait HostWorldResourceWithStore: wasmtime::component::HasData + Send {} +impl<_T: ?Sized> HostWorldResourceWithStore for _T +where + _T: wasmtime::component::HasData + Send, +{} pub trait HostWorldResource: Send { - async fn new(&mut self) -> wasmtime::component::Resource; - async fn foo(&mut self, self_: wasmtime::component::Resource) -> (); - async fn static_foo(&mut self) -> (); - async fn drop( + fn new( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send; + fn foo( + &mut self, + self_: wasmtime::component::Resource, + ) -> impl ::core::future::Future + Send; + fn static_foo(&mut self) -> impl ::core::future::Future + Send; + fn drop( &mut self, rep: wasmtime::component::Resource, - ) -> wasmtime::Result<()>; + ) -> impl ::core::future::Future> + Send; } impl<_T: HostWorldResource + ?Sized + Send> HostWorldResource for &mut _T { - async fn new(&mut self) -> wasmtime::component::Resource { - HostWorldResource::new(*self).await + fn new( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send { + async move { HostWorldResource::new(*self).await } } - async fn foo(&mut self, self_: wasmtime::component::Resource) -> () { - HostWorldResource::foo(*self, self_).await + fn foo( + &mut self, + self_: wasmtime::component::Resource, + ) -> impl ::core::future::Future + Send { + async move { HostWorldResource::foo(*self, self_).await } } - async fn static_foo(&mut self) -> () { - HostWorldResource::static_foo(*self).await + fn static_foo(&mut self) -> impl ::core::future::Future + Send { + async move { HostWorldResource::static_foo(*self).await } } async fn drop( &mut self, @@ -71,13 +89,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -102,13 +128,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -124,13 +150,25 @@ pub struct TheWorld { interface1: exports::foo::foo::uses_resource_transitively::Guest, some_world_func2: wasmtime::component::Func, } -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] -pub trait TheWorldImports: Send + HostWorldResource { - async fn some_world_func(&mut self) -> wasmtime::component::Resource; +pub trait TheWorldImportsWithStore: wasmtime::component::HasData + HostWorldResourceWithStore + Send {} +impl<_T: ?Sized> TheWorldImportsWithStore for _T +where + _T: wasmtime::component::HasData + HostWorldResourceWithStore + Send, +{} +pub trait TheWorldImports: HostWorldResource + Send { + fn some_world_func( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send; } impl<_T: TheWorldImports + ?Sized + Send> TheWorldImports for &mut _T { - async fn some_world_func(&mut self) -> wasmtime::component::Resource { - TheWorldImports::some_world_func(*self).await + fn some_world_func( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send { + async move { TheWorldImports::some_world_func(*self).await } } } const _: () = { @@ -209,17 +247,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -230,12 +265,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker_imports( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: TheWorldImportsWithStore, for<'a> D::Data<'a>: TheWorldImports, T: 'static + Send, { @@ -363,7 +411,13 @@ const _: () = { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::resources::HostWithStore + + foo::foo::long_use_chain1::HostWithStore + + foo::foo::long_use_chain2::HostWithStore + + foo::foo::long_use_chain3::HostWithStore + + foo::foo::long_use_chain4::HostWithStore + + foo::foo::transitive_interface_with_resource::HostWithStore + + TheWorldImportsWithStore + Send, for<'a> D::Data< 'a, >: foo::foo::resources::Host + foo::foo::long_use_chain1::Host @@ -424,31 +478,47 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum Bar {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostBarWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostBarWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait HostBar: Send { - async fn new(&mut self) -> wasmtime::component::Resource; - async fn static_a(&mut self) -> u32; - async fn method_a( + fn new( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send; + fn static_a( + &mut self, + ) -> impl ::core::future::Future + Send; + fn method_a( &mut self, self_: wasmtime::component::Resource, - ) -> u32; - async fn drop( + ) -> impl ::core::future::Future + Send; + fn drop( &mut self, rep: wasmtime::component::Resource, - ) -> wasmtime::Result<()>; + ) -> impl ::core::future::Future> + Send; } impl<_T: HostBar + ?Sized + Send> HostBar for &mut _T { - async fn new(&mut self) -> wasmtime::component::Resource { - HostBar::new(*self).await + fn new( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send { + async move { HostBar::new(*self).await } } - async fn static_a(&mut self) -> u32 { - HostBar::static_a(*self).await + fn static_a( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { HostBar::static_a(*self).await } } - async fn method_a( + fn method_a( &mut self, self_: wasmtime::component::Resource, - ) -> u32 { - HostBar::method_a(*self, self_).await + ) -> impl ::core::future::Future + Send { + async move { HostBar::method_a(*self, self_).await } } async fn drop( &mut self, @@ -512,173 +582,223 @@ pub mod foo { 4 == < SomeHandle as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostBar { - async fn bar_own_arg( + pub trait HostWithStore: wasmtime::component::HasData + HostBarWithStore + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostBarWithStore + Send, + {} + pub trait Host: HostBar + Send { + fn bar_own_arg( &mut self, x: wasmtime::component::Resource, - ) -> (); - async fn bar_borrow_arg( + ) -> impl ::core::future::Future + Send; + fn bar_borrow_arg( &mut self, x: wasmtime::component::Resource, - ) -> (); - async fn bar_result(&mut self) -> wasmtime::component::Resource; - async fn tuple_own_arg( + ) -> impl ::core::future::Future + Send; + fn bar_result( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send; + fn tuple_own_arg( &mut self, x: (wasmtime::component::Resource, u32), - ) -> (); - async fn tuple_borrow_arg( + ) -> impl ::core::future::Future + Send; + fn tuple_borrow_arg( &mut self, x: (wasmtime::component::Resource, u32), - ) -> (); - async fn tuple_result( + ) -> impl ::core::future::Future + Send; + fn tuple_result( &mut self, - ) -> (wasmtime::component::Resource, u32); - async fn option_own_arg( + ) -> impl ::core::future::Future< + Output = (wasmtime::component::Resource, u32), + > + Send; + fn option_own_arg( &mut self, x: Option>, - ) -> (); - async fn option_borrow_arg( + ) -> impl ::core::future::Future + Send; + fn option_borrow_arg( &mut self, x: Option>, - ) -> (); - async fn option_result( + ) -> impl ::core::future::Future + Send; + fn option_result( &mut self, - ) -> Option>; - async fn result_own_arg( + ) -> impl ::core::future::Future< + Output = Option>, + > + Send; + fn result_own_arg( &mut self, x: Result, ()>, - ) -> (); - async fn result_borrow_arg( + ) -> impl ::core::future::Future + Send; + fn result_borrow_arg( &mut self, x: Result, ()>, - ) -> (); - async fn result_result( + ) -> impl ::core::future::Future + Send; + fn result_result( &mut self, - ) -> Result, ()>; - async fn list_own_arg( + ) -> impl ::core::future::Future< + Output = Result, ()>, + > + Send; + fn list_own_arg( &mut self, x: wasmtime::component::__internal::Vec< wasmtime::component::Resource, >, - ) -> (); - async fn list_borrow_arg( + ) -> impl ::core::future::Future + Send; + fn list_borrow_arg( &mut self, x: wasmtime::component::__internal::Vec< wasmtime::component::Resource, >, - ) -> (); - async fn list_result( + ) -> impl ::core::future::Future + Send; + fn list_result( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + wasmtime::component::Resource, + >, + > + Send; + fn record_own_arg( + &mut self, + x: NestedOwn, + ) -> impl ::core::future::Future + Send; + fn record_borrow_arg( + &mut self, + x: NestedBorrow, + ) -> impl ::core::future::Future + Send; + fn record_result( &mut self, - ) -> wasmtime::component::__internal::Vec< - wasmtime::component::Resource, - >; - async fn record_own_arg(&mut self, x: NestedOwn) -> (); - async fn record_borrow_arg(&mut self, x: NestedBorrow) -> (); - async fn record_result(&mut self) -> NestedOwn; - async fn func_with_handle_typedef(&mut self, x: SomeHandle) -> (); + ) -> impl ::core::future::Future + Send; + fn func_with_handle_typedef( + &mut self, + x: SomeHandle, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn bar_own_arg( + fn bar_own_arg( &mut self, x: wasmtime::component::Resource, - ) -> () { - Host::bar_own_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::bar_own_arg(*self, x).await } } - async fn bar_borrow_arg( + fn bar_borrow_arg( &mut self, x: wasmtime::component::Resource, - ) -> () { - Host::bar_borrow_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::bar_borrow_arg(*self, x).await } } - async fn bar_result(&mut self) -> wasmtime::component::Resource { - Host::bar_result(*self).await + fn bar_result( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send { + async move { Host::bar_result(*self).await } } - async fn tuple_own_arg( + fn tuple_own_arg( &mut self, x: (wasmtime::component::Resource, u32), - ) -> () { - Host::tuple_own_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::tuple_own_arg(*self, x).await } } - async fn tuple_borrow_arg( + fn tuple_borrow_arg( &mut self, x: (wasmtime::component::Resource, u32), - ) -> () { - Host::tuple_borrow_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::tuple_borrow_arg(*self, x).await } } - async fn tuple_result( + fn tuple_result( &mut self, - ) -> (wasmtime::component::Resource, u32) { - Host::tuple_result(*self).await + ) -> impl ::core::future::Future< + Output = (wasmtime::component::Resource, u32), + > + Send { + async move { Host::tuple_result(*self).await } } - async fn option_own_arg( + fn option_own_arg( &mut self, x: Option>, - ) -> () { - Host::option_own_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::option_own_arg(*self, x).await } } - async fn option_borrow_arg( + fn option_borrow_arg( &mut self, x: Option>, - ) -> () { - Host::option_borrow_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::option_borrow_arg(*self, x).await } } - async fn option_result( + fn option_result( &mut self, - ) -> Option> { - Host::option_result(*self).await + ) -> impl ::core::future::Future< + Output = Option>, + > + Send { + async move { Host::option_result(*self).await } } - async fn result_own_arg( + fn result_own_arg( &mut self, x: Result, ()>, - ) -> () { - Host::result_own_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::result_own_arg(*self, x).await } } - async fn result_borrow_arg( + fn result_borrow_arg( &mut self, x: Result, ()>, - ) -> () { - Host::result_borrow_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::result_borrow_arg(*self, x).await } } - async fn result_result( + fn result_result( &mut self, - ) -> Result, ()> { - Host::result_result(*self).await + ) -> impl ::core::future::Future< + Output = Result, ()>, + > + Send { + async move { Host::result_result(*self).await } } - async fn list_own_arg( + fn list_own_arg( &mut self, x: wasmtime::component::__internal::Vec< wasmtime::component::Resource, >, - ) -> () { - Host::list_own_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_own_arg(*self, x).await } } - async fn list_borrow_arg( + fn list_borrow_arg( &mut self, x: wasmtime::component::__internal::Vec< wasmtime::component::Resource, >, - ) -> () { - Host::list_borrow_arg(*self, x).await + ) -> impl ::core::future::Future + Send { + async move { Host::list_borrow_arg(*self, x).await } } - async fn list_result( + fn list_result( &mut self, - ) -> wasmtime::component::__internal::Vec< - wasmtime::component::Resource, - > { - Host::list_result(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + wasmtime::component::Resource, + >, + > + Send { + async move { Host::list_result(*self).await } } - async fn record_own_arg(&mut self, x: NestedOwn) -> () { - Host::record_own_arg(*self, x).await + fn record_own_arg( + &mut self, + x: NestedOwn, + ) -> impl ::core::future::Future + Send { + async move { Host::record_own_arg(*self, x).await } } - async fn record_borrow_arg(&mut self, x: NestedBorrow) -> () { - Host::record_borrow_arg(*self, x).await + fn record_borrow_arg( + &mut self, + x: NestedBorrow, + ) -> impl ::core::future::Future + Send { + async move { Host::record_borrow_arg(*self, x).await } } - async fn record_result(&mut self) -> NestedOwn { - Host::record_result(*self).await + fn record_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::record_result(*self).await } } - async fn func_with_handle_typedef(&mut self, x: SomeHandle) -> () { - Host::func_with_handle_typedef(*self, x).await + fn func_with_handle_typedef( + &mut self, + x: SomeHandle, + ) -> impl ::core::future::Future + Send { + async move { Host::func_with_handle_typedef(*self, x).await } } } pub fn add_to_linker( @@ -686,7 +806,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -1314,12 +1434,16 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum A {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostA: Send { - async fn drop( + pub trait HostAWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostAWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait HostA { + fn drop( &mut self, rep: wasmtime::component::Resource, - ) -> wasmtime::Result<()>; + ) -> impl ::core::future::Future> + Send; } impl<_T: HostA + ?Sized + Send> HostA for &mut _T { async fn drop( @@ -1329,15 +1453,19 @@ pub mod foo { HostA::drop(*self, rep).await } } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostA {} + pub trait HostWithStore: wasmtime::component::HasData + HostAWithStore + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostAWithStore + Send, + {} + pub trait Host: HostA + Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -1363,17 +1491,21 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub type A = super::super::super::foo::foo::long_use_chain1::A; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/long-use-chain2")?; Ok(()) @@ -1384,17 +1516,21 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub type A = super::super::super::foo::foo::long_use_chain2::A; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/long-use-chain3")?; Ok(()) @@ -1405,13 +1541,25 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub type A = super::super::super::foo::foo::long_use_chain3::A; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn foo(&mut self) -> wasmtime::component::Resource; + fn foo( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn foo(&mut self) -> wasmtime::component::Resource { - Host::foo(*self).await + fn foo( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::Resource, + > + Send { + async move { Host::foo(*self).await } } } pub fn add_to_linker( @@ -1419,7 +1567,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -1455,12 +1603,16 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum Foo {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostFoo: Send { - async fn drop( + pub trait HostFooWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostFooWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait HostFoo { + fn drop( &mut self, rep: wasmtime::component::Resource, - ) -> wasmtime::Result<()>; + ) -> impl ::core::future::Future> + Send; } impl<_T: HostFoo + ?Sized + Send> HostFoo for &mut _T { async fn drop( @@ -1470,15 +1622,19 @@ pub mod foo { HostFoo::drop(*self, rep).await } } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostFoo {} + pub trait HostWithStore: wasmtime::component::HasData + HostFooWithStore + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostFooWithStore + Send, + {} + pub trait Host: HostFoo + Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -1542,7 +1698,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/uses-resource-transitively` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/share-types.rs b/crates/component-macro/tests/expanded/share-types.rs index 2149a85a03..622ac4bd10 100644 --- a/crates/component-macro/tests/expanded/share-types.rs +++ b/crates/component-macro/tests/expanded/share-types.rs @@ -52,6 +52,17 @@ impl<_T: 'static> HttpInterfacePre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> HttpInterfacePre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `http-interface`. /// @@ -144,12 +155,25 @@ const _: () = { let indices = HttpInterfaceIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`HttpInterfacePre::new`] and + /// [`HttpInterfacePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + HttpInterfacePre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::http_types::HostWithStore + http_fetch::HostWithStore, for<'a> D::Data<'a>: foo::foo::http_types::Host + http_fetch::Host, T: 'static, { @@ -206,6 +230,11 @@ pub mod foo { 4 == < Response as wasmtime::component::ComponentType >::ALIGN32 ); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host {} impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( @@ -213,7 +242,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -237,6 +266,11 @@ pub mod http_fetch { assert!(8 == < Response as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Response as wasmtime::component::ComponentType >::ALIGN32); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn fetch_request(&mut self, request: Request) -> Response; } @@ -250,7 +284,7 @@ pub mod http_fetch { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -311,7 +345,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `http-handler` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/share-types_async.rs b/crates/component-macro/tests/expanded/share-types_async.rs index d6f1c0d5b4..ad1e2ec494 100644 --- a/crates/component-macro/tests/expanded/share-types_async.rs +++ b/crates/component-macro/tests/expanded/share-types_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> HttpInterfacePre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> HttpInterfacePre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct HttpInterfaceIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`HttpInterface::instantiate_async`] which only needs a +/// [`HttpInterface::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`HttpInterfacePre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`HttpInterfacePre::instantiate_async`] to +/// method then uses [`HttpInterfacePre::instantiate`] to /// create a [`HttpInterface`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl HttpInterface { /// Convenience wrapper around [`HttpInterfacePre::new`] and - /// [`HttpInterfacePre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`HttpInterfacePre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - HttpInterfacePre::new(pre)?.instantiate_async(store).await + HttpInterfacePre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`HttpInterfaceIndices::new`] and /// [`HttpInterfaceIndices::load`]. @@ -150,12 +155,25 @@ const _: () = { let indices = HttpInterfaceIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`HttpInterfacePre::new`] and + /// [`HttpInterfacePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + HttpInterfacePre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::http_types::HostWithStore + http_fetch::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::http_types::Host + http_fetch::Host + Send, T: 'static + Send, { @@ -212,17 +230,21 @@ pub mod foo { 4 == < Response as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/http-types")?; Ok(()) @@ -244,13 +266,23 @@ pub mod http_fetch { assert!(8 == < Response as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Response as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn fetch_request(&mut self, request: Request) -> Response; + fn fetch_request( + &mut self, + request: Request, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn fetch_request(&mut self, request: Request) -> Response { - Host::fetch_request(*self, request).await + fn fetch_request( + &mut self, + request: Request, + ) -> impl ::core::future::Future + Send { + async move { Host::fetch_request(*self, request).await } } } pub fn add_to_linker( @@ -258,7 +290,7 @@ pub mod http_fetch { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -321,7 +353,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `http-handler` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/share-types_concurrent.rs b/crates/component-macro/tests/expanded/share-types_concurrent.rs index 81d3efbf81..2591cdd87c 100644 --- a/crates/component-macro/tests/expanded/share-types_concurrent.rs +++ b/crates/component-macro/tests/expanded/share-types_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> HttpInterfacePre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> HttpInterfacePre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct HttpInterfaceIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`HttpInterface::instantiate_async`] which only needs a +/// [`HttpInterface::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`HttpInterfacePre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`HttpInterfacePre::instantiate_async`] to +/// method then uses [`HttpInterfacePre::instantiate`] to /// create a [`HttpInterface`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl HttpInterface { /// Convenience wrapper around [`HttpInterfacePre::new`] and - /// [`HttpInterfacePre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`HttpInterfacePre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - HttpInterfacePre::new(pre)?.instantiate_async(store).await + HttpInterfacePre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`HttpInterfaceIndices::new`] and /// [`HttpInterfaceIndices::load`]. @@ -150,12 +155,25 @@ const _: () = { let indices = HttpInterfaceIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`HttpInterfacePre::new`] and + /// [`HttpInterfacePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + HttpInterfacePre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: http_fetch::HostConcurrent + Send, + D: foo::foo::http_types::HostWithStore + http_fetch::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::http_types::Host + http_fetch::Host + Send, T: 'static + Send, { @@ -212,17 +230,21 @@ pub mod foo { 4 == < Response as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/http-types")?; Ok(()) @@ -244,16 +266,12 @@ pub mod http_fetch { assert!(8 == < Response as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Response as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn fetch_request( accessor: &wasmtime::component::Accessor, request: Request, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -261,7 +279,7 @@ pub mod http_fetch { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -271,7 +289,7 @@ pub mod http_fetch { move |caller: &wasmtime::component::Accessor, (arg0,): (Request,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::fetch_request(accessor, arg0).await; + let r = ::fetch_request(accessor, arg0).await; Ok((r,)) }) }, diff --git a/crates/component-macro/tests/expanded/share-types_tracing_async.rs b/crates/component-macro/tests/expanded/share-types_tracing_async.rs index f57e3c7fdc..8f5d127702 100644 --- a/crates/component-macro/tests/expanded/share-types_tracing_async.rs +++ b/crates/component-macro/tests/expanded/share-types_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> HttpInterfacePre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> HttpInterfacePre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct HttpInterfaceIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`HttpInterface::instantiate_async`] which only needs a +/// [`HttpInterface::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`HttpInterfacePre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`HttpInterfacePre::instantiate_async`] to +/// method then uses [`HttpInterfacePre::instantiate`] to /// create a [`HttpInterface`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl HttpInterface { /// Convenience wrapper around [`HttpInterfacePre::new`] and - /// [`HttpInterfacePre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`HttpInterfacePre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - HttpInterfacePre::new(pre)?.instantiate_async(store).await + HttpInterfacePre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`HttpInterfaceIndices::new`] and /// [`HttpInterfaceIndices::load`]. @@ -150,12 +155,25 @@ const _: () = { let indices = HttpInterfaceIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`HttpInterfacePre::new`] and + /// [`HttpInterfacePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + HttpInterfacePre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::http_types::HostWithStore + http_fetch::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::http_types::Host + http_fetch::Host + Send, T: 'static + Send, { @@ -212,17 +230,21 @@ pub mod foo { 4 == < Response as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/http-types")?; Ok(()) @@ -244,13 +266,23 @@ pub mod http_fetch { assert!(8 == < Response as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Response as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn fetch_request(&mut self, request: Request) -> Response; + fn fetch_request( + &mut self, + request: Request, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn fetch_request(&mut self, request: Request) -> Response { - Host::fetch_request(*self, request).await + fn fetch_request( + &mut self, + request: Request, + ) -> impl ::core::future::Future + Send { + async move { Host::fetch_request(*self, request).await } } } pub fn add_to_linker( @@ -258,7 +290,7 @@ pub mod http_fetch { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -337,7 +369,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `http-handler` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/simple-functions.rs b/crates/component-macro/tests/expanded/simple-functions.rs index 89579b1b49..8c5abab81b 100644 --- a/crates/component-macro/tests/expanded/simple-functions.rs +++ b/crates/component-macro/tests/expanded/simple-functions.rs @@ -52,6 +52,17 @@ impl<_T: 'static> TheWorldPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `the-world`. /// @@ -146,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::simple::HostWithStore, for<'a> D::Data<'a>: foo::foo::simple::Host, T: 'static, { @@ -169,6 +193,11 @@ pub mod foo { pub mod simple { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn f1(&mut self) -> (); fn f2(&mut self, a: u32) -> (); @@ -202,7 +231,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -315,7 +344,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/simple` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/simple-functions_async.rs b/crates/component-macro/tests/expanded/simple-functions_async.rs index 219b0289c4..2cd17461eb 100644 --- a/crates/component-macro/tests/expanded/simple-functions_async.rs +++ b/crates/component-macro/tests/expanded/simple-functions_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::simple::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::simple::Host + Send, T: 'static + Send, { @@ -175,33 +193,65 @@ pub mod foo { pub mod simple { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn f1(&mut self) -> (); - async fn f2(&mut self, a: u32) -> (); - async fn f3(&mut self, a: u32, b: u32) -> (); - async fn f4(&mut self) -> u32; - async fn f5(&mut self) -> (u32, u32); - async fn f6(&mut self, a: u32, b: u32, c: u32) -> (u32, u32, u32); + fn f1(&mut self) -> impl ::core::future::Future + Send; + fn f2( + &mut self, + a: u32, + ) -> impl ::core::future::Future + Send; + fn f3( + &mut self, + a: u32, + b: u32, + ) -> impl ::core::future::Future + Send; + fn f4(&mut self) -> impl ::core::future::Future + Send; + fn f5( + &mut self, + ) -> impl ::core::future::Future + Send; + fn f6( + &mut self, + a: u32, + b: u32, + c: u32, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn f1(&mut self) -> () { - Host::f1(*self).await + fn f1(&mut self) -> impl ::core::future::Future + Send { + async move { Host::f1(*self).await } } - async fn f2(&mut self, a: u32) -> () { - Host::f2(*self, a).await + fn f2( + &mut self, + a: u32, + ) -> impl ::core::future::Future + Send { + async move { Host::f2(*self, a).await } } - async fn f3(&mut self, a: u32, b: u32) -> () { - Host::f3(*self, a, b).await + fn f3( + &mut self, + a: u32, + b: u32, + ) -> impl ::core::future::Future + Send { + async move { Host::f3(*self, a, b).await } } - async fn f4(&mut self) -> u32 { - Host::f4(*self).await + fn f4(&mut self) -> impl ::core::future::Future + Send { + async move { Host::f4(*self).await } } - async fn f5(&mut self) -> (u32, u32) { - Host::f5(*self).await + fn f5( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::f5(*self).await } } - async fn f6(&mut self, a: u32, b: u32, c: u32) -> (u32, u32, u32) { - Host::f6(*self, a, b, c).await + fn f6( + &mut self, + a: u32, + b: u32, + c: u32, + ) -> impl ::core::future::Future + Send { + async move { Host::f6(*self, a, b, c).await } } } pub fn add_to_linker( @@ -209,7 +259,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -334,7 +384,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/simple` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/simple-functions_concurrent.rs b/crates/component-macro/tests/expanded/simple-functions_concurrent.rs index c71d698724..843d04ae8e 100644 --- a/crates/component-macro/tests/expanded/simple-functions_concurrent.rs +++ b/crates/component-macro/tests/expanded/simple-functions_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::simple::HostConcurrent + Send, + D: foo::foo::simple::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::simple::Host + Send, T: 'static + Send, { @@ -175,46 +193,32 @@ pub mod foo { pub mod simple { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn f1( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn f2( accessor: &wasmtime::component::Accessor, a: u32, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn f3( accessor: &wasmtime::component::Accessor, a: u32, b: u32, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn f4( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn f5( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn f6( accessor: &wasmtime::component::Accessor, a: u32, b: u32, c: u32, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -222,7 +226,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -232,7 +236,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::f1(accessor).await; + let r = ::f1(accessor).await; Ok(r) }) }, @@ -242,7 +246,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (u32,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::f2(accessor, arg0).await; + let r = ::f2(accessor, arg0).await; Ok(r) }) }, @@ -255,8 +259,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::f3(accessor, arg0, arg1) - .await; + let r = ::f3(accessor, arg0, arg1).await; Ok(r) }) }, @@ -266,7 +269,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::f4(accessor).await; + let r = ::f4(accessor).await; Ok((r,)) }) }, @@ -276,7 +279,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::f5(accessor).await; + let r = ::f5(accessor).await; Ok((r,)) }) }, @@ -289,7 +292,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::f6(accessor, arg0, arg1, arg2) + let r = ::f6(accessor, arg0, arg1, arg2) .await; Ok((r,)) }) diff --git a/crates/component-macro/tests/expanded/simple-functions_tracing_async.rs b/crates/component-macro/tests/expanded/simple-functions_tracing_async.rs index 51a6599aef..ca16fdd8ca 100644 --- a/crates/component-macro/tests/expanded/simple-functions_tracing_async.rs +++ b/crates/component-macro/tests/expanded/simple-functions_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::simple::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::simple::Host + Send, T: 'static + Send, { @@ -175,33 +193,65 @@ pub mod foo { pub mod simple { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn f1(&mut self) -> (); - async fn f2(&mut self, a: u32) -> (); - async fn f3(&mut self, a: u32, b: u32) -> (); - async fn f4(&mut self) -> u32; - async fn f5(&mut self) -> (u32, u32); - async fn f6(&mut self, a: u32, b: u32, c: u32) -> (u32, u32, u32); + fn f1(&mut self) -> impl ::core::future::Future + Send; + fn f2( + &mut self, + a: u32, + ) -> impl ::core::future::Future + Send; + fn f3( + &mut self, + a: u32, + b: u32, + ) -> impl ::core::future::Future + Send; + fn f4(&mut self) -> impl ::core::future::Future + Send; + fn f5( + &mut self, + ) -> impl ::core::future::Future + Send; + fn f6( + &mut self, + a: u32, + b: u32, + c: u32, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn f1(&mut self) -> () { - Host::f1(*self).await + fn f1(&mut self) -> impl ::core::future::Future + Send { + async move { Host::f1(*self).await } } - async fn f2(&mut self, a: u32) -> () { - Host::f2(*self, a).await + fn f2( + &mut self, + a: u32, + ) -> impl ::core::future::Future + Send { + async move { Host::f2(*self, a).await } } - async fn f3(&mut self, a: u32, b: u32) -> () { - Host::f3(*self, a, b).await + fn f3( + &mut self, + a: u32, + b: u32, + ) -> impl ::core::future::Future + Send { + async move { Host::f3(*self, a, b).await } } - async fn f4(&mut self) -> u32 { - Host::f4(*self).await + fn f4(&mut self) -> impl ::core::future::Future + Send { + async move { Host::f4(*self).await } } - async fn f5(&mut self) -> (u32, u32) { - Host::f5(*self).await + fn f5( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::f5(*self).await } } - async fn f6(&mut self, a: u32, b: u32, c: u32) -> (u32, u32, u32) { - Host::f6(*self, a, b, c).await + fn f6( + &mut self, + a: u32, + b: u32, + c: u32, + ) -> impl ::core::future::Future + Send { + async move { Host::f6(*self, a, b, c).await } } } pub fn add_to_linker( @@ -209,7 +259,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -422,7 +472,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/simple` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/simple-lists.rs b/crates/component-macro/tests/expanded/simple-lists.rs index df65384c1d..3e8d2d7f3d 100644 --- a/crates/component-macro/tests/expanded/simple-lists.rs +++ b/crates/component-macro/tests/expanded/simple-lists.rs @@ -52,6 +52,17 @@ impl<_T: 'static> MyWorldPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> MyWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `my-world`. /// @@ -146,12 +157,25 @@ const _: () = { let indices = MyWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`MyWorldPre::new`] and + /// [`MyWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + MyWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::simple_lists::HostWithStore, for<'a> D::Data<'a>: foo::foo::simple_lists::Host, T: 'static, { @@ -169,6 +193,11 @@ pub mod foo { pub mod simple_lists { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn simple_list1( &mut self, @@ -228,7 +257,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -336,7 +365,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/simple-lists` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/simple-lists_async.rs b/crates/component-macro/tests/expanded/simple-lists_async.rs index d35106e6c2..18b7f05aa7 100644 --- a/crates/component-macro/tests/expanded/simple-lists_async.rs +++ b/crates/component-macro/tests/expanded/simple-lists_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> MyWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> MyWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct MyWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`MyWorld::instantiate_async`] which only needs a +/// [`MyWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`MyWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`MyWorldPre::instantiate_async`] to +/// method then uses [`MyWorldPre::instantiate`] to /// create a [`MyWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl MyWorld { /// Convenience wrapper around [`MyWorldPre::new`] and - /// [`MyWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`MyWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - MyWorldPre::new(pre)?.instantiate_async(store).await + MyWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`MyWorldIndices::new`] and /// [`MyWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = MyWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`MyWorldPre::new`] and + /// [`MyWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + MyWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::simple_lists::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::simple_lists::Host + Send, T: 'static + Send, { @@ -175,63 +193,79 @@ pub mod foo { pub mod simple_lists { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn simple_list1( + fn simple_list1( &mut self, l: wasmtime::component::__internal::Vec, - ) -> (); - async fn simple_list2( + ) -> impl ::core::future::Future + Send; + fn simple_list2( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn simple_list3( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn simple_list3( &mut self, a: wasmtime::component::__internal::Vec, b: wasmtime::component::__internal::Vec, - ) -> ( - wasmtime::component::__internal::Vec, - wasmtime::component::__internal::Vec, - ); - async fn simple_list4( + ) -> impl ::core::future::Future< + Output = ( + wasmtime::component::__internal::Vec, + wasmtime::component::__internal::Vec, + ), + > + Send; + fn simple_list4( &mut self, l: wasmtime::component::__internal::Vec< wasmtime::component::__internal::Vec, >, - ) -> wasmtime::component::__internal::Vec< - wasmtime::component::__internal::Vec, - >; + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + wasmtime::component::__internal::Vec, + >, + > + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn simple_list1( + fn simple_list1( &mut self, l: wasmtime::component::__internal::Vec, - ) -> () { - Host::simple_list1(*self, l).await + ) -> impl ::core::future::Future + Send { + async move { Host::simple_list1(*self, l).await } } - async fn simple_list2( + fn simple_list2( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::simple_list2(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::simple_list2(*self).await } } - async fn simple_list3( + fn simple_list3( &mut self, a: wasmtime::component::__internal::Vec, b: wasmtime::component::__internal::Vec, - ) -> ( - wasmtime::component::__internal::Vec, - wasmtime::component::__internal::Vec, - ) { - Host::simple_list3(*self, a, b).await + ) -> impl ::core::future::Future< + Output = ( + wasmtime::component::__internal::Vec, + wasmtime::component::__internal::Vec, + ), + > + Send { + async move { Host::simple_list3(*self, a, b).await } } - async fn simple_list4( + fn simple_list4( &mut self, l: wasmtime::component::__internal::Vec< wasmtime::component::__internal::Vec, >, - ) -> wasmtime::component::__internal::Vec< - wasmtime::component::__internal::Vec, - > { - Host::simple_list4(*self, l).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + wasmtime::component::__internal::Vec, + >, + > + Send { + async move { Host::simple_list4(*self, l).await } } } pub fn add_to_linker( @@ -239,7 +273,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -355,7 +389,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/simple-lists` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/simple-lists_concurrent.rs b/crates/component-macro/tests/expanded/simple-lists_concurrent.rs index 68c211b852..2dac5ac632 100644 --- a/crates/component-macro/tests/expanded/simple-lists_concurrent.rs +++ b/crates/component-macro/tests/expanded/simple-lists_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> MyWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> MyWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct MyWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`MyWorld::instantiate_async`] which only needs a +/// [`MyWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`MyWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`MyWorldPre::instantiate_async`] to +/// method then uses [`MyWorldPre::instantiate`] to /// create a [`MyWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl MyWorld { /// Convenience wrapper around [`MyWorldPre::new`] and - /// [`MyWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`MyWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - MyWorldPre::new(pre)?.instantiate_async(store).await + MyWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`MyWorldIndices::new`] and /// [`MyWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = MyWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`MyWorldPre::new`] and + /// [`MyWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + MyWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::simple_lists::HostConcurrent + Send, + D: foo::foo::simple_lists::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::simple_lists::Host + Send, T: 'static + Send, { @@ -175,21 +193,16 @@ pub mod foo { pub mod simple_lists { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn simple_list1( accessor: &wasmtime::component::Accessor, l: wasmtime::component::__internal::Vec, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn simple_list2( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::Vec, - > + Send - where - Self: Sized; + > + Send; fn simple_list3( accessor: &wasmtime::component::Accessor, a: wasmtime::component::__internal::Vec, @@ -199,9 +212,7 @@ pub mod foo { wasmtime::component::__internal::Vec, wasmtime::component::__internal::Vec, ), - > + Send - where - Self: Sized; + > + Send; fn simple_list4( accessor: &wasmtime::component::Accessor, l: wasmtime::component::__internal::Vec< @@ -211,11 +222,8 @@ pub mod foo { Output = wasmtime::component::__internal::Vec< wasmtime::component::__internal::Vec, >, - > + Send - where - Self: Sized; + > + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -223,7 +231,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -236,7 +244,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::simple_list1(accessor, arg0) + let r = ::simple_list1(accessor, arg0) .await; Ok(r) }) @@ -247,7 +255,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::simple_list2(accessor).await; + let r = ::simple_list2(accessor).await; Ok((r,)) }) }, @@ -266,7 +274,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::simple_list3( + let r = ::simple_list3( accessor, arg0, arg1, @@ -290,7 +298,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::simple_list4(accessor, arg0) + let r = ::simple_list4(accessor, arg0) .await; Ok((r,)) }) diff --git a/crates/component-macro/tests/expanded/simple-lists_tracing_async.rs b/crates/component-macro/tests/expanded/simple-lists_tracing_async.rs index f7b32a445b..c7837ff962 100644 --- a/crates/component-macro/tests/expanded/simple-lists_tracing_async.rs +++ b/crates/component-macro/tests/expanded/simple-lists_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> MyWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> MyWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct MyWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`MyWorld::instantiate_async`] which only needs a +/// [`MyWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`MyWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`MyWorldPre::instantiate_async`] to +/// method then uses [`MyWorldPre::instantiate`] to /// create a [`MyWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl MyWorld { /// Convenience wrapper around [`MyWorldPre::new`] and - /// [`MyWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`MyWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - MyWorldPre::new(pre)?.instantiate_async(store).await + MyWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`MyWorldIndices::new`] and /// [`MyWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = MyWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`MyWorldPre::new`] and + /// [`MyWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + MyWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::simple_lists::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::simple_lists::Host + Send, T: 'static + Send, { @@ -175,63 +193,79 @@ pub mod foo { pub mod simple_lists { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn simple_list1( + fn simple_list1( &mut self, l: wasmtime::component::__internal::Vec, - ) -> (); - async fn simple_list2( + ) -> impl ::core::future::Future + Send; + fn simple_list2( &mut self, - ) -> wasmtime::component::__internal::Vec; - async fn simple_list3( + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send; + fn simple_list3( &mut self, a: wasmtime::component::__internal::Vec, b: wasmtime::component::__internal::Vec, - ) -> ( - wasmtime::component::__internal::Vec, - wasmtime::component::__internal::Vec, - ); - async fn simple_list4( + ) -> impl ::core::future::Future< + Output = ( + wasmtime::component::__internal::Vec, + wasmtime::component::__internal::Vec, + ), + > + Send; + fn simple_list4( &mut self, l: wasmtime::component::__internal::Vec< wasmtime::component::__internal::Vec, >, - ) -> wasmtime::component::__internal::Vec< - wasmtime::component::__internal::Vec, - >; + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + wasmtime::component::__internal::Vec, + >, + > + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn simple_list1( + fn simple_list1( &mut self, l: wasmtime::component::__internal::Vec, - ) -> () { - Host::simple_list1(*self, l).await + ) -> impl ::core::future::Future + Send { + async move { Host::simple_list1(*self, l).await } } - async fn simple_list2( + fn simple_list2( &mut self, - ) -> wasmtime::component::__internal::Vec { - Host::simple_list2(*self).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec, + > + Send { + async move { Host::simple_list2(*self).await } } - async fn simple_list3( + fn simple_list3( &mut self, a: wasmtime::component::__internal::Vec, b: wasmtime::component::__internal::Vec, - ) -> ( - wasmtime::component::__internal::Vec, - wasmtime::component::__internal::Vec, - ) { - Host::simple_list3(*self, a, b).await + ) -> impl ::core::future::Future< + Output = ( + wasmtime::component::__internal::Vec, + wasmtime::component::__internal::Vec, + ), + > + Send { + async move { Host::simple_list3(*self, a, b).await } } - async fn simple_list4( + fn simple_list4( &mut self, l: wasmtime::component::__internal::Vec< wasmtime::component::__internal::Vec, >, - ) -> wasmtime::component::__internal::Vec< - wasmtime::component::__internal::Vec, - > { - Host::simple_list4(*self, l).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::Vec< + wasmtime::component::__internal::Vec, + >, + > + Send { + async move { Host::simple_list4(*self, l).await } } } pub fn add_to_linker( @@ -239,7 +273,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -416,7 +450,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/simple-lists` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/simple-wasi.rs b/crates/component-macro/tests/expanded/simple-wasi.rs index f5d26cc566..e03046174c 100644 --- a/crates/component-macro/tests/expanded/simple-wasi.rs +++ b/crates/component-macro/tests/expanded/simple-wasi.rs @@ -52,6 +52,17 @@ impl<_T: 'static> WasiPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> WasiPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `wasi`. /// @@ -138,12 +149,26 @@ const _: () = { let indices = WasiIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`WasiPre::new`] and + /// [`WasiPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + WasiPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::wasi_filesystem::HostWithStore + + foo::foo::wall_clock::HostWithStore, for<'a> D::Data< 'a, >: foo::foo::wasi_filesystem::Host + foo::foo::wall_clock::Host, @@ -222,6 +247,11 @@ pub mod foo { assert!(1 == < Errno as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Errno as wasmtime::component::ComponentType >::ALIGN32); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn create_directory_at(&mut self) -> Result<(), Errno>; fn stat(&mut self) -> Result; @@ -239,7 +269,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -286,6 +316,11 @@ pub mod foo { 1 == < WallClock as wasmtime::component::ComponentType >::ALIGN32 ); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host {} impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( @@ -293,7 +328,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { diff --git a/crates/component-macro/tests/expanded/simple-wasi_async.rs b/crates/component-macro/tests/expanded/simple-wasi_async.rs index 6325a8a477..440730644b 100644 --- a/crates/component-macro/tests/expanded/simple-wasi_async.rs +++ b/crates/component-macro/tests/expanded/simple-wasi_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> WasiPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> WasiPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct WasiIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Wasi::instantiate_async`] which only needs a +/// [`Wasi::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`WasiPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`WasiPre::instantiate_async`] to +/// method then uses [`WasiPre::instantiate`] to /// create a [`Wasi`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Wasi { /// Convenience wrapper around [`WasiPre::new`] and - /// [`WasiPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`WasiPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - WasiPre::new(pre)?.instantiate_async(store).await + WasiPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`WasiIndices::new`] and /// [`WasiIndices::load`]. @@ -144,12 +149,26 @@ const _: () = { let indices = WasiIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`WasiPre::new`] and + /// [`WasiPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + WasiPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::wasi_filesystem::HostWithStore + + foo::foo::wall_clock::HostWithStore + Send, for<'a> D::Data< 'a, >: foo::foo::wasi_filesystem::Host + foo::foo::wall_clock::Host + Send, @@ -228,17 +247,33 @@ pub mod foo { assert!(1 == < Errno as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Errno as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn create_directory_at(&mut self) -> Result<(), Errno>; - async fn stat(&mut self) -> Result; + fn create_directory_at( + &mut self, + ) -> impl ::core::future::Future> + Send; + fn stat( + &mut self, + ) -> impl ::core::future::Future< + Output = Result, + > + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn create_directory_at(&mut self) -> Result<(), Errno> { - Host::create_directory_at(*self).await + fn create_directory_at( + &mut self, + ) -> impl ::core::future::Future> + Send { + async move { Host::create_directory_at(*self).await } } - async fn stat(&mut self) -> Result { - Host::stat(*self).await + fn stat( + &mut self, + ) -> impl ::core::future::Future< + Output = Result, + > + Send { + async move { Host::stat(*self).await } } } pub fn add_to_linker( @@ -246,7 +281,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -297,17 +332,21 @@ pub mod foo { 1 == < WallClock as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/wall-clock")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/simple-wasi_concurrent.rs b/crates/component-macro/tests/expanded/simple-wasi_concurrent.rs index c1453128d0..3e2a1f612e 100644 --- a/crates/component-macro/tests/expanded/simple-wasi_concurrent.rs +++ b/crates/component-macro/tests/expanded/simple-wasi_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> WasiPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> WasiPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct WasiIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Wasi::instantiate_async`] which only needs a +/// [`Wasi::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`WasiPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`WasiPre::instantiate_async`] to +/// method then uses [`WasiPre::instantiate`] to /// create a [`Wasi`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Wasi { /// Convenience wrapper around [`WasiPre::new`] and - /// [`WasiPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`WasiPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - WasiPre::new(pre)?.instantiate_async(store).await + WasiPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`WasiIndices::new`] and /// [`WasiIndices::load`]. @@ -144,12 +149,26 @@ const _: () = { let indices = WasiIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`WasiPre::new`] and + /// [`WasiPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + WasiPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::wasi_filesystem::HostConcurrent + Send, + D: foo::foo::wasi_filesystem::HostWithStore + + foo::foo::wall_clock::HostWithStore + Send, for<'a> D::Data< 'a, >: foo::foo::wasi_filesystem::Host + foo::foo::wall_clock::Host + Send, @@ -228,22 +247,16 @@ pub mod foo { assert!(1 == < Errno as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Errno as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn create_directory_at( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future> + Send - where - Self: Sized; + ) -> impl ::core::future::Future> + Send; fn stat( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = Result, - > + Send - where - Self: Sized; + > + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -251,7 +264,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -261,7 +274,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::create_directory_at(accessor) + let r = ::create_directory_at(accessor) .await; Ok((r,)) }) @@ -272,7 +285,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::stat(accessor).await; + let r = ::stat(accessor).await; Ok((r,)) }) }, @@ -303,17 +316,21 @@ pub mod foo { 1 == < WallClock as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/wall-clock")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/simple-wasi_tracing_async.rs b/crates/component-macro/tests/expanded/simple-wasi_tracing_async.rs index 47909d4f15..bb6257dd21 100644 --- a/crates/component-macro/tests/expanded/simple-wasi_tracing_async.rs +++ b/crates/component-macro/tests/expanded/simple-wasi_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> WasiPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> WasiPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct WasiIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Wasi::instantiate_async`] which only needs a +/// [`Wasi::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`WasiPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`WasiPre::instantiate_async`] to +/// method then uses [`WasiPre::instantiate`] to /// create a [`Wasi`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Wasi { /// Convenience wrapper around [`WasiPre::new`] and - /// [`WasiPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`WasiPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - WasiPre::new(pre)?.instantiate_async(store).await + WasiPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`WasiIndices::new`] and /// [`WasiIndices::load`]. @@ -144,12 +149,26 @@ const _: () = { let indices = WasiIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`WasiPre::new`] and + /// [`WasiPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + WasiPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::wasi_filesystem::HostWithStore + + foo::foo::wall_clock::HostWithStore + Send, for<'a> D::Data< 'a, >: foo::foo::wasi_filesystem::Host + foo::foo::wall_clock::Host + Send, @@ -228,17 +247,33 @@ pub mod foo { assert!(1 == < Errno as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Errno as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn create_directory_at(&mut self) -> Result<(), Errno>; - async fn stat(&mut self) -> Result; + fn create_directory_at( + &mut self, + ) -> impl ::core::future::Future> + Send; + fn stat( + &mut self, + ) -> impl ::core::future::Future< + Output = Result, + > + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn create_directory_at(&mut self) -> Result<(), Errno> { - Host::create_directory_at(*self).await + fn create_directory_at( + &mut self, + ) -> impl ::core::future::Future> + Send { + async move { Host::create_directory_at(*self).await } } - async fn stat(&mut self) -> Result { - Host::stat(*self).await + fn stat( + &mut self, + ) -> impl ::core::future::Future< + Output = Result, + > + Send { + async move { Host::stat(*self).await } } } pub fn add_to_linker( @@ -246,7 +281,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -323,17 +358,21 @@ pub mod foo { 1 == < WallClock as wasmtime::component::ComponentType >::ALIGN32 ); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/wall-clock")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/small-anonymous.rs b/crates/component-macro/tests/expanded/small-anonymous.rs index 5bb11e4175..281f73a2e9 100644 --- a/crates/component-macro/tests/expanded/small-anonymous.rs +++ b/crates/component-macro/tests/expanded/small-anonymous.rs @@ -52,6 +52,17 @@ impl<_T: 'static> TheWorldPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `the-world`. /// @@ -144,12 +155,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::anon::HostWithStore, for<'a> D::Data<'a>: foo::foo::anon::Host, T: 'static, { @@ -212,6 +236,11 @@ pub mod foo { assert!(1 == < Error as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Error as wasmtime::component::ComponentType >::ALIGN32); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn option_test( &mut self, @@ -229,7 +258,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -339,7 +368,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/anon` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/small-anonymous_async.rs b/crates/component-macro/tests/expanded/small-anonymous_async.rs index 856ce69c09..c733e00f1e 100644 --- a/crates/component-macro/tests/expanded/small-anonymous_async.rs +++ b/crates/component-macro/tests/expanded/small-anonymous_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -150,12 +155,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::anon::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::anon::Host + Send, T: 'static + Send, { @@ -218,17 +236,31 @@ pub mod foo { assert!(1 == < Error as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Error as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn option_test( + fn option_test( &mut self, - ) -> Result, Error>; + ) -> impl ::core::future::Future< + Output = Result< + Option, + Error, + >, + > + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn option_test( + fn option_test( &mut self, - ) -> Result, Error> { - Host::option_test(*self).await + ) -> impl ::core::future::Future< + Output = Result< + Option, + Error, + >, + > + Send { + async move { Host::option_test(*self).await } } } pub fn add_to_linker( @@ -236,7 +268,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -348,7 +380,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/anon` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/small-anonymous_concurrent.rs b/crates/component-macro/tests/expanded/small-anonymous_concurrent.rs index df8bb972e5..6fe5843c0b 100644 --- a/crates/component-macro/tests/expanded/small-anonymous_concurrent.rs +++ b/crates/component-macro/tests/expanded/small-anonymous_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -150,12 +155,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::anon::HostConcurrent + Send, + D: foo::foo::anon::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::anon::Host + Send, T: 'static + Send, { @@ -218,8 +236,7 @@ pub mod foo { assert!(1 == < Error as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Error as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn option_test( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< @@ -227,11 +244,8 @@ pub mod foo { Option, Error, >, - > + Send - where - Self: Sized; + > + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -239,7 +253,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -249,7 +263,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::option_test(accessor).await; + let r = ::option_test(accessor).await; Ok((r,)) }) }, diff --git a/crates/component-macro/tests/expanded/small-anonymous_tracing_async.rs b/crates/component-macro/tests/expanded/small-anonymous_tracing_async.rs index f5dcee7c9b..6d772136fd 100644 --- a/crates/component-macro/tests/expanded/small-anonymous_tracing_async.rs +++ b/crates/component-macro/tests/expanded/small-anonymous_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -150,12 +155,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::anon::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::anon::Host + Send, T: 'static + Send, { @@ -218,17 +236,31 @@ pub mod foo { assert!(1 == < Error as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Error as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn option_test( + fn option_test( &mut self, - ) -> Result, Error>; + ) -> impl ::core::future::Future< + Output = Result< + Option, + Error, + >, + > + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn option_test( + fn option_test( &mut self, - ) -> Result, Error> { - Host::option_test(*self).await + ) -> impl ::core::future::Future< + Output = Result< + Option, + Error, + >, + > + Send { + async move { Host::option_test(*self).await } } } pub fn add_to_linker( @@ -236,7 +268,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -361,7 +393,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/anon` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/smoke-default.rs b/crates/component-macro/tests/expanded/smoke-default.rs index d12082f454..8339870717 100644 --- a/crates/component-macro/tests/expanded/smoke-default.rs +++ b/crates/component-macro/tests/expanded/smoke-default.rs @@ -52,6 +52,17 @@ impl<_T: 'static> TheWorldPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `the-world`. /// @@ -158,6 +169,19 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn call_y( &self, mut store: S, diff --git a/crates/component-macro/tests/expanded/smoke-default_async.rs b/crates/component-macro/tests/expanded/smoke-default_async.rs index d3e2c1122a..13b4fc07bf 100644 --- a/crates/component-macro/tests/expanded/smoke-default_async.rs +++ b/crates/component-macro/tests/expanded/smoke-default_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -143,17 +151,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -164,6 +169,19 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub async fn call_y( &self, mut store: S, diff --git a/crates/component-macro/tests/expanded/smoke-default_concurrent.rs b/crates/component-macro/tests/expanded/smoke-default_concurrent.rs index 45e983c36b..3de50eb724 100644 --- a/crates/component-macro/tests/expanded/smoke-default_concurrent.rs +++ b/crates/component-macro/tests/expanded/smoke-default_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -143,17 +151,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -164,6 +169,19 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub async fn call_y<_T, _D>( &self, accessor: &wasmtime::component::Accessor<_T, _D>, diff --git a/crates/component-macro/tests/expanded/smoke-default_tracing_async.rs b/crates/component-macro/tests/expanded/smoke-default_tracing_async.rs index 4751b4aa66..34da31174c 100644 --- a/crates/component-macro/tests/expanded/smoke-default_tracing_async.rs +++ b/crates/component-macro/tests/expanded/smoke-default_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -143,17 +151,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -164,6 +169,19 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub async fn call_y( &self, mut store: S, diff --git a/crates/component-macro/tests/expanded/smoke-export.rs b/crates/component-macro/tests/expanded/smoke-export.rs index ea8e7e959b..d3811adfd2 100644 --- a/crates/component-macro/tests/expanded/smoke-export.rs +++ b/crates/component-macro/tests/expanded/smoke-export.rs @@ -52,6 +52,17 @@ impl<_T: 'static> TheWorldPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `the-world`. /// @@ -144,6 +155,19 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn the_name(&self) -> &exports::the_name::Guest { &self.interface0 } diff --git a/crates/component-macro/tests/expanded/smoke-export_async.rs b/crates/component-macro/tests/expanded/smoke-export_async.rs index 037a5db230..14b21f578d 100644 --- a/crates/component-macro/tests/expanded/smoke-export_async.rs +++ b/crates/component-macro/tests/expanded/smoke-export_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -150,6 +155,19 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn the_name(&self) -> &exports::the_name::Guest { &self.interface0 } diff --git a/crates/component-macro/tests/expanded/smoke-export_concurrent.rs b/crates/component-macro/tests/expanded/smoke-export_concurrent.rs index e8f57104f1..f6c352a9b9 100644 --- a/crates/component-macro/tests/expanded/smoke-export_concurrent.rs +++ b/crates/component-macro/tests/expanded/smoke-export_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -150,6 +155,19 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn the_name(&self) -> &exports::the_name::Guest { &self.interface0 } diff --git a/crates/component-macro/tests/expanded/smoke-export_tracing_async.rs b/crates/component-macro/tests/expanded/smoke-export_tracing_async.rs index 32d33fc4ff..57d38d6fe1 100644 --- a/crates/component-macro/tests/expanded/smoke-export_tracing_async.rs +++ b/crates/component-macro/tests/expanded/smoke-export_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -129,17 +137,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -150,6 +155,19 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn the_name(&self) -> &exports::the_name::Guest { &self.interface0 } diff --git a/crates/component-macro/tests/expanded/smoke.rs b/crates/component-macro/tests/expanded/smoke.rs index 4e6d60dbc2..11017212a3 100644 --- a/crates/component-macro/tests/expanded/smoke.rs +++ b/crates/component-macro/tests/expanded/smoke.rs @@ -52,6 +52,17 @@ impl<_T: 'static> TheWorldPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `the-world`. /// @@ -138,12 +149,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: imports::HostWithStore, for<'a> D::Data<'a>: imports::Host, T: 'static, { @@ -156,6 +180,11 @@ const _: () = { pub mod imports { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn y(&mut self) -> (); } @@ -169,7 +198,7 @@ pub mod imports { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { diff --git a/crates/component-macro/tests/expanded/smoke_async.rs b/crates/component-macro/tests/expanded/smoke_async.rs index 7df0d39c47..bffddae5cd 100644 --- a/crates/component-macro/tests/expanded/smoke_async.rs +++ b/crates/component-macro/tests/expanded/smoke_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct TheWorldIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -144,12 +149,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: imports::HostWithStore + Send, for<'a> D::Data<'a>: imports::Host + Send, T: 'static + Send, { @@ -162,13 +180,17 @@ const _: () = { pub mod imports { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn y(&mut self) -> (); + fn y(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn y(&mut self) -> () { - Host::y(*self).await + fn y(&mut self) -> impl ::core::future::Future + Send { + async move { Host::y(*self).await } } } pub fn add_to_linker( @@ -176,7 +198,7 @@ pub mod imports { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/smoke_concurrent.rs b/crates/component-macro/tests/expanded/smoke_concurrent.rs index b56ab99c01..425d7783cd 100644 --- a/crates/component-macro/tests/expanded/smoke_concurrent.rs +++ b/crates/component-macro/tests/expanded/smoke_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct TheWorldIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -144,12 +149,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: imports::HostConcurrent + Send, + D: imports::HostWithStore + Send, for<'a> D::Data<'a>: imports::Host + Send, T: 'static + Send, { @@ -162,15 +180,11 @@ const _: () = { pub mod imports { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn y( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -178,7 +192,7 @@ pub mod imports { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -188,7 +202,7 @@ pub mod imports { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::y(accessor).await; + let r = ::y(accessor).await; Ok(r) }) }, diff --git a/crates/component-macro/tests/expanded/smoke_tracing_async.rs b/crates/component-macro/tests/expanded/smoke_tracing_async.rs index 0d15aa5e69..221f9b9bd0 100644 --- a/crates/component-macro/tests/expanded/smoke_tracing_async.rs +++ b/crates/component-macro/tests/expanded/smoke_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct TheWorldIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -144,12 +149,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: imports::HostWithStore + Send, for<'a> D::Data<'a>: imports::Host + Send, T: 'static + Send, { @@ -162,13 +180,17 @@ const _: () = { pub mod imports { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn y(&mut self) -> (); + fn y(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn y(&mut self) -> () { - Host::y(*self).await + fn y(&mut self) -> impl ::core::future::Future + Send { + async move { Host::y(*self).await } } } pub fn add_to_linker( @@ -176,7 +198,7 @@ pub mod imports { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/strings.rs b/crates/component-macro/tests/expanded/strings.rs index da6b113159..1075c2e8b0 100644 --- a/crates/component-macro/tests/expanded/strings.rs +++ b/crates/component-macro/tests/expanded/strings.rs @@ -52,6 +52,17 @@ impl<_T: 'static> TheWorldPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `the-world`. /// @@ -146,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::strings::HostWithStore, for<'a> D::Data<'a>: foo::foo::strings::Host, T: 'static, { @@ -169,6 +193,11 @@ pub mod foo { pub mod strings { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn a(&mut self, x: wasmtime::component::__internal::String) -> (); fn b(&mut self) -> wasmtime::component::__internal::String; @@ -198,7 +227,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -287,7 +316,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/strings` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/strings_async.rs b/crates/component-macro/tests/expanded/strings_async.rs index a0f1b89d72..7bb903dcef 100644 --- a/crates/component-macro/tests/expanded/strings_async.rs +++ b/crates/component-macro/tests/expanded/strings_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::strings::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::strings::Host + Send, T: 'static + Send, { @@ -175,29 +193,51 @@ pub mod foo { pub mod strings { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn a(&mut self, x: wasmtime::component::__internal::String) -> (); - async fn b(&mut self) -> wasmtime::component::__internal::String; - async fn c( + fn a( + &mut self, + x: wasmtime::component::__internal::String, + ) -> impl ::core::future::Future + Send; + fn b( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::String, + > + Send; + fn c( &mut self, a: wasmtime::component::__internal::String, b: wasmtime::component::__internal::String, - ) -> wasmtime::component::__internal::String; + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::String, + > + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn a(&mut self, x: wasmtime::component::__internal::String) -> () { - Host::a(*self, x).await + fn a( + &mut self, + x: wasmtime::component::__internal::String, + ) -> impl ::core::future::Future + Send { + async move { Host::a(*self, x).await } } - async fn b(&mut self) -> wasmtime::component::__internal::String { - Host::b(*self).await + fn b( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::String, + > + Send { + async move { Host::b(*self).await } } - async fn c( + fn c( &mut self, a: wasmtime::component::__internal::String, b: wasmtime::component::__internal::String, - ) -> wasmtime::component::__internal::String { - Host::c(*self, a, b).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::String, + > + Send { + async move { Host::c(*self, a, b).await } } } pub fn add_to_linker( @@ -205,7 +245,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -300,7 +340,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/strings` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/strings_concurrent.rs b/crates/component-macro/tests/expanded/strings_concurrent.rs index 62b5df7f43..8c47ecb736 100644 --- a/crates/component-macro/tests/expanded/strings_concurrent.rs +++ b/crates/component-macro/tests/expanded/strings_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::strings::HostConcurrent + Send, + D: foo::foo::strings::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::strings::Host + Send, T: 'static + Send, { @@ -175,32 +193,24 @@ pub mod foo { pub mod strings { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn a( accessor: &wasmtime::component::Accessor, x: wasmtime::component::__internal::String, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn b( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::String, - > + Send - where - Self: Sized; + > + Send; fn c( accessor: &wasmtime::component::Accessor, a: wasmtime::component::__internal::String, b: wasmtime::component::__internal::String, ) -> impl ::core::future::Future< Output = wasmtime::component::__internal::String, - > + Send - where - Self: Sized; + > + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -208,7 +218,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -221,7 +231,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::a(accessor, arg0).await; + let r = ::a(accessor, arg0).await; Ok(r) }) }, @@ -231,7 +241,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::b(accessor).await; + let r = ::b(accessor).await; Ok((r,)) }) }, @@ -250,7 +260,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::c(accessor, arg0, arg1).await; + let r = ::c(accessor, arg0, arg1).await; Ok((r,)) }) }, diff --git a/crates/component-macro/tests/expanded/strings_tracing_async.rs b/crates/component-macro/tests/expanded/strings_tracing_async.rs index 94d0a8146f..ff93e28993 100644 --- a/crates/component-macro/tests/expanded/strings_tracing_async.rs +++ b/crates/component-macro/tests/expanded/strings_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct TheWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::strings::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::strings::Host + Send, T: 'static + Send, { @@ -175,29 +193,51 @@ pub mod foo { pub mod strings { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn a(&mut self, x: wasmtime::component::__internal::String) -> (); - async fn b(&mut self) -> wasmtime::component::__internal::String; - async fn c( + fn a( + &mut self, + x: wasmtime::component::__internal::String, + ) -> impl ::core::future::Future + Send; + fn b( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::String, + > + Send; + fn c( &mut self, a: wasmtime::component::__internal::String, b: wasmtime::component::__internal::String, - ) -> wasmtime::component::__internal::String; + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::String, + > + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn a(&mut self, x: wasmtime::component::__internal::String) -> () { - Host::a(*self, x).await + fn a( + &mut self, + x: wasmtime::component::__internal::String, + ) -> impl ::core::future::Future + Send { + async move { Host::a(*self, x).await } } - async fn b(&mut self) -> wasmtime::component::__internal::String { - Host::b(*self).await + fn b( + &mut self, + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::String, + > + Send { + async move { Host::b(*self).await } } - async fn c( + fn c( &mut self, a: wasmtime::component::__internal::String, b: wasmtime::component::__internal::String, - ) -> wasmtime::component::__internal::String { - Host::c(*self, a, b).await + ) -> impl ::core::future::Future< + Output = wasmtime::component::__internal::String, + > + Send { + async move { Host::c(*self, a, b).await } } } pub fn add_to_linker( @@ -205,7 +245,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -345,7 +385,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/strings` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/unstable-features.rs b/crates/component-macro/tests/expanded/unstable-features.rs index 62347bc83e..30fa6778c6 100644 --- a/crates/component-macro/tests/expanded/unstable-features.rs +++ b/crates/component-macro/tests/expanded/unstable-features.rs @@ -79,6 +79,11 @@ impl core::convert::From<&LinkOptions> for foo::foo::the_interface::LinkOptions } } pub enum Baz {} +pub trait HostBazWithStore: wasmtime::component::HasData {} +impl<_T: ?Sized> HostBazWithStore for _T +where + _T: wasmtime::component::HasData, +{} pub trait HostBaz { fn foo(&mut self, self_: wasmtime::component::Resource) -> (); fn drop(&mut self, rep: wasmtime::component::Resource) -> wasmtime::Result<()>; @@ -145,6 +150,17 @@ impl<_T: 'static> TheWorldPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `the-world`. /// @@ -180,6 +196,11 @@ pub struct TheWorldIndices {} /// [`Component`]: wasmtime::component::Component /// [`Linker`]: wasmtime::component::Linker pub struct TheWorld {} +pub trait TheWorldImportsWithStore: wasmtime::component::HasData + HostBazWithStore {} +impl<_T: ?Sized> TheWorldImportsWithStore for _T +where + _T: wasmtime::component::HasData + HostBazWithStore, +{} pub trait TheWorldImports: HostBaz { fn foo(&mut self) -> (); } @@ -239,13 +260,26 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker_imports( linker: &mut wasmtime::component::Linker, options: &LinkOptions, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: TheWorldImportsWithStore, for<'a> D::Data<'a>: TheWorldImports, T: 'static, { @@ -298,7 +332,7 @@ const _: () = { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::the_interface::HostWithStore + TheWorldImportsWithStore, for<'a> D::Data<'a>: foo::foo::the_interface::Host + TheWorldImports, T: 'static, { @@ -361,6 +395,11 @@ pub mod foo { } } pub enum Bar {} + pub trait HostBarWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostBarWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait HostBar { fn foo(&mut self, self_: wasmtime::component::Resource) -> (); fn drop( @@ -379,6 +418,11 @@ pub mod foo { HostBar::drop(*self, rep) } } + pub trait HostWithStore: wasmtime::component::HasData + HostBarWithStore {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostBarWithStore, + {} pub trait Host: HostBar { fn foo(&mut self) -> (); } @@ -393,7 +437,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { diff --git a/crates/component-macro/tests/expanded/unstable-features_async.rs b/crates/component-macro/tests/expanded/unstable-features_async.rs index 18cf2e9db4..9e3ad87967 100644 --- a/crates/component-macro/tests/expanded/unstable-features_async.rs +++ b/crates/component-macro/tests/expanded/unstable-features_async.rs @@ -79,17 +79,27 @@ impl core::convert::From<&LinkOptions> for foo::foo::the_interface::LinkOptions } } pub enum Baz {} -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] +pub trait HostBazWithStore: wasmtime::component::HasData + Send {} +impl<_T: ?Sized> HostBazWithStore for _T +where + _T: wasmtime::component::HasData + Send, +{} pub trait HostBaz: Send { - async fn foo(&mut self, self_: wasmtime::component::Resource) -> (); - async fn drop( + fn foo( + &mut self, + self_: wasmtime::component::Resource, + ) -> impl ::core::future::Future + Send; + fn drop( &mut self, rep: wasmtime::component::Resource, - ) -> wasmtime::Result<()>; + ) -> impl ::core::future::Future> + Send; } impl<_T: HostBaz + ?Sized + Send> HostBaz for &mut _T { - async fn foo(&mut self, self_: wasmtime::component::Resource) -> () { - HostBaz::foo(*self, self_).await + fn foo( + &mut self, + self_: wasmtime::component::Resource, + ) -> impl ::core::future::Future + Send { + async move { HostBaz::foo(*self, self_).await } } async fn drop( &mut self, @@ -143,13 +153,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -171,13 +189,13 @@ pub struct TheWorldIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -190,13 +208,17 @@ pub struct TheWorldIndices {} /// [`Component`]: wasmtime::component::Component /// [`Linker`]: wasmtime::component::Linker pub struct TheWorld {} -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] -pub trait TheWorldImports: Send + HostBaz { - async fn foo(&mut self) -> (); +pub trait TheWorldImportsWithStore: wasmtime::component::HasData + HostBazWithStore + Send {} +impl<_T: ?Sized> TheWorldImportsWithStore for _T +where + _T: wasmtime::component::HasData + HostBazWithStore + Send, +{} +pub trait TheWorldImports: HostBaz + Send { + fn foo(&mut self) -> impl ::core::future::Future + Send; } impl<_T: TheWorldImports + ?Sized + Send> TheWorldImports for &mut _T { - async fn foo(&mut self) -> () { - TheWorldImports::foo(*self).await + fn foo(&mut self) -> impl ::core::future::Future + Send { + async move { TheWorldImports::foo(*self).await } } } const _: () = { @@ -232,17 +254,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -253,13 +272,26 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker_imports( linker: &mut wasmtime::component::Linker, options: &LinkOptions, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: TheWorldImportsWithStore, for<'a> D::Data<'a>: TheWorldImports, T: 'static + Send, { @@ -319,7 +351,7 @@ const _: () = { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::the_interface::HostWithStore + TheWorldImportsWithStore + Send, for<'a> D::Data<'a>: foo::foo::the_interface::Host + TheWorldImports + Send, T: 'static + Send, { @@ -382,20 +414,27 @@ pub mod foo { } } pub enum Bar {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostBarWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostBarWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait HostBar: Send { - async fn foo(&mut self, self_: wasmtime::component::Resource) -> (); - async fn drop( + fn foo( + &mut self, + self_: wasmtime::component::Resource, + ) -> impl ::core::future::Future + Send; + fn drop( &mut self, rep: wasmtime::component::Resource, - ) -> wasmtime::Result<()>; + ) -> impl ::core::future::Future> + Send; } impl<_T: HostBar + ?Sized + Send> HostBar for &mut _T { - async fn foo( + fn foo( &mut self, self_: wasmtime::component::Resource, - ) -> () { - HostBar::foo(*self, self_).await + ) -> impl ::core::future::Future + Send { + async move { HostBar::foo(*self, self_).await } } async fn drop( &mut self, @@ -404,13 +443,17 @@ pub mod foo { HostBar::drop(*self, rep).await } } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostBar { - async fn foo(&mut self) -> (); + pub trait HostWithStore: wasmtime::component::HasData + HostBarWithStore + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostBarWithStore + Send, + {} + pub trait Host: HostBar + Send { + fn foo(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn foo(&mut self) -> () { - Host::foo(*self).await + fn foo(&mut self) -> impl ::core::future::Future + Send { + async move { Host::foo(*self).await } } } pub fn add_to_linker( @@ -419,7 +462,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/unstable-features_concurrent.rs b/crates/component-macro/tests/expanded/unstable-features_concurrent.rs index f9f1f00a58..a45fe341cd 100644 --- a/crates/component-macro/tests/expanded/unstable-features_concurrent.rs +++ b/crates/component-macro/tests/expanded/unstable-features_concurrent.rs @@ -79,22 +79,18 @@ impl core::convert::From<&LinkOptions> for foo::foo::the_interface::LinkOptions } } pub enum Baz {} -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] -pub trait HostBazConcurrent: wasmtime::component::HasData + Send { - fn foo( - accessor: &wasmtime::component::Accessor, - self_: wasmtime::component::Resource, - ) -> impl ::core::future::Future + Send - where - Self: Sized; +pub trait HostBazWithStore: wasmtime::component::HasData + Send { fn drop( accessor: &wasmtime::component::Accessor, rep: wasmtime::component::Resource, ) -> impl ::core::future::Future> + Send where Self: Sized; + fn foo( + accessor: &wasmtime::component::Accessor, + self_: wasmtime::component::Resource, + ) -> impl ::core::future::Future + Send; } -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait HostBaz: Send {} impl<_T: HostBaz + ?Sized + Send> HostBaz for &mut _T {} /// Auto-generated bindings for a pre-instantiated version of a @@ -142,13 +138,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -170,13 +174,13 @@ pub struct TheWorldIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -189,16 +193,12 @@ pub struct TheWorldIndices {} /// [`Component`]: wasmtime::component::Component /// [`Linker`]: wasmtime::component::Linker pub struct TheWorld {} -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] -pub trait TheWorldImportsConcurrent: wasmtime::component::HasData + Send + HostBazConcurrent { +pub trait TheWorldImportsWithStore: wasmtime::component::HasData + HostBazWithStore + Send { fn foo( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] -pub trait TheWorldImports: Send + HostBaz {} +pub trait TheWorldImports: HostBaz + Send {} impl<_T: TheWorldImports + ?Sized + Send> TheWorldImports for &mut _T {} const _: () = { #[allow(unused_imports)] @@ -233,17 +233,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -254,13 +251,26 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker_imports( linker: &mut wasmtime::component::Linker, options: &LinkOptions, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: TheWorldImportsConcurrent, + D: TheWorldImportsWithStore, for<'a> D::Data<'a>: TheWorldImports, T: 'static + Send, { @@ -274,7 +284,7 @@ const _: () = { move |caller: &wasmtime::component::Accessor, rep| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - HostBazConcurrent::drop( + HostBazWithStore::drop( accessor, wasmtime::component::Resource::new_own(rep), ) @@ -290,7 +300,7 @@ const _: () = { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::foo(accessor) + let r = ::foo(accessor) .await; Ok(r) }) @@ -307,7 +317,7 @@ const _: () = { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::foo(accessor, arg0).await; + let r = ::foo(accessor, arg0).await; Ok(r) }) }, @@ -322,8 +332,7 @@ const _: () = { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::the_interface::HostConcurrent + TheWorldImportsConcurrent - + Send, + D: foo::foo::the_interface::HostWithStore + TheWorldImportsWithStore + Send, for<'a> D::Data<'a>: foo::foo::the_interface::Host + TheWorldImports + Send, T: 'static + Send, { @@ -386,34 +395,26 @@ pub mod foo { } } pub enum Bar {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostBarConcurrent: wasmtime::component::HasData + Send { - fn foo( - accessor: &wasmtime::component::Accessor, - self_: wasmtime::component::Resource, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + pub trait HostBarWithStore: wasmtime::component::HasData + Send { fn drop( accessor: &wasmtime::component::Accessor, rep: wasmtime::component::Resource, ) -> impl ::core::future::Future> + Send where Self: Sized; + fn foo( + accessor: &wasmtime::component::Accessor, + self_: wasmtime::component::Resource, + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait HostBar: Send {} impl<_T: HostBar + ?Sized + Send> HostBar for &mut _T {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send + HostBarConcurrent { + pub trait HostWithStore: wasmtime::component::HasData + HostBarWithStore + Send { fn foo( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostBar {} + pub trait Host: HostBar + Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, @@ -421,7 +422,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -434,7 +435,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, rep| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - HostBarConcurrent::drop( + HostBarWithStore::drop( accessor, wasmtime::component::Resource::new_own(rep), ) @@ -449,7 +450,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::foo(accessor).await; + let r = ::foo(accessor).await; Ok(r) }) }, @@ -464,7 +465,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::foo(accessor, arg0).await; + let r = ::foo(accessor, arg0).await; Ok(r) }) }, diff --git a/crates/component-macro/tests/expanded/unstable-features_tracing_async.rs b/crates/component-macro/tests/expanded/unstable-features_tracing_async.rs index 7e9cd4d291..4b651cdcdc 100644 --- a/crates/component-macro/tests/expanded/unstable-features_tracing_async.rs +++ b/crates/component-macro/tests/expanded/unstable-features_tracing_async.rs @@ -79,17 +79,27 @@ impl core::convert::From<&LinkOptions> for foo::foo::the_interface::LinkOptions } } pub enum Baz {} -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] +pub trait HostBazWithStore: wasmtime::component::HasData + Send {} +impl<_T: ?Sized> HostBazWithStore for _T +where + _T: wasmtime::component::HasData + Send, +{} pub trait HostBaz: Send { - async fn foo(&mut self, self_: wasmtime::component::Resource) -> (); - async fn drop( + fn foo( + &mut self, + self_: wasmtime::component::Resource, + ) -> impl ::core::future::Future + Send; + fn drop( &mut self, rep: wasmtime::component::Resource, - ) -> wasmtime::Result<()>; + ) -> impl ::core::future::Future> + Send; } impl<_T: HostBaz + ?Sized + Send> HostBaz for &mut _T { - async fn foo(&mut self, self_: wasmtime::component::Resource) -> () { - HostBaz::foo(*self, self_).await + fn foo( + &mut self, + self_: wasmtime::component::Resource, + ) -> impl ::core::future::Future + Send { + async move { HostBaz::foo(*self, self_).await } } async fn drop( &mut self, @@ -143,13 +153,21 @@ impl<_T: 'static> TheWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -171,13 +189,13 @@ pub struct TheWorldIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`TheWorld::instantiate_async`] which only needs a +/// [`TheWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`TheWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`TheWorldPre::instantiate_async`] to +/// method then uses [`TheWorldPre::instantiate`] to /// create a [`TheWorld`]. /// /// * If you've instantiated the instance yourself already @@ -190,13 +208,17 @@ pub struct TheWorldIndices {} /// [`Component`]: wasmtime::component::Component /// [`Linker`]: wasmtime::component::Linker pub struct TheWorld {} -#[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] -pub trait TheWorldImports: Send + HostBaz { - async fn foo(&mut self) -> (); +pub trait TheWorldImportsWithStore: wasmtime::component::HasData + HostBazWithStore + Send {} +impl<_T: ?Sized> TheWorldImportsWithStore for _T +where + _T: wasmtime::component::HasData + HostBazWithStore + Send, +{} +pub trait TheWorldImports: HostBaz + Send { + fn foo(&mut self) -> impl ::core::future::Future + Send; } impl<_T: TheWorldImports + ?Sized + Send> TheWorldImports for &mut _T { - async fn foo(&mut self) -> () { - TheWorldImports::foo(*self).await + fn foo(&mut self) -> impl ::core::future::Future + Send { + async move { TheWorldImports::foo(*self).await } } } const _: () = { @@ -232,17 +254,14 @@ const _: () = { } impl TheWorld { /// Convenience wrapper around [`TheWorldPre::new`] and - /// [`TheWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`TheWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - TheWorldPre::new(pre)?.instantiate_async(store).await + TheWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`TheWorldIndices::new`] and /// [`TheWorldIndices::load`]. @@ -253,13 +272,26 @@ const _: () = { let indices = TheWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker_imports( linker: &mut wasmtime::component::Linker, options: &LinkOptions, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: TheWorldImportsWithStore, for<'a> D::Data<'a>: TheWorldImports, T: 'static + Send, { @@ -348,7 +380,7 @@ const _: () = { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::the_interface::HostWithStore + TheWorldImportsWithStore + Send, for<'a> D::Data<'a>: foo::foo::the_interface::Host + TheWorldImports + Send, T: 'static + Send, { @@ -411,20 +443,27 @@ pub mod foo { } } pub enum Bar {} - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostBarWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostBarWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait HostBar: Send { - async fn foo(&mut self, self_: wasmtime::component::Resource) -> (); - async fn drop( + fn foo( + &mut self, + self_: wasmtime::component::Resource, + ) -> impl ::core::future::Future + Send; + fn drop( &mut self, rep: wasmtime::component::Resource, - ) -> wasmtime::Result<()>; + ) -> impl ::core::future::Future> + Send; } impl<_T: HostBar + ?Sized + Send> HostBar for &mut _T { - async fn foo( + fn foo( &mut self, self_: wasmtime::component::Resource, - ) -> () { - HostBar::foo(*self, self_).await + ) -> impl ::core::future::Future + Send { + async move { HostBar::foo(*self, self_).await } } async fn drop( &mut self, @@ -433,13 +472,17 @@ pub mod foo { HostBar::drop(*self, rep).await } } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostBar { - async fn foo(&mut self) -> (); + pub trait HostWithStore: wasmtime::component::HasData + HostBarWithStore + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + HostBarWithStore + Send, + {} + pub trait Host: HostBar + Send { + fn foo(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn foo(&mut self) -> () { - Host::foo(*self).await + fn foo(&mut self) -> impl ::core::future::Future + Send { + async move { Host::foo(*self).await } } } pub fn add_to_linker( @@ -448,7 +491,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/unversioned-foo.rs b/crates/component-macro/tests/expanded/unversioned-foo.rs index 3f9ec1626d..c95d2985a2 100644 --- a/crates/component-macro/tests/expanded/unversioned-foo.rs +++ b/crates/component-macro/tests/expanded/unversioned-foo.rs @@ -52,6 +52,17 @@ impl<_T: 'static> NopePre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> NopePre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `nope`. /// @@ -138,12 +149,25 @@ const _: () = { let indices = NopeIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`NopePre::new`] and + /// [`NopePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + NopePre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::a::HostWithStore, for<'a> D::Data<'a>: foo::foo::a::Host, T: 'static, { @@ -186,6 +210,11 @@ pub mod foo { assert!(12 == < Error as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Error as wasmtime::component::ComponentType >::ALIGN32); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn g(&mut self) -> Result<(), Error>; } @@ -199,7 +228,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { diff --git a/crates/component-macro/tests/expanded/unversioned-foo_async.rs b/crates/component-macro/tests/expanded/unversioned-foo_async.rs index 6ccfdd7555..38aea6b99e 100644 --- a/crates/component-macro/tests/expanded/unversioned-foo_async.rs +++ b/crates/component-macro/tests/expanded/unversioned-foo_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> NopePre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> NopePre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct NopeIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Nope::instantiate_async`] which only needs a +/// [`Nope::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`NopePre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`NopePre::instantiate_async`] to +/// method then uses [`NopePre::instantiate`] to /// create a [`Nope`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Nope { /// Convenience wrapper around [`NopePre::new`] and - /// [`NopePre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`NopePre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - NopePre::new(pre)?.instantiate_async(store).await + NopePre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`NopeIndices::new`] and /// [`NopeIndices::load`]. @@ -144,12 +149,25 @@ const _: () = { let indices = NopeIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`NopePre::new`] and + /// [`NopePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + NopePre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::a::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::a::Host + Send, T: 'static + Send, { @@ -192,13 +210,21 @@ pub mod foo { assert!(12 == < Error as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Error as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn g(&mut self) -> Result<(), Error>; + fn g( + &mut self, + ) -> impl ::core::future::Future> + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn g(&mut self) -> Result<(), Error> { - Host::g(*self).await + fn g( + &mut self, + ) -> impl ::core::future::Future> + Send { + async move { Host::g(*self).await } } } pub fn add_to_linker( @@ -206,7 +232,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/unversioned-foo_concurrent.rs b/crates/component-macro/tests/expanded/unversioned-foo_concurrent.rs index 92876736d4..096e97ccac 100644 --- a/crates/component-macro/tests/expanded/unversioned-foo_concurrent.rs +++ b/crates/component-macro/tests/expanded/unversioned-foo_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> NopePre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> NopePre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct NopeIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Nope::instantiate_async`] which only needs a +/// [`Nope::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`NopePre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`NopePre::instantiate_async`] to +/// method then uses [`NopePre::instantiate`] to /// create a [`Nope`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Nope { /// Convenience wrapper around [`NopePre::new`] and - /// [`NopePre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`NopePre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - NopePre::new(pre)?.instantiate_async(store).await + NopePre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`NopeIndices::new`] and /// [`NopeIndices::load`]. @@ -144,12 +149,25 @@ const _: () = { let indices = NopeIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`NopePre::new`] and + /// [`NopePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + NopePre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::a::HostConcurrent + Send, + D: foo::foo::a::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::a::Host + Send, T: 'static + Send, { @@ -192,15 +210,11 @@ pub mod foo { assert!(12 == < Error as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Error as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn g( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future> + Send - where - Self: Sized; + ) -> impl ::core::future::Future> + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -208,7 +222,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -218,7 +232,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::g(accessor).await; + let r = ::g(accessor).await; Ok((r,)) }) }, diff --git a/crates/component-macro/tests/expanded/unversioned-foo_tracing_async.rs b/crates/component-macro/tests/expanded/unversioned-foo_tracing_async.rs index 091aa5178f..d64a48942b 100644 --- a/crates/component-macro/tests/expanded/unversioned-foo_tracing_async.rs +++ b/crates/component-macro/tests/expanded/unversioned-foo_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> NopePre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> NopePre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct NopeIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Nope::instantiate_async`] which only needs a +/// [`Nope::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`NopePre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`NopePre::instantiate_async`] to +/// method then uses [`NopePre::instantiate`] to /// create a [`Nope`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl Nope { /// Convenience wrapper around [`NopePre::new`] and - /// [`NopePre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`NopePre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - NopePre::new(pre)?.instantiate_async(store).await + NopePre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`NopeIndices::new`] and /// [`NopeIndices::load`]. @@ -144,12 +149,25 @@ const _: () = { let indices = NopeIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`NopePre::new`] and + /// [`NopePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + NopePre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::a::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::a::Host + Send, T: 'static + Send, { @@ -192,13 +210,21 @@ pub mod foo { assert!(12 == < Error as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < Error as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn g(&mut self) -> Result<(), Error>; + fn g( + &mut self, + ) -> impl ::core::future::Future> + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn g(&mut self) -> Result<(), Error> { - Host::g(*self).await + fn g( + &mut self, + ) -> impl ::core::future::Future> + Send { + async move { Host::g(*self).await } } } pub fn add_to_linker( @@ -206,7 +232,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/use-paths.rs b/crates/component-macro/tests/expanded/use-paths.rs index 3964840701..f4d3056806 100644 --- a/crates/component-macro/tests/expanded/use-paths.rs +++ b/crates/component-macro/tests/expanded/use-paths.rs @@ -52,6 +52,17 @@ impl<_T: 'static> DPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> DPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `d`. /// @@ -138,12 +149,26 @@ const _: () = { let indices = DIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`DPre::new`] and + /// [`DPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + DPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::a::HostWithStore + foo::foo::b::HostWithStore + + foo::foo::c::HostWithStore + d::HostWithStore, for<'a> D::Data< 'a, >: foo::foo::a::Host + foo::foo::b::Host + foo::foo::c::Host + d::Host, @@ -178,6 +203,11 @@ pub mod foo { assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn a(&mut self) -> Foo; } @@ -191,7 +221,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -216,6 +246,11 @@ pub mod foo { assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn a(&mut self) -> Foo; } @@ -229,7 +264,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -254,6 +289,11 @@ pub mod foo { assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn a(&mut self) -> Foo; } @@ -267,7 +307,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -294,6 +334,11 @@ pub mod d { assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn b(&mut self) -> Foo; } @@ -307,7 +352,7 @@ pub mod d { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { diff --git a/crates/component-macro/tests/expanded/use-paths_async.rs b/crates/component-macro/tests/expanded/use-paths_async.rs index 7f9068fb59..83a377cfcb 100644 --- a/crates/component-macro/tests/expanded/use-paths_async.rs +++ b/crates/component-macro/tests/expanded/use-paths_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> DPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> DPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct DIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`D::instantiate_async`] which only needs a +/// [`D::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`DPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`DPre::instantiate_async`] to +/// method then uses [`DPre::instantiate`] to /// create a [`D`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl D { /// Convenience wrapper around [`DPre::new`] and - /// [`DPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`DPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - DPre::new(pre)?.instantiate_async(store).await + DPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`DIndices::new`] and /// [`DIndices::load`]. @@ -144,12 +149,26 @@ const _: () = { let indices = DIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`DPre::new`] and + /// [`DPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + DPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::a::HostWithStore + foo::foo::b::HostWithStore + + foo::foo::c::HostWithStore + d::HostWithStore + Send, for<'a> D::Data< 'a, >: foo::foo::a::Host + foo::foo::b::Host + foo::foo::c::Host + d::Host @@ -185,13 +204,17 @@ pub mod foo { assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn a(&mut self) -> Foo; + fn a(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn a(&mut self) -> Foo { - Host::a(*self).await + fn a(&mut self) -> impl ::core::future::Future + Send { + async move { Host::a(*self).await } } } pub fn add_to_linker( @@ -199,7 +222,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -226,13 +249,17 @@ pub mod foo { assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn a(&mut self) -> Foo; + fn a(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn a(&mut self) -> Foo { - Host::a(*self).await + fn a(&mut self) -> impl ::core::future::Future + Send { + async move { Host::a(*self).await } } } pub fn add_to_linker( @@ -240,7 +267,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -267,13 +294,17 @@ pub mod foo { assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn a(&mut self) -> Foo; + fn a(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn a(&mut self) -> Foo { - Host::a(*self).await + fn a(&mut self) -> impl ::core::future::Future + Send { + async move { Host::a(*self).await } } } pub fn add_to_linker( @@ -281,7 +312,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -310,13 +341,17 @@ pub mod d { assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn b(&mut self) -> Foo; + fn b(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn b(&mut self) -> Foo { - Host::b(*self).await + fn b(&mut self) -> impl ::core::future::Future + Send { + async move { Host::b(*self).await } } } pub fn add_to_linker( @@ -324,7 +359,7 @@ pub mod d { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/use-paths_concurrent.rs b/crates/component-macro/tests/expanded/use-paths_concurrent.rs index 867cfcc6ec..ef8f1f4cac 100644 --- a/crates/component-macro/tests/expanded/use-paths_concurrent.rs +++ b/crates/component-macro/tests/expanded/use-paths_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> DPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> DPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct DIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`D::instantiate_async`] which only needs a +/// [`D::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`DPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`DPre::instantiate_async`] to +/// method then uses [`DPre::instantiate`] to /// create a [`D`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl D { /// Convenience wrapper around [`DPre::new`] and - /// [`DPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`DPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - DPre::new(pre)?.instantiate_async(store).await + DPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`DIndices::new`] and /// [`DIndices::load`]. @@ -144,13 +149,26 @@ const _: () = { let indices = DIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`DPre::new`] and + /// [`DPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + DPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::a::HostConcurrent + foo::foo::b::HostConcurrent - + foo::foo::c::HostConcurrent + d::HostConcurrent + Send, + D: foo::foo::a::HostWithStore + foo::foo::b::HostWithStore + + foo::foo::c::HostWithStore + d::HostWithStore + Send, for<'a> D::Data< 'a, >: foo::foo::a::Host + foo::foo::b::Host + foo::foo::c::Host + d::Host @@ -186,15 +204,11 @@ pub mod foo { assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn a( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -202,7 +216,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -212,7 +226,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::a(accessor).await; + let r = ::a(accessor).await; Ok((r,)) }) }, @@ -229,15 +243,11 @@ pub mod foo { assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn a( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -245,7 +255,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -255,7 +265,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::a(accessor).await; + let r = ::a(accessor).await; Ok((r,)) }) }, @@ -272,15 +282,11 @@ pub mod foo { assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn a( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -288,7 +294,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -298,7 +304,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::a(accessor).await; + let r = ::a(accessor).await; Ok((r,)) }) }, @@ -317,15 +323,11 @@ pub mod d { assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn b( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -333,7 +335,7 @@ pub mod d { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -343,7 +345,7 @@ pub mod d { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::b(accessor).await; + let r = ::b(accessor).await; Ok((r,)) }) }, diff --git a/crates/component-macro/tests/expanded/use-paths_tracing_async.rs b/crates/component-macro/tests/expanded/use-paths_tracing_async.rs index 624d8b4f45..bb5596886e 100644 --- a/crates/component-macro/tests/expanded/use-paths_tracing_async.rs +++ b/crates/component-macro/tests/expanded/use-paths_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> DPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> DPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -71,13 +79,13 @@ pub struct DIndices {} /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`D::instantiate_async`] which only needs a +/// [`D::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`DPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`DPre::instantiate_async`] to +/// method then uses [`DPre::instantiate`] to /// create a [`D`]. /// /// * If you've instantiated the instance yourself already @@ -123,17 +131,14 @@ const _: () = { } impl D { /// Convenience wrapper around [`DPre::new`] and - /// [`DPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`DPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - DPre::new(pre)?.instantiate_async(store).await + DPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`DIndices::new`] and /// [`DIndices::load`]. @@ -144,12 +149,26 @@ const _: () = { let indices = DIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`DPre::new`] and + /// [`DPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + DPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::a::HostWithStore + foo::foo::b::HostWithStore + + foo::foo::c::HostWithStore + d::HostWithStore + Send, for<'a> D::Data< 'a, >: foo::foo::a::Host + foo::foo::b::Host + foo::foo::c::Host + d::Host @@ -185,13 +204,17 @@ pub mod foo { assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn a(&mut self) -> Foo; + fn a(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn a(&mut self) -> Foo { - Host::a(*self).await + fn a(&mut self) -> impl ::core::future::Future + Send { + async move { Host::a(*self).await } } } pub fn add_to_linker( @@ -199,7 +222,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -239,13 +262,17 @@ pub mod foo { assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn a(&mut self) -> Foo; + fn a(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn a(&mut self) -> Foo { - Host::a(*self).await + fn a(&mut self) -> impl ::core::future::Future + Send { + async move { Host::a(*self).await } } } pub fn add_to_linker( @@ -253,7 +280,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -293,13 +320,17 @@ pub mod foo { assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn a(&mut self) -> Foo; + fn a(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn a(&mut self) -> Foo { - Host::a(*self).await + fn a(&mut self) -> impl ::core::future::Future + Send { + async move { Host::a(*self).await } } } pub fn add_to_linker( @@ -307,7 +338,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -349,13 +380,17 @@ pub mod d { assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn b(&mut self) -> Foo; + fn b(&mut self) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn b(&mut self) -> Foo { - Host::b(*self).await + fn b(&mut self) -> impl ::core::future::Future + Send { + async move { Host::b(*self).await } } } pub fn add_to_linker( @@ -363,7 +398,7 @@ pub mod d { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { diff --git a/crates/component-macro/tests/expanded/variants.rs b/crates/component-macro/tests/expanded/variants.rs index f54b873380..faf367221f 100644 --- a/crates/component-macro/tests/expanded/variants.rs +++ b/crates/component-macro/tests/expanded/variants.rs @@ -52,6 +52,17 @@ impl<_T: 'static> MyWorldPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> MyWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `my-world`. /// @@ -146,12 +157,25 @@ const _: () = { let indices = MyWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`MyWorldPre::new`] and + /// [`MyWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + MyWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::variants::HostWithStore, for<'a> D::Data<'a>: foo::foo::variants::Host, T: 'static, { @@ -441,6 +465,11 @@ pub mod foo { assert!(12 == < IsClone as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < IsClone as wasmtime::component::ComponentType >::ALIGN32); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host { fn e1_arg(&mut self, x: E1) -> (); fn e1_result(&mut self) -> E1; @@ -626,7 +655,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { @@ -1286,7 +1315,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/variants` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/variants_async.rs b/crates/component-macro/tests/expanded/variants_async.rs index 97e29945f4..c8cd1a062d 100644 --- a/crates/component-macro/tests/expanded/variants_async.rs +++ b/crates/component-macro/tests/expanded/variants_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> MyWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> MyWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct MyWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`MyWorld::instantiate_async`] which only needs a +/// [`MyWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`MyWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`MyWorldPre::instantiate_async`] to +/// method then uses [`MyWorldPre::instantiate`] to /// create a [`MyWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl MyWorld { /// Convenience wrapper around [`MyWorldPre::new`] and - /// [`MyWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`MyWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - MyWorldPre::new(pre)?.instantiate_async(store).await + MyWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`MyWorldIndices::new`] and /// [`MyWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = MyWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`MyWorldPre::new`] and + /// [`MyWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + MyWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::variants::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::variants::Host + Send, T: 'static + Send, { @@ -447,15 +465,34 @@ pub mod foo { assert!(12 == < IsClone as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < IsClone as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn e1_arg(&mut self, x: E1) -> (); - async fn e1_result(&mut self) -> E1; - async fn v1_arg(&mut self, x: V1) -> (); - async fn v1_result(&mut self) -> V1; - async fn bool_arg(&mut self, x: bool) -> (); - async fn bool_result(&mut self) -> bool; - async fn option_arg( + fn e1_arg( + &mut self, + x: E1, + ) -> impl ::core::future::Future + Send; + fn e1_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn v1_arg( + &mut self, + x: V1, + ) -> impl ::core::future::Future + Send; + fn v1_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn bool_arg( + &mut self, + x: bool, + ) -> impl ::core::future::Future + Send; + fn bool_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn option_arg( &mut self, a: Option, b: Option<()>, @@ -463,18 +500,20 @@ pub mod foo { d: Option, e: Option, g: Option>, - ) -> (); - async fn option_result( + ) -> impl ::core::future::Future + Send; + fn option_result( &mut self, - ) -> ( - Option, - Option<()>, - Option, - Option, - Option, - Option>, - ); - async fn casts( + ) -> impl ::core::future::Future< + Output = ( + Option, + Option<()>, + Option, + Option, + Option, + Option>, + ), + > + Send; + fn casts( &mut self, a: Casts1, b: Casts2, @@ -482,8 +521,10 @@ pub mod foo { d: Casts4, e: Casts5, f: Casts6, - ) -> (Casts1, Casts2, Casts3, Casts4, Casts5, Casts6); - async fn result_arg( + ) -> impl ::core::future::Future< + Output = (Casts1, Casts2, Casts3, Casts4, Casts5, Casts6), + > + Send; + fn result_arg( &mut self, a: Result<(), ()>, b: Result<(), E1>, @@ -494,50 +535,90 @@ pub mod foo { wasmtime::component::__internal::String, wasmtime::component::__internal::Vec, >, - ) -> (); - async fn result_result( + ) -> impl ::core::future::Future + Send; + fn result_result( &mut self, - ) -> ( - Result<(), ()>, - Result<(), E1>, - Result, - Result<(), ()>, - Result, - Result< - wasmtime::component::__internal::String, - wasmtime::component::__internal::Vec, - >, - ); - async fn return_result_sugar(&mut self) -> Result; - async fn return_result_sugar2(&mut self) -> Result<(), MyErrno>; - async fn return_result_sugar3(&mut self) -> Result; - async fn return_result_sugar4(&mut self) -> Result<(i32, u32), MyErrno>; - async fn return_option_sugar(&mut self) -> Option; - async fn return_option_sugar2(&mut self) -> Option; - async fn result_simple(&mut self) -> Result; - async fn is_clone_arg(&mut self, a: IsClone) -> (); - async fn is_clone_return(&mut self) -> IsClone; + ) -> impl ::core::future::Future< + Output = ( + Result<(), ()>, + Result<(), E1>, + Result, + Result<(), ()>, + Result, + Result< + wasmtime::component::__internal::String, + wasmtime::component::__internal::Vec, + >, + ), + > + Send; + fn return_result_sugar( + &mut self, + ) -> impl ::core::future::Future> + Send; + fn return_result_sugar2( + &mut self, + ) -> impl ::core::future::Future> + Send; + fn return_result_sugar3( + &mut self, + ) -> impl ::core::future::Future< + Output = Result, + > + Send; + fn return_result_sugar4( + &mut self, + ) -> impl ::core::future::Future< + Output = Result<(i32, u32), MyErrno>, + > + Send; + fn return_option_sugar( + &mut self, + ) -> impl ::core::future::Future> + Send; + fn return_option_sugar2( + &mut self, + ) -> impl ::core::future::Future> + Send; + fn result_simple( + &mut self, + ) -> impl ::core::future::Future> + Send; + fn is_clone_arg( + &mut self, + a: IsClone, + ) -> impl ::core::future::Future + Send; + fn is_clone_return( + &mut self, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn e1_arg(&mut self, x: E1) -> () { - Host::e1_arg(*self, x).await + fn e1_arg( + &mut self, + x: E1, + ) -> impl ::core::future::Future + Send { + async move { Host::e1_arg(*self, x).await } } - async fn e1_result(&mut self) -> E1 { - Host::e1_result(*self).await + fn e1_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::e1_result(*self).await } } - async fn v1_arg(&mut self, x: V1) -> () { - Host::v1_arg(*self, x).await + fn v1_arg( + &mut self, + x: V1, + ) -> impl ::core::future::Future + Send { + async move { Host::v1_arg(*self, x).await } } - async fn v1_result(&mut self) -> V1 { - Host::v1_result(*self).await + fn v1_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::v1_result(*self).await } } - async fn bool_arg(&mut self, x: bool) -> () { - Host::bool_arg(*self, x).await + fn bool_arg( + &mut self, + x: bool, + ) -> impl ::core::future::Future + Send { + async move { Host::bool_arg(*self, x).await } } - async fn bool_result(&mut self) -> bool { - Host::bool_result(*self).await + fn bool_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::bool_result(*self).await } } - async fn option_arg( + fn option_arg( &mut self, a: Option, b: Option<()>, @@ -545,22 +626,24 @@ pub mod foo { d: Option, e: Option, g: Option>, - ) -> () { - Host::option_arg(*self, a, b, c, d, e, g).await + ) -> impl ::core::future::Future + Send { + async move { Host::option_arg(*self, a, b, c, d, e, g).await } } - async fn option_result( + fn option_result( &mut self, - ) -> ( - Option, - Option<()>, - Option, - Option, - Option, - Option>, - ) { - Host::option_result(*self).await + ) -> impl ::core::future::Future< + Output = ( + Option, + Option<()>, + Option, + Option, + Option, + Option>, + ), + > + Send { + async move { Host::option_result(*self).await } } - async fn casts( + fn casts( &mut self, a: Casts1, b: Casts2, @@ -568,10 +651,12 @@ pub mod foo { d: Casts4, e: Casts5, f: Casts6, - ) -> (Casts1, Casts2, Casts3, Casts4, Casts5, Casts6) { - Host::casts(*self, a, b, c, d, e, f).await + ) -> impl ::core::future::Future< + Output = (Casts1, Casts2, Casts3, Casts4, Casts5, Casts6), + > + Send { + async move { Host::casts(*self, a, b, c, d, e, f).await } } - async fn result_arg( + fn result_arg( &mut self, a: Result<(), ()>, b: Result<(), E1>, @@ -582,50 +667,75 @@ pub mod foo { wasmtime::component::__internal::String, wasmtime::component::__internal::Vec, >, - ) -> () { - Host::result_arg(*self, a, b, c, d, e, f).await + ) -> impl ::core::future::Future + Send { + async move { Host::result_arg(*self, a, b, c, d, e, f).await } } - async fn result_result( + fn result_result( &mut self, - ) -> ( - Result<(), ()>, - Result<(), E1>, - Result, - Result<(), ()>, - Result, - Result< - wasmtime::component::__internal::String, - wasmtime::component::__internal::Vec, - >, - ) { - Host::result_result(*self).await + ) -> impl ::core::future::Future< + Output = ( + Result<(), ()>, + Result<(), E1>, + Result, + Result<(), ()>, + Result, + Result< + wasmtime::component::__internal::String, + wasmtime::component::__internal::Vec, + >, + ), + > + Send { + async move { Host::result_result(*self).await } } - async fn return_result_sugar(&mut self) -> Result { - Host::return_result_sugar(*self).await + fn return_result_sugar( + &mut self, + ) -> impl ::core::future::Future> + Send { + async move { Host::return_result_sugar(*self).await } } - async fn return_result_sugar2(&mut self) -> Result<(), MyErrno> { - Host::return_result_sugar2(*self).await + fn return_result_sugar2( + &mut self, + ) -> impl ::core::future::Future> + Send { + async move { Host::return_result_sugar2(*self).await } } - async fn return_result_sugar3(&mut self) -> Result { - Host::return_result_sugar3(*self).await + fn return_result_sugar3( + &mut self, + ) -> impl ::core::future::Future< + Output = Result, + > + Send { + async move { Host::return_result_sugar3(*self).await } } - async fn return_result_sugar4(&mut self) -> Result<(i32, u32), MyErrno> { - Host::return_result_sugar4(*self).await + fn return_result_sugar4( + &mut self, + ) -> impl ::core::future::Future< + Output = Result<(i32, u32), MyErrno>, + > + Send { + async move { Host::return_result_sugar4(*self).await } } - async fn return_option_sugar(&mut self) -> Option { - Host::return_option_sugar(*self).await + fn return_option_sugar( + &mut self, + ) -> impl ::core::future::Future> + Send { + async move { Host::return_option_sugar(*self).await } } - async fn return_option_sugar2(&mut self) -> Option { - Host::return_option_sugar2(*self).await + fn return_option_sugar2( + &mut self, + ) -> impl ::core::future::Future> + Send { + async move { Host::return_option_sugar2(*self).await } } - async fn result_simple(&mut self) -> Result { - Host::result_simple(*self).await + fn result_simple( + &mut self, + ) -> impl ::core::future::Future> + Send { + async move { Host::result_simple(*self).await } } - async fn is_clone_arg(&mut self, a: IsClone) -> () { - Host::is_clone_arg(*self, a).await + fn is_clone_arg( + &mut self, + a: IsClone, + ) -> impl ::core::future::Future + Send { + async move { Host::is_clone_arg(*self, a).await } } - async fn is_clone_return(&mut self) -> IsClone { - Host::is_clone_return(*self).await + fn is_clone_return( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::is_clone_return(*self).await } } } pub fn add_to_linker( @@ -633,7 +743,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -1336,7 +1446,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/variants` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/variants_concurrent.rs b/crates/component-macro/tests/expanded/variants_concurrent.rs index 8c1f3bfe92..a63406844f 100644 --- a/crates/component-macro/tests/expanded/variants_concurrent.rs +++ b/crates/component-macro/tests/expanded/variants_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> MyWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> MyWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct MyWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`MyWorld::instantiate_async`] which only needs a +/// [`MyWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`MyWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`MyWorldPre::instantiate_async`] to +/// method then uses [`MyWorldPre::instantiate`] to /// create a [`MyWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl MyWorld { /// Convenience wrapper around [`MyWorldPre::new`] and - /// [`MyWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`MyWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - MyWorldPre::new(pre)?.instantiate_async(store).await + MyWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`MyWorldIndices::new`] and /// [`MyWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = MyWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`MyWorldPre::new`] and + /// [`MyWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + MyWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: foo::foo::variants::HostConcurrent + Send, + D: foo::foo::variants::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::variants::Host + Send, T: 'static + Send, { @@ -447,41 +465,28 @@ pub mod foo { assert!(12 == < IsClone as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < IsClone as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostConcurrent: wasmtime::component::HasData + Send { + pub trait HostWithStore: wasmtime::component::HasData + Send { fn e1_arg( accessor: &wasmtime::component::Accessor, x: E1, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn e1_result( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn v1_arg( accessor: &wasmtime::component::Accessor, x: V1, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn v1_result( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn bool_arg( accessor: &wasmtime::component::Accessor, x: bool, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn bool_result( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn option_arg( accessor: &wasmtime::component::Accessor, a: Option, @@ -490,9 +495,7 @@ pub mod foo { d: Option, e: Option, g: Option>, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn option_result( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< @@ -504,9 +507,7 @@ pub mod foo { Option, Option>, ), - > + Send - where - Self: Sized; + > + Send; fn casts( accessor: &wasmtime::component::Accessor, a: Casts1, @@ -517,9 +518,7 @@ pub mod foo { f: Casts6, ) -> impl ::core::future::Future< Output = (Casts1, Casts2, Casts3, Casts4, Casts5, Casts6), - > + Send - where - Self: Sized; + > + Send; fn result_arg( accessor: &wasmtime::component::Accessor, a: Result<(), ()>, @@ -531,9 +530,7 @@ pub mod foo { wasmtime::component::__internal::String, wasmtime::component::__internal::Vec, >, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn result_result( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< @@ -548,61 +545,40 @@ pub mod foo { wasmtime::component::__internal::Vec, >, ), - > + Send - where - Self: Sized; + > + Send; fn return_result_sugar( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future> + Send - where - Self: Sized; + ) -> impl ::core::future::Future> + Send; fn return_result_sugar2( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future> + Send - where - Self: Sized; + ) -> impl ::core::future::Future> + Send; fn return_result_sugar3( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = Result, - > + Send - where - Self: Sized; + > + Send; fn return_result_sugar4( accessor: &wasmtime::component::Accessor, ) -> impl ::core::future::Future< Output = Result<(i32, u32), MyErrno>, - > + Send - where - Self: Sized; + > + Send; fn return_option_sugar( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future> + Send - where - Self: Sized; + ) -> impl ::core::future::Future> + Send; fn return_option_sugar2( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future> + Send - where - Self: Sized; + ) -> impl ::core::future::Future> + Send; fn result_simple( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future> + Send - where - Self: Sized; + ) -> impl ::core::future::Future> + Send; fn is_clone_arg( accessor: &wasmtime::component::Accessor, a: IsClone, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; fn is_clone_return( accessor: &wasmtime::component::Accessor, - ) -> impl ::core::future::Future + Send - where - Self: Sized; + ) -> impl ::core::future::Future + Send; } - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] pub trait Host: Send {} impl<_T: Host + ?Sized + Send> Host for &mut _T {} pub fn add_to_linker( @@ -610,7 +586,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: HostConcurrent, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -620,7 +596,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (E1,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::e1_arg(accessor, arg0).await; + let r = ::e1_arg(accessor, arg0).await; Ok(r) }) }, @@ -630,7 +606,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::e1_result(accessor).await; + let r = ::e1_result(accessor).await; Ok((r,)) }) }, @@ -640,7 +616,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (V1,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::v1_arg(accessor, arg0).await; + let r = ::v1_arg(accessor, arg0).await; Ok(r) }) }, @@ -650,7 +626,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::v1_result(accessor).await; + let r = ::v1_result(accessor).await; Ok((r,)) }) }, @@ -660,8 +636,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (arg0,): (bool,)| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::bool_arg(accessor, arg0) - .await; + let r = ::bool_arg(accessor, arg0).await; Ok(r) }) }, @@ -671,7 +646,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::bool_result(accessor).await; + let r = ::bool_result(accessor).await; Ok((r,)) }) }, @@ -698,7 +673,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::option_arg( + let r = ::option_arg( accessor, arg0, arg1, @@ -717,7 +692,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::option_result(accessor).await; + let r = ::option_result(accessor).await; Ok((r,)) }) }, @@ -737,7 +712,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::casts( + let r = ::casts( accessor, arg0, arg1, @@ -776,7 +751,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::result_arg( + let r = ::result_arg( accessor, arg0, arg1, @@ -795,7 +770,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::result_result(accessor).await; + let r = ::result_result(accessor).await; Ok((r,)) }) }, @@ -805,7 +780,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::return_result_sugar(accessor) + let r = ::return_result_sugar(accessor) .await; Ok((r,)) }) @@ -816,7 +791,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::return_result_sugar2(accessor) + let r = ::return_result_sugar2(accessor) .await; Ok((r,)) }) @@ -827,7 +802,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::return_result_sugar3(accessor) + let r = ::return_result_sugar3(accessor) .await; Ok((r,)) }) @@ -838,7 +813,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::return_result_sugar4(accessor) + let r = ::return_result_sugar4(accessor) .await; Ok((r,)) }) @@ -849,7 +824,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::return_option_sugar(accessor) + let r = ::return_option_sugar(accessor) .await; Ok((r,)) }) @@ -860,7 +835,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::return_option_sugar2(accessor) + let r = ::return_option_sugar2(accessor) .await; Ok((r,)) }) @@ -871,7 +846,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::result_simple(accessor).await; + let r = ::result_simple(accessor).await; Ok((r,)) }) }, @@ -884,7 +859,7 @@ pub mod foo { { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::is_clone_arg(accessor, arg0) + let r = ::is_clone_arg(accessor, arg0) .await; Ok(r) }) @@ -895,7 +870,7 @@ pub mod foo { move |caller: &wasmtime::component::Accessor, (): ()| { wasmtime::component::__internal::Box::pin(async move { let accessor = &caller.with_data(host_getter); - let r = ::is_clone_return(accessor) + let r = ::is_clone_return(accessor) .await; Ok((r,)) }) diff --git a/crates/component-macro/tests/expanded/variants_tracing_async.rs b/crates/component-macro/tests/expanded/variants_tracing_async.rs index e4fa5341d3..f62145fbda 100644 --- a/crates/component-macro/tests/expanded/variants_tracing_async.rs +++ b/crates/component-macro/tests/expanded/variants_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> MyWorldPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> MyWorldPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct MyWorldIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`MyWorld::instantiate_async`] which only needs a +/// [`MyWorld::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`MyWorldPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`MyWorldPre::instantiate_async`] to +/// method then uses [`MyWorldPre::instantiate`] to /// create a [`MyWorld`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl MyWorld { /// Convenience wrapper around [`MyWorldPre::new`] and - /// [`MyWorldPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`MyWorldPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - MyWorldPre::new(pre)?.instantiate_async(store).await + MyWorldPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`MyWorldIndices::new`] and /// [`MyWorldIndices::load`]. @@ -152,12 +157,25 @@ const _: () = { let indices = MyWorldIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`MyWorldPre::new`] and + /// [`MyWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + MyWorldPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::variants::HostWithStore + Send, for<'a> D::Data<'a>: foo::foo::variants::Host + Send, T: 'static + Send, { @@ -447,15 +465,34 @@ pub mod foo { assert!(12 == < IsClone as wasmtime::component::ComponentType >::SIZE32); assert!(4 == < IsClone as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] + pub trait HostWithStore: wasmtime::component::HasData + Send {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData + Send, + {} pub trait Host: Send { - async fn e1_arg(&mut self, x: E1) -> (); - async fn e1_result(&mut self) -> E1; - async fn v1_arg(&mut self, x: V1) -> (); - async fn v1_result(&mut self) -> V1; - async fn bool_arg(&mut self, x: bool) -> (); - async fn bool_result(&mut self) -> bool; - async fn option_arg( + fn e1_arg( + &mut self, + x: E1, + ) -> impl ::core::future::Future + Send; + fn e1_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn v1_arg( + &mut self, + x: V1, + ) -> impl ::core::future::Future + Send; + fn v1_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn bool_arg( + &mut self, + x: bool, + ) -> impl ::core::future::Future + Send; + fn bool_result( + &mut self, + ) -> impl ::core::future::Future + Send; + fn option_arg( &mut self, a: Option, b: Option<()>, @@ -463,18 +500,20 @@ pub mod foo { d: Option, e: Option, g: Option>, - ) -> (); - async fn option_result( + ) -> impl ::core::future::Future + Send; + fn option_result( &mut self, - ) -> ( - Option, - Option<()>, - Option, - Option, - Option, - Option>, - ); - async fn casts( + ) -> impl ::core::future::Future< + Output = ( + Option, + Option<()>, + Option, + Option, + Option, + Option>, + ), + > + Send; + fn casts( &mut self, a: Casts1, b: Casts2, @@ -482,8 +521,10 @@ pub mod foo { d: Casts4, e: Casts5, f: Casts6, - ) -> (Casts1, Casts2, Casts3, Casts4, Casts5, Casts6); - async fn result_arg( + ) -> impl ::core::future::Future< + Output = (Casts1, Casts2, Casts3, Casts4, Casts5, Casts6), + > + Send; + fn result_arg( &mut self, a: Result<(), ()>, b: Result<(), E1>, @@ -494,50 +535,90 @@ pub mod foo { wasmtime::component::__internal::String, wasmtime::component::__internal::Vec, >, - ) -> (); - async fn result_result( + ) -> impl ::core::future::Future + Send; + fn result_result( &mut self, - ) -> ( - Result<(), ()>, - Result<(), E1>, - Result, - Result<(), ()>, - Result, - Result< - wasmtime::component::__internal::String, - wasmtime::component::__internal::Vec, - >, - ); - async fn return_result_sugar(&mut self) -> Result; - async fn return_result_sugar2(&mut self) -> Result<(), MyErrno>; - async fn return_result_sugar3(&mut self) -> Result; - async fn return_result_sugar4(&mut self) -> Result<(i32, u32), MyErrno>; - async fn return_option_sugar(&mut self) -> Option; - async fn return_option_sugar2(&mut self) -> Option; - async fn result_simple(&mut self) -> Result; - async fn is_clone_arg(&mut self, a: IsClone) -> (); - async fn is_clone_return(&mut self) -> IsClone; + ) -> impl ::core::future::Future< + Output = ( + Result<(), ()>, + Result<(), E1>, + Result, + Result<(), ()>, + Result, + Result< + wasmtime::component::__internal::String, + wasmtime::component::__internal::Vec, + >, + ), + > + Send; + fn return_result_sugar( + &mut self, + ) -> impl ::core::future::Future> + Send; + fn return_result_sugar2( + &mut self, + ) -> impl ::core::future::Future> + Send; + fn return_result_sugar3( + &mut self, + ) -> impl ::core::future::Future< + Output = Result, + > + Send; + fn return_result_sugar4( + &mut self, + ) -> impl ::core::future::Future< + Output = Result<(i32, u32), MyErrno>, + > + Send; + fn return_option_sugar( + &mut self, + ) -> impl ::core::future::Future> + Send; + fn return_option_sugar2( + &mut self, + ) -> impl ::core::future::Future> + Send; + fn result_simple( + &mut self, + ) -> impl ::core::future::Future> + Send; + fn is_clone_arg( + &mut self, + a: IsClone, + ) -> impl ::core::future::Future + Send; + fn is_clone_return( + &mut self, + ) -> impl ::core::future::Future + Send; } impl<_T: Host + ?Sized + Send> Host for &mut _T { - async fn e1_arg(&mut self, x: E1) -> () { - Host::e1_arg(*self, x).await + fn e1_arg( + &mut self, + x: E1, + ) -> impl ::core::future::Future + Send { + async move { Host::e1_arg(*self, x).await } } - async fn e1_result(&mut self) -> E1 { - Host::e1_result(*self).await + fn e1_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::e1_result(*self).await } } - async fn v1_arg(&mut self, x: V1) -> () { - Host::v1_arg(*self, x).await + fn v1_arg( + &mut self, + x: V1, + ) -> impl ::core::future::Future + Send { + async move { Host::v1_arg(*self, x).await } } - async fn v1_result(&mut self) -> V1 { - Host::v1_result(*self).await + fn v1_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::v1_result(*self).await } } - async fn bool_arg(&mut self, x: bool) -> () { - Host::bool_arg(*self, x).await + fn bool_arg( + &mut self, + x: bool, + ) -> impl ::core::future::Future + Send { + async move { Host::bool_arg(*self, x).await } } - async fn bool_result(&mut self) -> bool { - Host::bool_result(*self).await + fn bool_result( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::bool_result(*self).await } } - async fn option_arg( + fn option_arg( &mut self, a: Option, b: Option<()>, @@ -545,22 +626,24 @@ pub mod foo { d: Option, e: Option, g: Option>, - ) -> () { - Host::option_arg(*self, a, b, c, d, e, g).await + ) -> impl ::core::future::Future + Send { + async move { Host::option_arg(*self, a, b, c, d, e, g).await } } - async fn option_result( + fn option_result( &mut self, - ) -> ( - Option, - Option<()>, - Option, - Option, - Option, - Option>, - ) { - Host::option_result(*self).await + ) -> impl ::core::future::Future< + Output = ( + Option, + Option<()>, + Option, + Option, + Option, + Option>, + ), + > + Send { + async move { Host::option_result(*self).await } } - async fn casts( + fn casts( &mut self, a: Casts1, b: Casts2, @@ -568,10 +651,12 @@ pub mod foo { d: Casts4, e: Casts5, f: Casts6, - ) -> (Casts1, Casts2, Casts3, Casts4, Casts5, Casts6) { - Host::casts(*self, a, b, c, d, e, f).await + ) -> impl ::core::future::Future< + Output = (Casts1, Casts2, Casts3, Casts4, Casts5, Casts6), + > + Send { + async move { Host::casts(*self, a, b, c, d, e, f).await } } - async fn result_arg( + fn result_arg( &mut self, a: Result<(), ()>, b: Result<(), E1>, @@ -582,50 +667,75 @@ pub mod foo { wasmtime::component::__internal::String, wasmtime::component::__internal::Vec, >, - ) -> () { - Host::result_arg(*self, a, b, c, d, e, f).await + ) -> impl ::core::future::Future + Send { + async move { Host::result_arg(*self, a, b, c, d, e, f).await } } - async fn result_result( + fn result_result( &mut self, - ) -> ( - Result<(), ()>, - Result<(), E1>, - Result, - Result<(), ()>, - Result, - Result< - wasmtime::component::__internal::String, - wasmtime::component::__internal::Vec, - >, - ) { - Host::result_result(*self).await + ) -> impl ::core::future::Future< + Output = ( + Result<(), ()>, + Result<(), E1>, + Result, + Result<(), ()>, + Result, + Result< + wasmtime::component::__internal::String, + wasmtime::component::__internal::Vec, + >, + ), + > + Send { + async move { Host::result_result(*self).await } } - async fn return_result_sugar(&mut self) -> Result { - Host::return_result_sugar(*self).await + fn return_result_sugar( + &mut self, + ) -> impl ::core::future::Future> + Send { + async move { Host::return_result_sugar(*self).await } } - async fn return_result_sugar2(&mut self) -> Result<(), MyErrno> { - Host::return_result_sugar2(*self).await + fn return_result_sugar2( + &mut self, + ) -> impl ::core::future::Future> + Send { + async move { Host::return_result_sugar2(*self).await } } - async fn return_result_sugar3(&mut self) -> Result { - Host::return_result_sugar3(*self).await + fn return_result_sugar3( + &mut self, + ) -> impl ::core::future::Future< + Output = Result, + > + Send { + async move { Host::return_result_sugar3(*self).await } } - async fn return_result_sugar4(&mut self) -> Result<(i32, u32), MyErrno> { - Host::return_result_sugar4(*self).await + fn return_result_sugar4( + &mut self, + ) -> impl ::core::future::Future< + Output = Result<(i32, u32), MyErrno>, + > + Send { + async move { Host::return_result_sugar4(*self).await } } - async fn return_option_sugar(&mut self) -> Option { - Host::return_option_sugar(*self).await + fn return_option_sugar( + &mut self, + ) -> impl ::core::future::Future> + Send { + async move { Host::return_option_sugar(*self).await } } - async fn return_option_sugar2(&mut self) -> Option { - Host::return_option_sugar2(*self).await + fn return_option_sugar2( + &mut self, + ) -> impl ::core::future::Future> + Send { + async move { Host::return_option_sugar2(*self).await } } - async fn result_simple(&mut self) -> Result { - Host::result_simple(*self).await + fn result_simple( + &mut self, + ) -> impl ::core::future::Future> + Send { + async move { Host::result_simple(*self).await } } - async fn is_clone_arg(&mut self, a: IsClone) -> () { - Host::is_clone_arg(*self, a).await + fn is_clone_arg( + &mut self, + a: IsClone, + ) -> impl ::core::future::Future + Send { + async move { Host::is_clone_arg(*self, a).await } } - async fn is_clone_return(&mut self) -> IsClone { - Host::is_clone_return(*self).await + fn is_clone_return( + &mut self, + ) -> impl ::core::future::Future + Send { + async move { Host::is_clone_return(*self).await } } } pub fn add_to_linker( @@ -633,7 +743,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static + Send, { @@ -1634,7 +1744,7 @@ pub mod exports { .ok_or_else(|| { anyhow::anyhow!( "instance export `foo:foo/variants` does \ - not have export `{name}`" + not have export `{name}`" ) }) }; diff --git a/crates/component-macro/tests/expanded/wat.rs b/crates/component-macro/tests/expanded/wat.rs index d43c75eb64..6586292522 100644 --- a/crates/component-macro/tests/expanded/wat.rs +++ b/crates/component-macro/tests/expanded/wat.rs @@ -52,6 +52,17 @@ impl<_T: 'static> ExamplePre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> ExamplePre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `example`. /// @@ -146,6 +157,19 @@ const _: () = { let indices = ExampleIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`ExamplePre::new`] and + /// [`ExamplePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + ExamplePre::new(pre)?.instantiate_async(store).await + } pub fn same_name_this_name_is_duplicated( &self, ) -> &exports::same::name::this_name_is_duplicated::Guest { diff --git a/crates/component-macro/tests/expanded/wat_async.rs b/crates/component-macro/tests/expanded/wat_async.rs index 55a47a3c68..6586292522 100644 --- a/crates/component-macro/tests/expanded/wat_async.rs +++ b/crates/component-macro/tests/expanded/wat_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> ExamplePre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> ExamplePre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct ExampleIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Example::instantiate_async`] which only needs a +/// [`Example::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`ExamplePre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`ExamplePre::instantiate_async`] to +/// method then uses [`ExamplePre::instantiate`] to /// create a [`Example`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl Example { /// Convenience wrapper around [`ExamplePre::new`] and - /// [`ExamplePre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`ExamplePre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - ExamplePre::new(pre)?.instantiate_async(store).await + ExamplePre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`ExampleIndices::new`] and /// [`ExampleIndices::load`]. @@ -152,6 +157,19 @@ const _: () = { let indices = ExampleIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`ExamplePre::new`] and + /// [`ExamplePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + ExamplePre::new(pre)?.instantiate_async(store).await + } pub fn same_name_this_name_is_duplicated( &self, ) -> &exports::same::name::this_name_is_duplicated::Guest { diff --git a/crates/component-macro/tests/expanded/wat_concurrent.rs b/crates/component-macro/tests/expanded/wat_concurrent.rs index 55a47a3c68..6586292522 100644 --- a/crates/component-macro/tests/expanded/wat_concurrent.rs +++ b/crates/component-macro/tests/expanded/wat_concurrent.rs @@ -43,13 +43,21 @@ impl<_T: 'static> ExamplePre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> ExamplePre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct ExampleIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Example::instantiate_async`] which only needs a +/// [`Example::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`ExamplePre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`ExamplePre::instantiate_async`] to +/// method then uses [`ExamplePre::instantiate`] to /// create a [`Example`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl Example { /// Convenience wrapper around [`ExamplePre::new`] and - /// [`ExamplePre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`ExamplePre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - ExamplePre::new(pre)?.instantiate_async(store).await + ExamplePre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`ExampleIndices::new`] and /// [`ExampleIndices::load`]. @@ -152,6 +157,19 @@ const _: () = { let indices = ExampleIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`ExamplePre::new`] and + /// [`ExamplePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + ExamplePre::new(pre)?.instantiate_async(store).await + } pub fn same_name_this_name_is_duplicated( &self, ) -> &exports::same::name::this_name_is_duplicated::Guest { diff --git a/crates/component-macro/tests/expanded/wat_tracing_async.rs b/crates/component-macro/tests/expanded/wat_tracing_async.rs index 55a47a3c68..6586292522 100644 --- a/crates/component-macro/tests/expanded/wat_tracing_async.rs +++ b/crates/component-macro/tests/expanded/wat_tracing_async.rs @@ -43,13 +43,21 @@ impl<_T: 'static> ExamplePre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> ExamplePre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -73,13 +81,13 @@ pub struct ExampleIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Example::instantiate_async`] which only needs a +/// [`Example::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`ExamplePre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`ExamplePre::instantiate_async`] to +/// method then uses [`ExamplePre::instantiate`] to /// create a [`Example`]. /// /// * If you've instantiated the instance yourself already @@ -131,17 +139,14 @@ const _: () = { } impl Example { /// Convenience wrapper around [`ExamplePre::new`] and - /// [`ExamplePre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`ExamplePre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - ExamplePre::new(pre)?.instantiate_async(store).await + ExamplePre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`ExampleIndices::new`] and /// [`ExampleIndices::load`]. @@ -152,6 +157,19 @@ const _: () = { let indices = ExampleIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`ExamplePre::new`] and + /// [`ExamplePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + ExamplePre::new(pre)?.instantiate_async(store).await + } pub fn same_name_this_name_is_duplicated( &self, ) -> &exports::same::name::this_name_is_duplicated::Guest { diff --git a/crates/component-macro/tests/expanded/worlds-with-types.rs b/crates/component-macro/tests/expanded/worlds-with-types.rs index 2a72375d7c..02825fcbfe 100644 --- a/crates/component-macro/tests/expanded/worlds-with-types.rs +++ b/crates/component-macro/tests/expanded/worlds-with-types.rs @@ -77,6 +77,17 @@ impl<_T: 'static> FooPre<_T> { self.indices.load(&mut store, &instance) } } +impl<_T: Send + 'static> FooPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} /// Auto-generated bindings for index of the exports of /// `foo`. /// @@ -185,12 +196,25 @@ const _: () = { let indices = FooIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: foo::foo::i::HostWithStore, for<'a> D::Data<'a>: foo::foo::i::Host, T: 'static, { @@ -221,6 +245,11 @@ pub mod foo { assert!(2 == < T as wasmtime::component::ComponentType >::SIZE32); assert!(2 == < T as wasmtime::component::ComponentType >::ALIGN32); }; + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} pub trait Host {} impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( @@ -228,7 +257,7 @@ pub mod foo { host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, T: 'static, { diff --git a/crates/component-macro/tests/expanded/worlds-with-types_async.rs b/crates/component-macro/tests/expanded/worlds-with-types_async.rs index 62641c2045..43b074c640 100644 --- a/crates/component-macro/tests/expanded/worlds-with-types_async.rs +++ b/crates/component-macro/tests/expanded/worlds-with-types_async.rs @@ -68,13 +68,21 @@ impl<_T: 'static> FooPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> FooPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -98,13 +106,13 @@ pub struct FooIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Foo::instantiate_async`] which only needs a +/// [`Foo::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`FooPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`FooPre::instantiate_async`] to +/// method then uses [`FooPre::instantiate`] to /// create a [`Foo`]. /// /// * If you've instantiated the instance yourself already @@ -170,17 +178,14 @@ const _: () = { } impl Foo { /// Convenience wrapper around [`FooPre::new`] and - /// [`FooPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`FooPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - FooPre::new(pre)?.instantiate_async(store).await + FooPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`FooIndices::new`] and /// [`FooIndices::load`]. @@ -191,14 +196,27 @@ const _: () = { let indices = FooIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, - for<'a> D::Data<'a>: foo::foo::i::Host + Send, - T: 'static + Send, + D: foo::foo::i::HostWithStore, + for<'a> D::Data<'a>: foo::foo::i::Host, + T: 'static, { foo::foo::i::add_to_linker::(linker, host_getter)?; Ok(()) @@ -230,17 +248,21 @@ pub mod foo { assert!(2 == < T as wasmtime::component::ComponentType >::SIZE32); assert!(2 == < T as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/i")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/worlds-with-types_concurrent.rs b/crates/component-macro/tests/expanded/worlds-with-types_concurrent.rs index abcdf1f16b..64ab96eddc 100644 --- a/crates/component-macro/tests/expanded/worlds-with-types_concurrent.rs +++ b/crates/component-macro/tests/expanded/worlds-with-types_concurrent.rs @@ -68,13 +68,21 @@ impl<_T: 'static> FooPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> FooPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -98,13 +106,13 @@ pub struct FooIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Foo::instantiate_async`] which only needs a +/// [`Foo::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`FooPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`FooPre::instantiate_async`] to +/// method then uses [`FooPre::instantiate`] to /// create a [`Foo`]. /// /// * If you've instantiated the instance yourself already @@ -170,17 +178,14 @@ const _: () = { } impl Foo { /// Convenience wrapper around [`FooPre::new`] and - /// [`FooPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`FooPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - FooPre::new(pre)?.instantiate_async(store).await + FooPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`FooIndices::new`] and /// [`FooIndices::load`]. @@ -191,14 +196,27 @@ const _: () = { let indices = FooIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, - for<'a> D::Data<'a>: foo::foo::i::Host + Send, - T: 'static + Send, + D: foo::foo::i::HostWithStore, + for<'a> D::Data<'a>: foo::foo::i::Host, + T: 'static, { foo::foo::i::add_to_linker::(linker, host_getter)?; Ok(()) @@ -230,17 +248,21 @@ pub mod foo { assert!(2 == < T as wasmtime::component::ComponentType >::SIZE32); assert!(2 == < T as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/i")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/worlds-with-types_tracing_async.rs b/crates/component-macro/tests/expanded/worlds-with-types_tracing_async.rs index 68bbe29896..91530ce5f8 100644 --- a/crates/component-macro/tests/expanded/worlds-with-types_tracing_async.rs +++ b/crates/component-macro/tests/expanded/worlds-with-types_tracing_async.rs @@ -68,13 +68,21 @@ impl<_T: 'static> FooPre<_T> { /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. + pub fn instantiate( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate(&mut store)?; + self.indices.load(&mut store, &instance) + } +} +impl<_T: Send + 'static> FooPre<_T> { + /// Same as [`Self::instantiate`], except with `async`. pub async fn instantiate_async( &self, mut store: impl wasmtime::AsContextMut, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate_async(&mut store).await?; self.indices.load(&mut store, &instance) @@ -98,13 +106,13 @@ pub struct FooIndices { /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use -/// [`Foo::instantiate_async`] which only needs a +/// [`Foo::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`FooPre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This -/// method then uses [`FooPre::instantiate_async`] to +/// method then uses [`FooPre::instantiate`] to /// create a [`Foo`]. /// /// * If you've instantiated the instance yourself already @@ -170,17 +178,14 @@ const _: () = { } impl Foo { /// Convenience wrapper around [`FooPre::new`] and - /// [`FooPre::instantiate_async`]. - pub async fn instantiate_async<_T>( + /// [`FooPre::instantiate`]. + pub fn instantiate<_T>( store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result - where - _T: Send, - { + ) -> wasmtime::Result { let pre = linker.instantiate_pre(component)?; - FooPre::new(pre)?.instantiate_async(store).await + FooPre::new(pre)?.instantiate(store) } /// Convenience wrapper around [`FooIndices::new`] and /// [`FooIndices::load`]. @@ -191,14 +196,27 @@ const _: () = { let indices = FooIndices::new(&instance.instance_pre(&store))?; indices.load(&mut store, instance) } + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, - for<'a> D::Data<'a>: foo::foo::i::Host + Send, - T: 'static + Send, + D: foo::foo::i::HostWithStore, + for<'a> D::Data<'a>: foo::foo::i::Host, + T: 'static, { foo::foo::i::add_to_linker::(linker, host_getter)?; Ok(()) @@ -238,17 +256,21 @@ pub mod foo { assert!(2 == < T as wasmtime::component::ComponentType >::SIZE32); assert!(2 == < T as wasmtime::component::ComponentType >::ALIGN32); }; - #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send {} - impl<_T: Host + ?Sized + Send> Host for &mut _T {} + pub trait HostWithStore: wasmtime::component::HasData {} + impl<_T: ?Sized> HostWithStore for _T + where + _T: wasmtime::component::HasData, + {} + pub trait Host {} + impl<_T: Host + ?Sized> Host for &mut _T {} pub fn add_to_linker( linker: &mut wasmtime::component::Linker, host_getter: fn(&mut T) -> D::Data<'_>, ) -> wasmtime::Result<()> where - D: wasmtime::component::HasData, + D: HostWithStore, for<'a> D::Data<'a>: Host, - T: 'static + Send, + T: 'static, { let mut inst = linker.instance("foo:foo/i")?; Ok(()) diff --git a/crates/cranelift/src/builder.rs b/crates/cranelift/src/builder.rs index a06c284806..d10c1bb974 100644 --- a/crates/cranelift/src/builder.rs +++ b/crates/cranelift/src/builder.rs @@ -65,16 +65,27 @@ impl CompilerBuilder for Builder { fn set(&mut self, name: &str, value: &str) -> Result<()> { // Special wasmtime-cranelift-only settings first - if name == "wasmtime_linkopt_padding_between_functions" { - self.linkopts.padding_between_functions = value.parse()?; - return Ok(()); + match name { + "wasmtime_linkopt_padding_between_functions" => { + self.linkopts.padding_between_functions = value.parse()?; + } + "wasmtime_linkopt_force_jump_veneer" => { + self.linkopts.force_jump_veneers = value.parse()?; + } + "wasmtime_inlining_intra_module" => { + self.tunables.as_mut().unwrap().inlining_intra_module = value.parse()?; + } + "wasmtime_inlining_small_callee_size" => { + self.tunables.as_mut().unwrap().inlining_small_callee_size = value.parse()?; + } + "wasmtime_inlining_sum_size_threshold" => { + self.tunables.as_mut().unwrap().inlining_sum_size_threshold = value.parse()?; + } + _ => { + self.inner.set(name, value)?; + } } - if name == "wasmtime_linkopt_force_jump_veneer" { - self.linkopts.force_jump_veneers = value.parse()?; - return Ok(()); - } - - self.inner.set(name, value) + Ok(()) } fn enable(&mut self, name: &str) -> Result<()> { diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs index 154f5e0818..c0434a2710 100644 --- a/crates/cranelift/src/compiler.rs +++ b/crates/cranelift/src/compiler.rs @@ -6,6 +6,7 @@ use crate::{BuiltinFunctionSignatures, builder::LinkOptions, wasm_call_signature use crate::{CompiledFunction, ModuleTextBuilder, array_call_signature}; use anyhow::{Context as _, Result}; use cranelift_codegen::binemit::CodeOffset; +use cranelift_codegen::inline::InlineCommand; use cranelift_codegen::ir::condcodes::IntCC; use cranelift_codegen::ir::{self, InstBuilder, MemFlags, UserExternalName, UserFuncName, Value}; use cranelift_codegen::isa::{ @@ -19,6 +20,7 @@ use cranelift_frontend::FunctionBuilder; use object::write::{Object, StandardSegment, SymbolId}; use object::{RelocationEncoding, RelocationFlags, RelocationKind, SectionKind}; use std::any::Any; +use std::borrow::Cow; use std::cmp; use std::collections::HashMap; use std::mem; @@ -28,9 +30,10 @@ use std::sync::{Arc, Mutex}; use wasmparser::{FuncValidatorAllocations, FunctionBody}; use wasmtime_environ::{ AddressMapSection, BuiltinFunctionIndex, CacheStore, CompileError, CompiledFunctionBody, - DefinedFuncIndex, FlagValue, FunctionBodyData, FunctionLoc, HostCall, ModuleTranslation, - ModuleTypesBuilder, PtrSize, RelocationTarget, StackMapSection, StaticModuleIndex, - TrapEncodingBuilder, TrapSentinel, TripleExt, Tunables, VMOffsets, WasmFuncType, WasmValType, + DefinedFuncIndex, FlagValue, FuncIndex, FunctionBodyData, FunctionLoc, HostCall, + InliningCompiler, ModuleTranslation, ModuleTypesBuilder, PtrSize, RelocationTarget, + StackMapSection, StaticModuleIndex, TrapEncodingBuilder, TrapSentinel, TripleExt, Tunables, + VMOffsets, WasmFuncType, WasmValType, }; #[cfg(feature = "component-model")] @@ -43,11 +46,25 @@ struct IncrementalCacheContext { num_cached: usize, } +/// ABI signature of functions that are generated here. +#[derive(Debug, Copy, Clone)] +#[cfg_attr( + not(feature = "component-model"), + expect(dead_code, reason = "only used with component model compiler") +)] +enum Abi { + /// The "wasm" ABI, or suitable to be a `wasm_call` field of a `VMFuncRef`. + Wasm, + /// The "array" ABI, or suitable to be an `array_call` field. + Array, +} + struct CompilerContext { func_translator: FuncTranslator, codegen_context: Context, incremental_cache_ctx: Option, validator_allocations: FuncValidatorAllocations, + abi: Option, } impl Default for CompilerContext { @@ -57,6 +74,7 @@ impl Default for CompilerContext { codegen_context: Context::new(), incremental_cache_ctx: None, validator_allocations: Default::default(), + abi: None, } } } @@ -181,6 +199,10 @@ impl Compiler { } impl wasmtime_environ::Compiler for Compiler { + fn inlining_compiler(&self) -> Option<&dyn wasmtime_environ::InliningCompiler> { + Some(self) + } + fn compile_function( &self, translation: &ModuleTranslation<'_>, @@ -276,14 +298,20 @@ impl wasmtime_environ::Compiler for Compiler { &mut func_env, )?; - let func = compiler.finish_with_info(Some((&body, &self.tunables)), symbol)?; + if self.tunables.inlining { + compiler + .cx + .codegen_context + .legalize(isa) + .map_err(|e| CompileError::Codegen(e.to_string()))?; + } let timing = cranelift_codegen::timing::take_current(); - log::debug!("{:?} translated in {:?}", func_index, timing.total()); - log::trace!("{func_index:?} timing info\n{timing}"); + log::debug!("`{symbol}` translated to CLIF in {:?}", timing.total()); + log::trace!("`{symbol}` timing info\n{timing}"); Ok(CompiledFunctionBody { - code: Box::new(func), + code: Box::new(Some(compiler.cx)), needs_gc_heap: func_env.needs_gc_heap(), }) } @@ -293,7 +321,7 @@ impl wasmtime_environ::Compiler for Compiler { translation: &ModuleTranslation<'_>, types: &ModuleTypesBuilder, def_func_index: DefinedFuncIndex, - symbol: &str, + _symbol: &str, ) -> Result { let func_index = translation.module.func_index(def_func_index); let sig = translation.module.functions[func_index] @@ -362,7 +390,7 @@ impl wasmtime_environ::Compiler for Compiler { builder.finalize(); Ok(CompiledFunctionBody { - code: Box::new(compiler.finish(symbol)?), + code: Box::new(Some(compiler.cx)), needs_gc_heap: false, }) } @@ -370,7 +398,7 @@ impl wasmtime_environ::Compiler for Compiler { fn compile_wasm_to_array_trampoline( &self, wasm_func_ty: &WasmFuncType, - symbol: &str, + _symbol: &str, ) -> Result { let isa = &*self.isa; let pointer_type = isa.pointer_type(); @@ -436,7 +464,7 @@ impl wasmtime_environ::Compiler for Compiler { builder.finalize(); Ok(CompiledFunctionBody { - code: Box::new(compiler.finish(&symbol)?), + code: Box::new(Some(compiler.cx)), needs_gc_heap: false, }) } @@ -444,7 +472,7 @@ impl wasmtime_environ::Compiler for Compiler { fn append_code( &self, obj: &mut Object<'static>, - funcs: &[(String, Box)], + funcs: &[(String, Box)], resolve_reloc: &dyn Fn(usize, RelocationTarget) -> usize, ) -> Result> { let mut builder = @@ -517,7 +545,7 @@ impl wasmtime_environ::Compiler for Compiler { get_func: &'a dyn Fn( StaticModuleIndex, DefinedFuncIndex, - ) -> (SymbolId, &'a (dyn Any + Send)), + ) -> (SymbolId, &'a (dyn Any + Send + Sync)), dwarf_package_bytes: Option<&'a [u8]>, tunables: &'a Tunables, ) -> Result<()> { @@ -586,7 +614,7 @@ impl wasmtime_environ::Compiler for Compiler { fn compile_wasm_to_builtin( &self, index: BuiltinFunctionIndex, - symbol: &str, + _symbol: &str, ) -> Result { let isa = &*self.isa; let ptr_size = isa.pointer_bytes(); @@ -657,7 +685,7 @@ impl wasmtime_environ::Compiler for Compiler { builder.finalize(); Ok(CompiledFunctionBody { - code: Box::new(compiler.finish(&symbol)?), + code: Box::new(Some(compiler.cx)), needs_gc_heap: false, }) } @@ -671,6 +699,130 @@ impl wasmtime_environ::Compiler for Compiler { } } +impl InliningCompiler for Compiler { + fn calls( + &self, + func_body: &CompiledFunctionBody, + calls: &mut wasmtime_environ::prelude::IndexSet, + ) -> Result<()> { + let cx = func_body + .code + .downcast_ref::>() + .unwrap() + .as_ref() + .unwrap(); + let func = &cx.codegen_context.func; + calls.extend( + func.params + .user_named_funcs() + .values() + .filter(|name| name.namespace == crate::NS_WASM_FUNC) + .map(|name| FuncIndex::from_u32(name.index)), + ); + Ok(()) + } + + fn size(&self, func_body: &CompiledFunctionBody) -> u32 { + let cx = func_body + .code + .downcast_ref::>() + .unwrap() + .as_ref() + .unwrap(); + let func = &cx.codegen_context.func; + let size = func.dfg.values().len(); + u32::try_from(size).unwrap() + } + + fn inline<'a>( + &self, + func_body: &mut CompiledFunctionBody, + get_callee: &'a mut dyn FnMut(FuncIndex) -> Option<&'a CompiledFunctionBody>, + ) -> Result<()> { + let code = func_body + .code + .downcast_mut::>() + .unwrap(); + let cx = code.as_mut().unwrap(); + + cx.codegen_context.inline(Inliner(get_callee))?; + return Ok(()); + + struct Inliner<'a>(&'a mut dyn FnMut(FuncIndex) -> Option<&'a CompiledFunctionBody>); + + impl cranelift_codegen::inline::Inline for Inliner<'_> { + fn inline( + &mut self, + caller: &ir::Function, + _call_inst: ir::Inst, + _call_opcode: ir::Opcode, + callee: ir::FuncRef, + _call_args: &[ir::Value], + ) -> InlineCommand<'_> { + let callee = &caller.dfg.ext_funcs[callee].name; + let callee = match callee { + ir::ExternalName::User(callee) => *callee, + ir::ExternalName::TestCase(_) + | ir::ExternalName::LibCall(_) + | ir::ExternalName::KnownSymbol(_) => return InlineCommand::KeepCall, + }; + let callee = &caller.params.user_named_funcs()[callee]; + let callee = if callee.namespace == crate::NS_WASM_FUNC { + FuncIndex::from_u32(callee.index) + } else { + return InlineCommand::KeepCall; + }; + match (self.0)(callee) { + None => InlineCommand::KeepCall, + Some(func_body) => { + let cx = func_body + .code + .downcast_ref::>() + .unwrap(); + InlineCommand::Inline(Cow::Borrowed( + &cx.as_ref().unwrap().codegen_context.func, + )) + } + } + } + } + } + + fn finish_compiling( + &self, + func_body: &mut CompiledFunctionBody, + input: Option>, + symbol: &str, + ) -> Result<()> { + let cx = func_body + .code + .downcast_mut::>() + .unwrap() + .take() + .unwrap(); + let compiler = FunctionCompiler { compiler: self, cx }; + + let symbol = match compiler.cx.abi { + None => Cow::Borrowed(symbol), + Some(Abi::Wasm) => Cow::Owned(format!("{symbol}_wasm_call")), + Some(Abi::Array) => Cow::Owned(format!("{symbol}_array_call")), + }; + + let compiled_func = if let Some(input) = input { + compiler.finish_with_info(Some((&input, &self.tunables)), &symbol)? + } else { + compiler.finish(&symbol)? + }; + + let timing = cranelift_codegen::timing::take_current(); + log::debug!("`{symbol}` compiled in {:?}", timing.total()); + log::trace!("`{symbol}` timing info\n{timing}"); + + func_body.code = Box::new(compiled_func); + Ok(()) + } +} + #[cfg(feature = "incremental-cache")] mod incremental_cache { use super::*; diff --git a/crates/cranelift/src/compiler/component.rs b/crates/cranelift/src/compiler/component.rs index acc68d1c2b..b60b5a2a9c 100644 --- a/crates/cranelift/src/compiler/component.rs +++ b/crates/cranelift/src/compiler/component.rs @@ -1,6 +1,9 @@ //! Compilation support for the component model. -use crate::{TRAP_ALWAYS, TRAP_CANNOT_ENTER, TRAP_INTERNAL_ASSERT, compiler::Compiler}; +use crate::{ + TRAP_ALWAYS, TRAP_CANNOT_ENTER, TRAP_INTERNAL_ASSERT, + compiler::{Abi, Compiler}, +}; use anyhow::Result; use cranelift_codegen::ir::condcodes::IntCC; use cranelift_codegen::ir::{self, InstBuilder, MemFlags, Value}; @@ -9,7 +12,8 @@ use cranelift_frontend::FunctionBuilder; use wasmtime_environ::fact::PREPARE_CALL_FIXED_PARAMS; use wasmtime_environ::{CompiledFunctionBody, component::*}; use wasmtime_environ::{ - HostCall, ModuleInternedTypeIndex, PtrSize, TrapSentinel, Tunables, WasmFuncType, WasmValType, + EntityRef, HostCall, ModuleInternedTypeIndex, PtrSize, TrapSentinel, Tunables, WasmFuncType, + WasmValType, }; struct TrampolineCompiler<'a> { @@ -25,15 +29,76 @@ struct TrampolineCompiler<'a> { tunables: &'a Tunables, } -#[derive(Debug, Copy, Clone)] -enum Abi { - Wasm, - Array, +/// What host functions can be called, used in `translate_hostcall` below. +enum HostCallee { + /// Call a host-lowered function specified by this index. + Lowering(LoweredIndex), + /// Call a host libcall, specified by this accessor. + Libcall(GetLibcallFn), } type GetLibcallFn = fn(&dyn TargetIsa, &mut ir::Function) -> (ir::SigRef, ComponentBuiltinFunctionIndex); +impl From for HostCallee { + fn from(index: LoweredIndex) -> HostCallee { + HostCallee::Lowering(index) + } +} + +impl From for HostCallee { + fn from(f: GetLibcallFn) -> HostCallee { + HostCallee::Libcall(f) + } +} + +/// How to interpret the results of a host function. +enum HostResult { + /// The host function has no results. + None, + + /// The host function returns the sentinel specified which is interpreted + /// and translated to the real return value. + Sentinel(TrapSentinel), + + /// The host function returns a `bool` indicating whether it succeeded or + /// not. + /// + /// After the return value is interpreted the host function also filled in + /// `ptr` and `len` with wasm return values which need to be returned. + /// + /// If `ptr` and `len` are not specified then this must be used with + /// `WasmArgs::ValRawList` and that ptr/len is used. + MultiValue { + /// The base pointer of the `ValRaw` list on the stack. + ptr: Option, + /// The length of the `ValRaw` list on the stack. + len: Option, + }, +} + +impl From for HostResult { + fn from(sentinel: TrapSentinel) -> HostResult { + HostResult::Sentinel(sentinel) + } +} + +/// Different means of passing WebAssembly arguments to host calls. +#[derive(Debug, Copy, Clone)] +enum WasmArgs { + /// All wasm arguments to the host are passed directly as values, typically + /// through registers. + InRegisters, + + /// All wasm arguments to the host are passed indirectly by spilling them + /// to the stack as a sequence of contiguous `ValRaw`s. + ValRawList, + + /// The first `n` arguments are passed in registers, but everything after + /// that is spilled to the stack. + InRegistersUpTo(usize), +} + impl<'a> TrampolineCompiler<'a> { fn new( compiler: &'a Compiler, @@ -94,190 +159,540 @@ impl<'a> TrampolineCompiler<'a> { options, lower_ty, } => { - self.translate_lower_import(*index, options, *lower_ty); + let pointer_type = self.isa.pointer_type(); + self.translate_hostcall( + HostCallee::Lowering(*index), + HostResult::MultiValue { + ptr: None, + len: None, + }, + WasmArgs::ValRawList, + |me, params| { + let vmctx = params[0]; + params.extend([ + me.builder.ins().load( + pointer_type, + MemFlags::trusted(), + vmctx, + i32::try_from(me.offsets.lowering_data(*index)).unwrap(), + ), + me.index_value(*lower_ty), + me.index_value(*options), + ]); + }, + ); } Trampoline::AlwaysTrap => { - self.translate_always_trap(); + if self.tunables.signals_based_traps { + self.builder.ins().trap(TRAP_ALWAYS); + return; + } + self.translate_libcall( + host::trap, + TrapSentinel::Falsy, + WasmArgs::InRegisters, + |me, params| { + let code = wasmtime_environ::Trap::AlwaysTrapAdapter as u8; + params.push(me.builder.ins().iconst(ir::types::I8, i64::from(code))); + }, + ); + } + Trampoline::ResourceNew(ty) => { + // Currently this only supports resources represented by `i32` + assert_eq!( + self.types[self.signature].unwrap_func().params()[0], + WasmValType::I32 + ); + self.translate_libcall( + host::resource_new32, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + }, + ); + } + Trampoline::ResourceRep(ty) => { + // Currently this only supports resources represented by `i32` + assert_eq!( + self.types[self.signature].unwrap_func().returns()[0], + WasmValType::I32 + ); + self.translate_libcall( + host::resource_rep32, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + }, + ); + } + Trampoline::ResourceDrop(ty) => { + self.translate_resource_drop(*ty); } - Trampoline::ResourceNew(ty) => self.translate_resource_new(*ty), - Trampoline::ResourceRep(ty) => self.translate_resource_rep(*ty), - Trampoline::ResourceDrop(ty) => self.translate_resource_drop(*ty), Trampoline::BackpressureSet { instance } => { - self.translate_backpressure_set_call(*instance) + self.translate_libcall( + host::backpressure_set, + TrapSentinel::Falsy, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*instance)); + }, + ); } Trampoline::TaskReturn { results, options } => { - self.translate_task_return_call(*results, options) - } - Trampoline::TaskCancel { instance } => self.translate_task_cancel_call(*instance), - Trampoline::WaitableSetNew { instance } => self.translate_waitable_set_new(*instance), - Trampoline::WaitableSetWait { - instance, - async_, - memory, - } => self.translate_task_wait_or_poll_call( - *instance, - *async_, - *memory, - host::waitable_set_wait, - ), - Trampoline::WaitableSetPoll { - instance, - async_, - memory, - } => self.translate_task_wait_or_poll_call( - *instance, - *async_, - *memory, - host::waitable_set_poll, - ), - Trampoline::WaitableSetDrop { instance } => self.translate_waitable_set_drop(*instance), - Trampoline::WaitableJoin { instance } => self.translate_waitable_join(*instance), - Trampoline::Yield { async_ } => self.translate_yield_call(*async_), - Trampoline::SubtaskDrop { instance } => self.translate_subtask_drop_call(*instance), + self.translate_libcall( + host::task_return, + TrapSentinel::Falsy, + WasmArgs::ValRawList, + |me, params| { + params.push(me.index_value(*results)); + params.push(me.index_value(*options)); + }, + ); + } + Trampoline::TaskCancel { instance } => { + self.translate_libcall( + host::task_cancel, + TrapSentinel::Falsy, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*instance)); + }, + ); + } + Trampoline::WaitableSetNew { instance } => { + self.translate_libcall( + host::waitable_set_new, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*instance)); + }, + ); + } + Trampoline::WaitableSetWait { options } => { + self.translate_libcall( + host::waitable_set_wait, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*options)); + }, + ); + } + Trampoline::WaitableSetPoll { options } => { + self.translate_libcall( + host::waitable_set_poll, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*options)); + }, + ); + } + Trampoline::WaitableSetDrop { instance } => { + self.translate_libcall( + host::waitable_set_drop, + TrapSentinel::Falsy, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*instance)); + }, + ); + } + Trampoline::WaitableJoin { instance } => { + self.translate_libcall( + host::waitable_join, + TrapSentinel::Falsy, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*instance)); + }, + ); + } + Trampoline::Yield { async_ } => { + self.translate_libcall( + host::yield_, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.push(me.builder.ins().iconst(ir::types::I8, i64::from(*async_))); + }, + ); + } + Trampoline::SubtaskDrop { instance } => { + self.translate_libcall( + host::subtask_drop, + TrapSentinel::Falsy, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*instance)); + }, + ); + } Trampoline::SubtaskCancel { instance, async_ } => { - self.translate_subtask_cancel_call(*instance, *async_) + self.translate_libcall( + host::subtask_cancel, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*instance)); + params.push(me.builder.ins().iconst(ir::types::I8, i64::from(*async_))); + }, + ); + } + Trampoline::StreamNew { ty } => { + self.translate_libcall( + host::stream_new, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + }, + ); } - Trampoline::StreamNew { ty } => self.translate_future_or_stream_call( - &[ty.as_u32()], - None, - host::stream_new, - TrapSentinel::NegativeOne, - ), Trampoline::StreamRead { ty, options } => { - let tys = &[ty.as_u32()]; if let Some(info) = self.flat_stream_element_info(*ty).cloned() { - self.translate_flat_stream_call(tys, options, host::flat_stream_read, &info) + self.translate_libcall( + host::flat_stream_read, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.extend([ + me.index_value(*ty), + me.index_value(*options), + me.builder + .ins() + .iconst(ir::types::I32, i64::from(info.size32)), + me.builder + .ins() + .iconst(ir::types::I32, i64::from(info.align32)), + ]); + }, + ); } else { - self.translate_future_or_stream_call( - tys, - Some(options), + self.translate_libcall( host::stream_read, TrapSentinel::NegativeOne, - ) + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + params.push(me.index_value(*options)); + }, + ); } } Trampoline::StreamWrite { ty, options } => { - let tys = &[ty.as_u32()]; if let Some(info) = self.flat_stream_element_info(*ty).cloned() { - self.translate_flat_stream_call(tys, options, host::flat_stream_write, &info) + self.translate_libcall( + host::flat_stream_write, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.extend([ + me.index_value(*ty), + me.index_value(*options), + me.builder + .ins() + .iconst(ir::types::I32, i64::from(info.size32)), + me.builder + .ins() + .iconst(ir::types::I32, i64::from(info.align32)), + ]); + }, + ); } else { - self.translate_future_or_stream_call( - tys, - Some(options), + self.translate_libcall( host::stream_write, TrapSentinel::NegativeOne, - ) + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + params.push(me.index_value(*options)); + }, + ); } } Trampoline::StreamCancelRead { ty, async_ } => { - self.translate_cancel_call(ty.as_u32(), *async_, host::stream_cancel_read) + self.translate_libcall( + host::stream_cancel_read, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + params.push(me.builder.ins().iconst(ir::types::I8, i64::from(*async_))); + }, + ); } Trampoline::StreamCancelWrite { ty, async_ } => { - self.translate_cancel_call(ty.as_u32(), *async_, host::stream_cancel_write) + self.translate_libcall( + host::stream_cancel_write, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + params.push(me.builder.ins().iconst(ir::types::I8, i64::from(*async_))); + }, + ); + } + Trampoline::StreamDropReadable { ty } => { + self.translate_libcall( + host::stream_drop_readable, + TrapSentinel::Falsy, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + }, + ); + } + Trampoline::StreamDropWritable { ty } => { + self.translate_libcall( + host::stream_drop_writable, + TrapSentinel::Falsy, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + }, + ); + } + Trampoline::FutureNew { ty } => { + self.translate_libcall( + host::future_new, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + }, + ); + } + Trampoline::FutureRead { ty, options } => { + self.translate_libcall( + host::future_read, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + params.push(me.index_value(*options)); + }, + ); + } + Trampoline::FutureWrite { ty, options } => { + self.translate_libcall( + host::future_write, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + params.push(me.index_value(*options)); + }, + ); } - Trampoline::StreamDropReadable { ty } => self.translate_future_or_stream_call( - &[ty.as_u32()], - None, - host::stream_drop_readable, - TrapSentinel::Falsy, - ), - Trampoline::StreamDropWritable { ty } => self.translate_future_or_stream_call( - &[ty.as_u32()], - None, - host::stream_drop_writable, - TrapSentinel::Falsy, - ), - Trampoline::FutureNew { ty } => self.translate_future_or_stream_call( - &[ty.as_u32()], - None, - host::future_new, - TrapSentinel::NegativeOne, - ), - Trampoline::FutureRead { ty, options } => self.translate_future_or_stream_call( - &[ty.as_u32()], - Some(&options), - host::future_read, - TrapSentinel::NegativeOne, - ), - Trampoline::FutureWrite { ty, options } => self.translate_future_or_stream_call( - &[ty.as_u32()], - Some(options), - host::future_write, - TrapSentinel::NegativeOne, - ), Trampoline::FutureCancelRead { ty, async_ } => { - self.translate_cancel_call(ty.as_u32(), *async_, host::future_cancel_read) + self.translate_libcall( + host::future_cancel_read, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + params.push(me.builder.ins().iconst(ir::types::I8, i64::from(*async_))); + }, + ); } Trampoline::FutureCancelWrite { ty, async_ } => { - self.translate_cancel_call(ty.as_u32(), *async_, host::future_cancel_write) + self.translate_libcall( + host::future_cancel_write, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + params.push(me.builder.ins().iconst(ir::types::I8, i64::from(*async_))); + }, + ); } - Trampoline::FutureDropReadable { ty } => self.translate_future_or_stream_call( - &[ty.as_u32()], - None, - host::future_drop_readable, - TrapSentinel::Falsy, - ), - Trampoline::FutureDropWritable { ty } => self.translate_future_or_stream_call( - &[ty.as_u32()], - None, - host::future_drop_writable, - TrapSentinel::Falsy, - ), - Trampoline::ErrorContextNew { ty, options } => self.translate_error_context_call( - *ty, - options, - host::error_context_new, - TrapSentinel::NegativeOne, - ), - Trampoline::ErrorContextDebugMessage { ty, options } => self - .translate_error_context_call( - *ty, - options, + Trampoline::FutureDropReadable { ty } => { + self.translate_libcall( + host::future_drop_readable, + TrapSentinel::Falsy, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + }, + ); + } + Trampoline::FutureDropWritable { ty } => { + self.translate_libcall( + host::future_drop_writable, + TrapSentinel::Falsy, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + }, + ); + } + Trampoline::ErrorContextNew { ty, options } => { + self.translate_libcall( + host::error_context_new, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + params.push(me.index_value(*options)); + }, + ); + } + Trampoline::ErrorContextDebugMessage { ty, options } => { + self.translate_libcall( host::error_context_debug_message, TrapSentinel::Falsy, - ), - Trampoline::ErrorContextDrop { ty } => self.translate_error_context_drop_call(*ty), + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + params.push(me.index_value(*options)); + }, + ); + } + Trampoline::ErrorContextDrop { ty } => { + self.translate_libcall( + host::error_context_drop, + TrapSentinel::Falsy, + WasmArgs::InRegisters, + |me, params| { + params.push(me.index_value(*ty)); + }, + ); + } Trampoline::ResourceTransferOwn => { - self.translate_host_libcall(host::resource_transfer_own, |me, rets| { - rets[0] = me.raise_if_negative_one_and_truncate(rets[0]); - }) + self.translate_libcall( + host::resource_transfer_own, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |_, _| {}, + ); } Trampoline::ResourceTransferBorrow => { - self.translate_host_libcall(host::resource_transfer_borrow, |me, rets| { - rets[0] = me.raise_if_negative_one_and_truncate(rets[0]); - }) + self.translate_libcall( + host::resource_transfer_borrow, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |_, _| {}, + ); } Trampoline::ResourceEnterCall => { - self.translate_host_libcall(host::resource_enter_call, |_, _| {}) + self.translate_libcall( + host::resource_enter_call, + HostResult::None, + WasmArgs::InRegisters, + |_, _| {}, + ); } Trampoline::ResourceExitCall => { - self.translate_host_libcall(host::resource_exit_call, |me, rets| { - me.raise_if_host_trapped(rets.pop().unwrap()); - }) + self.translate_libcall( + host::resource_exit_call, + TrapSentinel::Falsy, + WasmArgs::InRegisters, + |_, _| {}, + ); + } + Trampoline::PrepareCall { memory } => { + self.translate_libcall( + host::prepare_call, + TrapSentinel::Falsy, + WasmArgs::InRegistersUpTo(PREPARE_CALL_FIXED_PARAMS.len()), + |me, params| { + let vmctx = params[0]; + params.push(me.load_optional_memory(vmctx, *memory)); + }, + ); + } + Trampoline::SyncStartCall { callback } => { + let pointer_type = self.isa.pointer_type(); + let wasm_func_ty = &self.types[self.signature].unwrap_func(); + let (values_vec_ptr, len) = self.compiler.allocate_stack_array_and_spill_args( + &WasmFuncType::new( + Box::new([]), + wasm_func_ty.returns().iter().copied().collect(), + ), + &mut self.builder, + &[], + ); + let values_vec_len = self.builder.ins().iconst(pointer_type, i64::from(len)); + self.translate_libcall( + host::sync_start, + HostResult::MultiValue { + ptr: Some(values_vec_ptr), + len: Some(values_vec_len), + }, + WasmArgs::InRegisters, + |me, params| { + let vmctx = params[0]; + params.push(me.load_callback(vmctx, *callback)); + params.push(values_vec_ptr); + params.push(values_vec_len); + }, + ); } - Trampoline::PrepareCall { memory } => self.translate_prepare(*memory), - Trampoline::SyncStartCall { callback } => self.translate_sync_start(*callback), Trampoline::AsyncStartCall { callback, post_return, - } => self.translate_async_start(*callback, *post_return), + } => { + self.translate_libcall( + host::async_start, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + let vmctx = params[0]; + params.extend([ + me.load_callback(vmctx, *callback), + me.load_post_return(vmctx, *post_return), + ]); + }, + ); + } Trampoline::FutureTransfer => { - self.translate_host_libcall(host::future_transfer, |me, rets| { - rets[0] = me.raise_if_negative_one_and_truncate(rets[0]); - }) + self.translate_libcall( + host::future_transfer, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |_, _| {}, + ); } Trampoline::StreamTransfer => { - self.translate_host_libcall(host::stream_transfer, |me, rets| { - rets[0] = me.raise_if_negative_one_and_truncate(rets[0]); - }) + self.translate_libcall( + host::stream_transfer, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |_, _| {}, + ); } Trampoline::ErrorContextTransfer => { - self.translate_host_libcall(host::error_context_transfer, |me, rets| { - rets[0] = me.raise_if_negative_one_and_truncate(rets[0]); - }) + self.translate_libcall( + host::error_context_transfer, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |_, _| {}, + ); + } + Trampoline::ContextGet(i) => { + self.translate_libcall( + host::context_get, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |me, params| { + params.push(me.builder.ins().iconst(ir::types::I32, i64::from(*i))); + }, + ); + } + Trampoline::ContextSet(i) => { + self.translate_libcall( + host::context_set, + TrapSentinel::Falsy, + WasmArgs::InRegisters, + |me, params| { + params.push(me.builder.ins().iconst(ir::types::I32, i64::from(*i))); + }, + ); } - Trampoline::ContextGet(i) => self.translate_context_get(*i), - Trampoline::ContextSet(i) => self.translate_context_set(*i), } } @@ -312,13 +727,15 @@ impl<'a> TrampolineCompiler<'a> { } } + /// Helper function to spill the wasm arguments `args` to this function into + /// a stack-allocated array. fn store_wasm_arguments(&mut self, args: &[Value]) -> (Value, Value) { let pointer_type = self.isa.pointer_type(); let wasm_func_ty = &self.types[self.signature].unwrap_func(); - // Start off by spilling all the wasm arguments into a stack slot to be - // passed to the host function. match self.abi { + // For the wasm ABI a stack needs to be allocated and these + // arguments are stored onto the stack. Abi::Wasm => { let (ptr, len) = self.compiler.allocate_stack_array_and_spill_args( wasm_func_ty, @@ -328,633 +745,185 @@ impl<'a> TrampolineCompiler<'a> { let len = self.builder.ins().iconst(pointer_type, i64::from(len)); (ptr, len) } + + // For the array ABI all arguments were already in a stack, so + // forward along that pointer/len. Abi::Array => { let params = self.builder.func.dfg.block_params(self.block0); (params[2], params[3]) } } - } - - fn translate_intrinsic_libcall( - &mut self, - vmctx: ir::Value, - get_libcall: GetLibcallFn, - args: &[ir::Value], - sentinel: TrapSentinel, - ) { - match self.abi { - Abi::Wasm => {} - - Abi::Array => { - // TODO: A guest could hypothetically export the same intrinsic - // it imported, allowing the host to call it directly. We need - // to support that here (except for `sync-prepare`, - // `sync-start`, `async-prepare`, and `async-start`, which are - // only ever called from FACT-generated Wasm code and never - // exported). - // - // https://github.com/bytecodealliance/wasmtime/issues/10143 - self.builder.ins().trap(TRAP_INTERNAL_ASSERT); - return; - } - } - - let call = self.call_libcall(vmctx, get_libcall, args); - - let result = self.builder.func.dfg.inst_results(call)[0]; - let result_ty = self.builder.func.dfg.value_type(result); - let expected = &self.builder.func.signature.returns; - match sentinel { - TrapSentinel::NegativeOne => { - assert_eq!(expected.len(), 1); - let result = match (result_ty, expected[0].value_type) { - (ir::types::I64, ir::types::I32) => { - self.raise_if_negative_one_and_truncate(result) - } - (ir::types::I64, ir::types::I64) | (ir::types::I32, ir::types::I32) => { - self.raise_if_negative_one(result) - } - other => panic!("unsupported NegativeOne combo {other:?}"), - }; - self.abi_store_results(&[result]); - } - TrapSentinel::Falsy => { - assert_eq!(expected.len(), 0); - self.raise_if_host_trapped(result); - self.builder.ins().return_(&[]); - } - _ => todo!("support additional return types if/when necessary"), - } - } - - fn translate_task_return_call(&mut self, results: TypeTupleIndex, options: &CanonicalOptions) { - let mem_opts = match &options.data_model { - CanonicalOptionsDataModel::Gc {} => todo!("CM+GC"), - CanonicalOptionsDataModel::LinearMemory(opts) => opts, - }; - - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - - let (values_vec_ptr, values_vec_len) = self.store_wasm_arguments(&args[2..]); - - let ty = self - .builder - .ins() - .iconst(ir::types::I32, i64::from(results.as_u32())); - - let memory = self.load_optional_memory(vmctx, mem_opts.memory); - let string_encoding = self.string_encoding(options.string_encoding); - - self.translate_intrinsic_libcall( - vmctx, - host::task_return, - &[ - vmctx, - ty, - memory, - string_encoding, - values_vec_ptr, - values_vec_len, - ], - TrapSentinel::Falsy, - ); - } - - fn translate_waitable_set_new(&mut self, instance: RuntimeComponentInstanceIndex) { - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - - let instance = self - .builder - .ins() - .iconst(ir::types::I32, i64::from(instance.as_u32())); - - self.translate_intrinsic_libcall( - vmctx, - host::waitable_set_new, - &[vmctx, instance], - TrapSentinel::NegativeOne, - ); - } - - fn translate_waitable_set_drop(&mut self, instance: RuntimeComponentInstanceIndex) { - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - let set = args[2]; - - let instance = self - .builder - .ins() - .iconst(ir::types::I32, i64::from(instance.as_u32())); - - self.translate_intrinsic_libcall( - vmctx, - host::waitable_set_drop, - &[vmctx, instance, set], - TrapSentinel::Falsy, - ); - } - - fn translate_waitable_join(&mut self, instance: RuntimeComponentInstanceIndex) { - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - let set = args[2]; - let waitable = args[3]; - - let instance = self - .builder - .ins() - .iconst(ir::types::I32, i64::from(instance.as_u32())); - - self.translate_intrinsic_libcall( - vmctx, - host::waitable_join, - &[vmctx, instance, set, waitable], - TrapSentinel::Falsy, - ); - } - - fn translate_prepare(&mut self, memory: Option) { - match self.abi { - Abi::Wasm => {} - - Abi::Array => { - // This code can only be called from (FACT-generated) Wasm, so - // we don't need to support the array ABI. - self.builder.ins().trap(TRAP_INTERNAL_ASSERT); - return; - } - } - - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - - let pointer_type = self.isa.pointer_type(); - let wasm_func_ty = &self.types[self.signature].unwrap_func(); - - let param_offset = PREPARE_CALL_FIXED_PARAMS.len(); - let spill_offset = param_offset + 2; // skip caller/callee vmctx - - let (values_vec_ptr, len) = self.compiler.allocate_stack_array_and_spill_args( - &WasmFuncType::new( - wasm_func_ty - .params() - .iter() - .skip(param_offset) - .copied() - .collect(), - Box::new([]), - ), - &mut self.builder, - &args[spill_offset..], - ); - let values_vec_len = self.builder.ins().iconst(pointer_type, i64::from(len)); - - let mut callee_args = vec![vmctx, self.load_optional_memory(vmctx, memory)]; - - // remaining non-Wasm parameters - callee_args.extend(args[2..spill_offset].iter().copied()); - - callee_args.push(values_vec_ptr); - callee_args.push(values_vec_len); - - self.translate_intrinsic_libcall( - vmctx, - host::prepare_call, - &callee_args, - TrapSentinel::Falsy, - ); - } - - fn translate_sync_start(&mut self, callback: Option) { - match self.abi { - Abi::Wasm => {} - - Abi::Array => { - // This code can only be called from (FACT-generated) Wasm, so - // we don't need to support the array ABI. - self.builder.ins().trap(TRAP_INTERNAL_ASSERT); - return; - } - } - - let pointer_type = self.isa.pointer_type(); - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - let wasm_func_ty = &self.types[self.signature].unwrap_func(); - let mut callee_args = vec![vmctx, self.load_callback(vmctx, callback)]; - - // remaining non-Wasm parameters - callee_args.extend(args[2..].iter().copied()); - - let (values_vec_ptr, len) = self.compiler.allocate_stack_array_and_spill_args( - &WasmFuncType::new( - Box::new([]), - wasm_func_ty.returns().iter().copied().collect(), - ), - &mut self.builder, - &[], - ); - let values_vec_len = self.builder.ins().iconst(pointer_type, i64::from(len)); - - callee_args.push(values_vec_ptr); - callee_args.push(values_vec_len); - - let call = self.call_libcall(vmctx, host::sync_start, &callee_args); - - let succeeded = self.builder.func.dfg.inst_results(call)[0]; - self.raise_if_host_trapped(succeeded); - // After the host function has returned the results are loaded from - // `values_vec_ptr` and then returned. - let results = self.compiler.load_values_from_array( - wasm_func_ty.returns(), - &mut self.builder, - values_vec_ptr, - values_vec_len, - ); - self.builder.ins().return_(&results); - } - - fn translate_async_start( - &mut self, - callback: Option, - post_return: Option, - ) { - match self.abi { - Abi::Wasm => {} - - Abi::Array => { - // This code can only be called from (FACT-generated) Wasm, so - // we don't need to support the array ABI. - self.builder.ins().trap(TRAP_INTERNAL_ASSERT); - return; - } - } - - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - - let mut callee_args = vec![ - vmctx, - self.load_callback(vmctx, callback), - self.load_post_return(vmctx, post_return), - ]; - - // remaining parameters - callee_args.extend(args[2..].iter().copied()); - - self.translate_intrinsic_libcall( - vmctx, - host::async_start, - &callee_args, - TrapSentinel::NegativeOne, - ); - } - - fn translate_backpressure_set_call(&mut self, caller_instance: RuntimeComponentInstanceIndex) { - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - - let mut callee_args = vec![ - vmctx, - self.builder - .ins() - .iconst(ir::types::I32, i64::from(caller_instance.as_u32())), - ]; - - callee_args.extend(args[2..].iter().copied()); - - self.translate_intrinsic_libcall( - vmctx, - host::backpressure_set, - &callee_args, - TrapSentinel::Falsy, - ); - } - - fn translate_task_cancel_call(&mut self, caller_instance: RuntimeComponentInstanceIndex) { - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - - let callee_args = vec![ - vmctx, - self.builder - .ins() - .iconst(ir::types::I32, i64::from(caller_instance.as_u32())), - ]; - - self.translate_intrinsic_libcall( - vmctx, - host::task_cancel, - &callee_args, - TrapSentinel::Falsy, - ); - } - - fn translate_task_wait_or_poll_call( - &mut self, - caller_instance: RuntimeComponentInstanceIndex, - async_: bool, - memory: RuntimeMemoryIndex, - get_libcall: GetLibcallFn, - ) { - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - - let mut callee_args = vec![ - vmctx, - self.builder - .ins() - .iconst(ir::types::I32, i64::from(caller_instance.as_u32())), - self.builder - .ins() - .iconst(ir::types::I8, if async_ { 1 } else { 0 }), - self.load_memory(vmctx, memory), - ]; - - callee_args.extend(args[2..].iter().copied()); - - self.translate_intrinsic_libcall( - vmctx, - get_libcall, - &callee_args, - TrapSentinel::NegativeOne, - ); - } - - fn translate_yield_call(&mut self, async_: bool) { - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - - let callee_args = [ - vmctx, - self.builder - .ins() - .iconst(ir::types::I8, if async_ { 1 } else { 0 }), - ]; - - self.translate_intrinsic_libcall( - vmctx, - host::yield_, - &callee_args, - TrapSentinel::NegativeOne, - ); - } - - fn translate_subtask_drop_call(&mut self, caller_instance: RuntimeComponentInstanceIndex) { - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - - let mut callee_args = vec![ - vmctx, - self.builder - .ins() - .iconst(ir::types::I32, i64::from(caller_instance.as_u32())), - ]; - - callee_args.extend(args[2..].iter().copied()); + } - self.translate_intrinsic_libcall( - vmctx, - host::subtask_drop, - &callee_args, - TrapSentinel::Falsy, - ); + /// Convenience wrapper around `translate_hostcall` to enable type inference + /// on the `get_libcall` parameter here. + fn translate_libcall( + &mut self, + get_libcall: GetLibcallFn, + host_result: impl Into, + wasm_args: WasmArgs, + extra_host_args: impl FnOnce(&mut Self, &mut Vec), + ) { + self.translate_hostcall( + HostCallee::Libcall(get_libcall), + host_result.into(), + wasm_args, + extra_host_args, + ) } - fn translate_lower_import( + /// Translates an invokation of a host function and interpret the result. + /// + /// This is intended to be a relatively narrow waist which most intrinsics + /// go through. The configuration supported here is: + /// + /// * `host_callee` - what's being called, either a libcall or a lowered + /// function + /// * `host_result` - how to interpret the return value to see if it's a + /// trap + /// * `wasm_args` - how to pass wasm args to the host, either in registers + /// or on the stack + /// * `extra_host_args` - a closure used to push extra arguments just before + /// the wasm arguments are forwarded. + fn translate_hostcall( &mut self, - index: LoweredIndex, - options: &CanonicalOptions, - lower_ty: TypeFuncIndex, + host_callee: HostCallee, + host_result: impl Into, + wasm_args: WasmArgs, + extra_host_args: impl FnOnce(&mut Self, &mut Vec), ) { let pointer_type = self.isa.pointer_type(); - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; let wasm_func_ty = self.types[self.signature].unwrap_func(); - let (values_vec_ptr, values_vec_len) = self.store_wasm_arguments(&args[2..]); - - // Below this will incrementally build both the signature of the host - // function we're calling as well as the list of arguments since the - // list is somewhat long. - let mut callee_args = Vec::new(); - let mut host_sig = ir::Signature::new(CallConv::triple_default(self.isa.triple())); - - let CanonicalOptions { - instance, - callback, - post_return, - string_encoding, - async_, - core_type: _, - data_model, - } = *options; - - assert!(callback.is_none()); - - let LinearMemoryOptions { memory, realloc } = match data_model { - CanonicalOptionsDataModel::Gc {} => todo!("CM+GC"), - CanonicalOptionsDataModel::LinearMemory(opts) => opts, - }; - - // vmctx: *mut VMComponentContext - host_sig.params.push(ir::AbiParam::new(pointer_type)); - callee_args.push(vmctx); - - // data: *mut u8, - host_sig.params.push(ir::AbiParam::new(pointer_type)); - callee_args.push(self.builder.ins().load( - pointer_type, - MemFlags::trusted(), - vmctx, - i32::try_from(self.offsets.lowering_data(index)).unwrap(), - )); - - // ty: TypeFuncIndex, - host_sig.params.push(ir::AbiParam::new(ir::types::I32)); - callee_args.push( - self.builder - .ins() - .iconst(ir::types::I32, i64::from(lower_ty.as_u32())), - ); - - // caller_instance: RuntimeComponentInstanceIndex - host_sig.params.push(ir::AbiParam::new(ir::types::I32)); - callee_args.push( - self.builder - .ins() - .iconst(ir::types::I32, i64::from(instance.as_u32())), - ); - - // flags: *mut VMGlobalDefinition - host_sig.params.push(ir::AbiParam::new(pointer_type)); - callee_args.push( - self.builder - .ins() - .iadd_imm(vmctx, i64::from(self.offsets.instance_flags(instance))), - ); - - // memory: *mut VMMemoryDefinition - host_sig.params.push(ir::AbiParam::new(pointer_type)); - callee_args.push(self.load_optional_memory(vmctx, memory)); - - // realloc: *mut VMFuncRef - host_sig.params.push(ir::AbiParam::new(pointer_type)); - callee_args.push(self.load_realloc(vmctx, realloc)); - - // A post-return option is only valid on `canon.lift`'d functions so no - // valid component should have this specified for a lowering which this - // trampoline compiler is interested in. - assert!(post_return.is_none()); - - // string_encoding: StringEncoding - host_sig.params.push(ir::AbiParam::new(ir::types::I8)); - callee_args.push(self.string_encoding(string_encoding)); + // Load all parameters in an ABI-agnostic fashion, of which the + // `VMComponentContext` will be the first. + let params = self.abi_load_params(); + let vmctx = params[0]; + let wasm_params = ¶ms[2..]; - // async_: bool - host_sig.params.push(ir::AbiParam::new(ir::types::I8)); - callee_args.push( - self.builder - .ins() - .iconst(ir::types::I8, if async_ { 1 } else { 0 }), - ); + // Start building up arguments to the host. The first is always the + // vmctx. After is whatever `extra_host_args` appends, and then finally + // is what `WasmArgs` specifies. + let mut host_args = vec![vmctx]; + extra_host_args(self, &mut host_args); + let mut val_raw_ptr = None; + let mut val_raw_len = None; + match wasm_args { + // Wasm params are passed through as values themselves. + WasmArgs::InRegisters => host_args.extend(wasm_params.iter().copied()), + + // Wasm params are spilled and then the ptr/len is passed. + WasmArgs::ValRawList => { + let (ptr, len) = self.store_wasm_arguments(wasm_params); + val_raw_ptr = Some(ptr); + val_raw_len = Some(len); + host_args.push(ptr); + host_args.push(len); + } - // storage: *mut ValRaw - host_sig.params.push(ir::AbiParam::new(pointer_type)); - callee_args.push(values_vec_ptr); + // A mixture of the above two. + WasmArgs::InRegistersUpTo(n) => { + let (values_vec_ptr, len) = self.compiler.allocate_stack_array_and_spill_args( + &WasmFuncType::new( + wasm_func_ty.params().iter().skip(n).copied().collect(), + Box::new([]), + ), + &mut self.builder, + &wasm_params[n..], + ); + let values_vec_len = self.builder.ins().iconst(pointer_type, i64::from(len)); - // storage_len: usize - host_sig.params.push(ir::AbiParam::new(pointer_type)); - callee_args.push(values_vec_len); + host_args.extend(wasm_params[..n].iter().copied()); + host_args.push(values_vec_ptr); + host_args.push(values_vec_len); + } + } - // return value is a bool whether a trap was raised or not - host_sig.returns.push(ir::AbiParam::new(ir::types::I8)); + // Next perform the actual invocation of the host with `host_args`. + let call = match host_callee { + HostCallee::Libcall(get_libcall) => self.call_libcall(vmctx, get_libcall, &host_args), + HostCallee::Lowering(index) => { + // Load host function pointer from the vmcontext and then call that + // indirect function pointer with the list of arguments. + let host_fn = self.builder.ins().load( + pointer_type, + MemFlags::trusted(), + vmctx, + i32::try_from(self.offsets.lowering_callee(index)).unwrap(), + ); + let host_sig = { + let mut sig = ir::Signature::new(CallConv::triple_default(self.isa.triple())); + for param in host_args.iter() { + let ty = self.builder.func.dfg.value_type(*param); + sig.params.push(ir::AbiParam::new(ty)); + } + // return value is a bool whether a trap was raised or not + sig.returns.push(ir::AbiParam::new(ir::types::I8)); + self.builder.import_signature(sig) + }; + self.compiler.call_indirect_host( + &mut self.builder, + HostCall::ComponentLowerImport, + host_sig, + host_fn, + &host_args, + ) + } + }; - // Load host function pointer from the vmcontext and then call that - // indirect function pointer with the list of arguments. - let host_fn = self.builder.ins().load( - pointer_type, - MemFlags::trusted(), - vmctx, - i32::try_from(self.offsets.lowering_callee(index)).unwrap(), - ); - let host_sig = self.builder.import_signature(host_sig); - let call = self.compiler.call_indirect_host( - &mut self.builder, - HostCall::ComponentLowerImport, - host_sig, - host_fn, - &callee_args, - ); - let succeeded = self.builder.func.dfg.inst_results(call)[0]; + // Acquire the result of this function (if any) and interpret it + // according to `host_result`. + // + // NOte that all match arms here end with `abi_store_results` which + // accounts for the ABI of this function when storing results. + let result = self.builder.func.dfg.inst_results(call).get(0).copied(); + let result_ty = result.map(|v| self.builder.func.dfg.value_type(v)); + let expected = wasm_func_ty.returns(); + match host_result.into() { + HostResult::Sentinel(TrapSentinel::NegativeOne) => { + assert_eq!(expected.len(), 1); + let (result, result_ty) = (result.unwrap(), result_ty.unwrap()); + let result = match (result_ty, expected[0]) { + (ir::types::I64, WasmValType::I32) => { + self.raise_if_negative_one_and_truncate(result) + } + (ir::types::I64, WasmValType::I64) | (ir::types::I32, WasmValType::I32) => { + self.raise_if_negative_one(result) + } + other => panic!("unsupported NegativeOne combo {other:?}"), + }; + self.abi_store_results(&[result]); + } + HostResult::Sentinel(TrapSentinel::Falsy) => { + assert_eq!(expected.len(), 0); + self.raise_if_host_trapped(result.unwrap()); + self.abi_store_results(&[]); + } + HostResult::Sentinel(_) => todo!("support additional return types if/when necessary"), + HostResult::None => { + assert!(result.is_none()); + self.abi_store_results(&[]); + } - match self.abi { - Abi::Wasm => { - self.raise_if_host_trapped(succeeded); - // After the host function has returned the results are loaded from - // `values_vec_ptr` and then returned. + HostResult::MultiValue { ptr, len } => { + let ptr = ptr.or(val_raw_ptr).unwrap(); + let len = len.or(val_raw_len).unwrap(); + self.raise_if_host_trapped(result.unwrap()); let results = self.compiler.load_values_from_array( wasm_func_ty.returns(), &mut self.builder, - values_vec_ptr, - values_vec_len, + ptr, + len, ); - self.builder.ins().return_(&results); + self.abi_store_results(&results); } - Abi::Array => { - self.builder.ins().return_(&[succeeded]); - } - } - } - - fn translate_always_trap(&mut self) { - if self.tunables.signals_based_traps { - self.builder.ins().trap(TRAP_ALWAYS); - return; } - - let args = self.abi_load_params(); - let vmctx = args[0]; - - let (host_sig, index) = host::trap(self.isa, &mut self.builder.func); - let host_fn = self.load_libcall(vmctx, index); - - let code = self.builder.ins().iconst( - ir::types::I8, - i64::from(wasmtime_environ::Trap::AlwaysTrapAdapter as u8), - ); - self.compiler.call_indirect_host( - &mut self.builder, - index, - host_sig, - host_fn, - &[vmctx, code], - ); - let succeeded = self.builder.ins().iconst(ir::types::I8, 0); - self.raise_if_host_trapped(succeeded); - // debug trap in case execution actually falls through, but this - // shouldn't ever get hit at runtime. - self.builder.ins().trap(TRAP_INTERNAL_ASSERT); - } - - fn translate_resource_new(&mut self, resource: TypeResourceTableIndex) { - let args = self.abi_load_params(); - let vmctx = args[0]; - - // The arguments this shim passes along to the libcall are: - // - // * the vmctx - // * a constant value for this `ResourceNew` intrinsic - // * the wasm argument to wrap - let mut host_args = Vec::new(); - host_args.push(vmctx); - host_args.push( - self.builder - .ins() - .iconst(ir::types::I32, i64::from(resource.as_u32())), - ); - host_args.push(args[2]); - - // Currently this only support resources represented by `i32` - assert_eq!( - self.types[self.signature].unwrap_func().params()[0], - WasmValType::I32 - ); - let call = self.call_libcall(vmctx, host::resource_new32, &host_args); - let result = self.builder.func.dfg.inst_results(call)[0]; - let result = self.raise_if_negative_one_and_truncate(result); - self.abi_store_results(&[result]); } - fn translate_resource_rep(&mut self, resource: TypeResourceTableIndex) { - let args = self.abi_load_params(); - let vmctx = args[0]; - - // The arguments this shim passes along to the libcall are: - // - // * the vmctx - // * a constant value for this `ResourceRep` intrinsic - // * the wasm argument to unwrap - let mut host_args = Vec::new(); - host_args.push(vmctx); - host_args.push( - self.builder - .ins() - .iconst(ir::types::I32, i64::from(resource.as_u32())), - ); - host_args.push(args[2]); - - // Currently this only support resources represented by `i32` - assert_eq!( - self.types[self.signature].unwrap_func().returns()[0], - WasmValType::I32 - ); - let call = self.call_libcall(vmctx, host::resource_rep32, &host_args); - let result = self.builder.func.dfg.inst_results(call)[0]; - let result = self.raise_if_negative_one_and_truncate(result); - self.abi_store_results(&[result]); + fn index_value(&mut self, index: impl EntityRef) -> ir::Value { + self.builder + .ins() + .iconst(ir::types::I32, i64::try_from(index.index()).unwrap()) } fn translate_resource_drop(&mut self, resource: TypeResourceTableIndex) { @@ -978,15 +947,10 @@ impl<'a> TrampolineCompiler<'a> { host_args.push(args[2]); let call = self.call_libcall(vmctx, host::resource_drop, &host_args); - let should_run_destructor = self.builder.func.dfg.inst_results(call)[0]; // Immediately raise a trap if requested by the host - let minus_one = self.builder.ins().iconst(ir::types::I64, -1); - let succeeded = self - .builder - .ins() - .icmp(IntCC::NotEqual, should_run_destructor, minus_one); - self.raise_if_host_trapped(succeeded); + let should_run_destructor = + self.raise_if_negative_one(self.builder.func.dfg.inst_results(call)[0]); let resource_ty = self.types[resource].ty; let resource_def = self @@ -1140,85 +1104,6 @@ impl<'a> TrampolineCompiler<'a> { self.abi_store_results(&[]); } - /// Invokes a host libcall and returns the result. - /// - /// Only intended for simple trampolines and effectively acts as a bridge - /// from the wasm abi to host. - fn translate_host_libcall( - &mut self, - get_libcall: GetLibcallFn, - handle_results: fn(&mut Self, &mut Vec), - ) { - match self.abi { - Abi::Wasm => {} - - // These trampolines can only actually be called by Wasm, so - // let's assert that here. - Abi::Array => { - self.builder.ins().trap(TRAP_INTERNAL_ASSERT); - return; - } - } - - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - let mut host_args = vec![vmctx]; - host_args.extend(args[2..].iter().copied()); - - let call = self.call_libcall(vmctx, get_libcall, &host_args); - let mut results = self.builder.func.dfg.inst_results(call).to_vec(); - handle_results(self, &mut results); - self.builder.ins().return_(&results); - } - - fn translate_cancel_call(&mut self, ty: u32, async_: bool, get_libcall: GetLibcallFn) { - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - let mut callee_args = vec![ - vmctx, - self.builder.ins().iconst(ir::types::I32, i64::from(ty)), - self.builder - .ins() - .iconst(ir::types::I8, if async_ { 1 } else { 0 }), - ]; - - callee_args.extend(args[2..].iter().copied()); - - self.translate_intrinsic_libcall( - vmctx, - get_libcall, - &callee_args, - TrapSentinel::NegativeOne, - ); - } - - fn translate_subtask_cancel_call( - &mut self, - caller_instance: RuntimeComponentInstanceIndex, - async_: bool, - ) { - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - let mut callee_args = vec![ - vmctx, - self.builder - .ins() - .iconst(ir::types::I32, i64::from(caller_instance.as_u32())), - self.builder - .ins() - .iconst(ir::types::I8, if async_ { 1 } else { 0 }), - ]; - - callee_args.extend(args[2..].iter().copied()); - - self.translate_intrinsic_libcall( - vmctx, - host::subtask_cancel, - &callee_args, - TrapSentinel::NegativeOne, - ); - } - fn load_optional_memory( &mut self, vmctx: ir::Value, @@ -1239,23 +1124,6 @@ impl<'a> TrampolineCompiler<'a> { ) } - fn load_realloc( - &mut self, - vmctx: ir::Value, - realloc: Option, - ) -> ir::Value { - let pointer_type = self.isa.pointer_type(); - match realloc { - Some(idx) => self.builder.ins().load( - pointer_type, - MemFlags::trusted(), - vmctx, - i32::try_from(self.offsets.runtime_realloc(idx)).unwrap(), - ), - None => self.builder.ins().iconst(pointer_type, 0), - } - } - fn load_callback( &mut self, vmctx: ir::Value, @@ -1290,146 +1158,6 @@ impl<'a> TrampolineCompiler<'a> { } } - fn string_encoding(&mut self, string_encoding: StringEncoding) -> ir::Value { - self.builder - .ins() - .iconst(ir::types::I8, i64::from(string_encoding as u8)) - } - - fn translate_future_or_stream_call( - &mut self, - tys: &[u32], - options: Option<&CanonicalOptions>, - get_libcall: GetLibcallFn, - sentinel: TrapSentinel, - ) { - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - let mut callee_args = vec![vmctx]; - - if let Some(options) = options { - let mem_opts = match options.data_model { - CanonicalOptionsDataModel::Gc {} => todo!("CM+GC"), - CanonicalOptionsDataModel::LinearMemory(opts) => opts, - }; - - // memory: *mut VMMemoryDefinition - callee_args.push(self.load_memory(vmctx, mem_opts.memory.unwrap())); - // realloc: *mut VMFuncRef - callee_args.push(self.load_realloc(vmctx, mem_opts.realloc)); - // string_encoding: StringEncoding - callee_args.push(self.string_encoding(options.string_encoding)); - // async: bool - callee_args.push( - self.builder - .ins() - .iconst(ir::types::I8, if options.async_ { 1 } else { 0 }), - ); - } - - for ty in tys { - callee_args.push(self.builder.ins().iconst(ir::types::I32, i64::from(*ty))); - } - - callee_args.extend(args[2..].iter().copied()); - - self.translate_intrinsic_libcall(vmctx, get_libcall, &callee_args, sentinel); - } - - fn translate_flat_stream_call( - &mut self, - tys: &[u32], - options: &CanonicalOptions, - get_libcall: GetLibcallFn, - info: &CanonicalAbiInfo, - ) { - let mem_opts = match &options.data_model { - CanonicalOptionsDataModel::Gc {} => todo!("CM+GC"), - CanonicalOptionsDataModel::LinearMemory(opts) => opts, - }; - - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - let mut callee_args = vec![ - vmctx, - self.load_memory(vmctx, mem_opts.memory.unwrap()), - self.load_realloc(vmctx, mem_opts.realloc), - self.builder - .ins() - .iconst(ir::types::I8, if options.async_ { 1 } else { 0 }), - ]; - for ty in tys { - callee_args.push(self.builder.ins().iconst(ir::types::I32, i64::from(*ty))); - } - - callee_args.extend([ - self.builder - .ins() - .iconst(ir::types::I32, i64::from(info.size32)), - self.builder - .ins() - .iconst(ir::types::I32, i64::from(info.align32)), - ]); - - callee_args.extend(args[2..].iter().copied()); - - self.translate_intrinsic_libcall( - vmctx, - get_libcall, - &callee_args, - TrapSentinel::NegativeOne, - ); - } - - fn translate_error_context_call( - &mut self, - ty: TypeComponentLocalErrorContextTableIndex, - options: &CanonicalOptions, - get_libcall: GetLibcallFn, - sentinel: TrapSentinel, - ) { - let mem_opts = match &options.data_model { - CanonicalOptionsDataModel::Gc {} => todo!("CM+GC"), - CanonicalOptionsDataModel::LinearMemory(opts) => opts, - }; - - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - let mut callee_args = vec![ - vmctx, - self.load_memory(vmctx, mem_opts.memory.unwrap()), - self.load_realloc(vmctx, mem_opts.realloc), - self.string_encoding(options.string_encoding), - self.builder - .ins() - .iconst(ir::types::I32, i64::from(ty.as_u32())), - ]; - - callee_args.extend(args[2..].iter().copied()); - - self.translate_intrinsic_libcall(vmctx, get_libcall, &callee_args, sentinel); - } - - fn translate_error_context_drop_call(&mut self, ty: TypeComponentLocalErrorContextTableIndex) { - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - let mut callee_args = vec![ - vmctx, - self.builder - .ins() - .iconst(ir::types::I32, i64::from(ty.as_u32())), - ]; - - callee_args.extend(args[2..].iter().copied()); - - self.translate_intrinsic_libcall( - vmctx, - host::error_context_drop, - &callee_args, - TrapSentinel::Falsy, - ); - } - /// Loads a host function pointer for a libcall stored at the `offset` /// provided in the libcalls array. /// @@ -1546,32 +1274,6 @@ impl<'a> TrampolineCompiler<'a> { self.compiler .call_indirect_host(&mut self.builder, index, host_sig, host_fn, args) } - - fn translate_context_get(&mut self, slot: u32) { - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - let slot = self.builder.ins().iconst(ir::types::I32, i64::from(slot)); - - self.translate_intrinsic_libcall( - vmctx, - host::context_get, - &[vmctx, slot], - TrapSentinel::NegativeOne, - ); - } - - fn translate_context_set(&mut self, slot: u32) { - let args = self.abi_load_params(); - let vmctx = args[0]; - let slot = self.builder.ins().iconst(ir::types::I32, i64::from(slot)); - - self.translate_intrinsic_libcall( - vmctx, - host::context_set, - &[vmctx, slot, args[2]], - TrapSentinel::Falsy, - ); - } } impl ComponentCompiler for Compiler { @@ -1581,7 +1283,7 @@ impl ComponentCompiler for Compiler { types: &ComponentTypesBuilder, index: TrampolineIndex, tunables: &Tunables, - symbol: &str, + _symbol: &str, ) -> Result> { let compile = |abi: Abi| -> Result<_> { let mut compiler = self.function_compiler(); @@ -1625,15 +1327,14 @@ impl ComponentCompiler for Compiler { c.translate(&component.trampolines[index]); c.builder.finalize(); - let symbol = match abi { - Abi::Wasm => format!("{symbol}_wasm_call"), - Abi::Array => format!("{symbol}_array_call"), - }; + compiler.cx.abi = Some(abi); + Ok(CompiledFunctionBody { - code: Box::new(compiler.finish(&symbol)?), + code: Box::new(Some(compiler.cx)), needs_gc_heap: false, }) }; + Ok(AllCallFunc { wasm_call: compile(Abi::Wasm)?, array_call: compile(Abi::Array)?, diff --git a/crates/cranelift/src/debug/gc.rs b/crates/cranelift/src/debug/gc.rs index ec1b055894..c51bfa6177 100644 --- a/crates/cranelift/src/debug/gc.rs +++ b/crates/cranelift/src/debug/gc.rs @@ -1,10 +1,9 @@ +use crate::debug::Reader; use crate::debug::transform::AddressTransform; -use crate::debug::{Compilation, Reader}; use gimli::UnitSectionOffset; use gimli::constants; use gimli::read; use std::collections::{HashMap, HashSet}; -use wasmtime_environ::{PrimaryMap, StaticModuleIndex}; #[derive(Debug)] pub struct Dependencies { @@ -68,17 +67,13 @@ impl Dependencies { } pub fn build_dependencies( - compilation: &mut Compilation<'_>, - dwp: &Option>>, - at: &PrimaryMap, + dwarf: &read::Dwarf>, + at: &AddressTransform, ) -> read::Result { let mut deps = Dependencies::new(); - for (i, translation) in compilation.translations.iter() { - let dwarf = &translation.debuginfo.dwarf; - let mut units = dwarf.units(); - while let Some(unit) = units.next()? { - build_unit_dependencies(unit, dwarf, dwp, &at[i], &mut deps)?; - } + let mut units = dwarf.units(); + while let Some(unit) = units.next()? { + build_unit_dependencies(unit, dwarf, at, &mut deps)?; } Ok(deps) } @@ -86,7 +81,6 @@ pub fn build_dependencies( fn build_unit_dependencies( header: read::UnitHeader>, dwarf: &read::Dwarf>, - dwp: &Option>>, at: &AddressTransform, deps: &mut Dependencies, ) -> read::Result<()> { @@ -94,17 +88,6 @@ fn build_unit_dependencies( let mut tree = unit.entries_tree(None)?; let root = tree.root()?; build_die_dependencies(root, dwarf, &unit, at, deps)?; - - if let Some(dwarf_package) = dwp { - if let Some(dwo_id) = unit.dwo_id { - if let Some(cu) = dwarf_package.find_cu(dwo_id, dwarf)? { - if let Some(unit_header) = cu.debug_info.units().next()? { - build_unit_dependencies(unit_header, &cu, &None, at, deps)?; - } - } - } - } - Ok(()) } diff --git a/crates/cranelift/src/debug/transform/mod.rs b/crates/cranelift/src/debug/transform/mod.rs index 922ad2b4a2..4eb20832df 100644 --- a/crates/cranelift/src/debug/transform/mod.rs +++ b/crates/cranelift/src/debug/transform/mod.rs @@ -163,8 +163,6 @@ pub fn transform_dwarf( ) .flatten(); - let reachable = build_dependencies(compilation, &dwarf_package, &transforms)?.get_reachable(); - let out_encoding = gimli::Encoding { format: gimli::Format::Dwarf32, version: 4, // TODO: this should be configurable @@ -185,9 +183,8 @@ pub fn transform_dwarf( let addr_tr = &transforms[module]; let di = &translation.debuginfo; - let context = DebugInputContext { - reachable: &reachable, - }; + let reachable = build_dependencies(&di.dwarf, addr_tr)?.get_reachable(); + let out_module_synthetic_unit = ModuleSyntheticUnit::new( module, compilation, @@ -202,25 +199,31 @@ pub fn transform_dwarf( while let Some(header) = iter.next().unwrap_or(None) { let unit = di.dwarf.unit(header)?; - let mut resolved_unit = None; + let mut split_unit = None; let mut split_dwarf = None; + let mut split_reachable = None; if unit.dwo_id.is_some() { if let Some(dwarf_package) = &dwarf_package { if let Some((fused, fused_dwarf)) = replace_unit_from_split_dwarf(&unit, dwarf_package, &di.dwarf) { - resolved_unit = Some(fused); + split_reachable = + Some(build_dependencies(&fused_dwarf, addr_tr)?.get_reachable()); + split_unit = Some(fused); split_dwarf = Some(fused_dwarf); } } } + let context = DebugInputContext { + reachable: split_reachable.as_ref().unwrap_or(&reachable), + }; if let Some((id, ref_map, pending_refs)) = clone_unit( compilation, module, &unit, - resolved_unit.as_ref(), + split_unit.as_ref(), split_dwarf.as_ref(), &context, &addr_tr, diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs index 21fb0fdab8..63a4a3c9c1 100644 --- a/crates/cranelift/src/func_environ.rs +++ b/crates/cranelift/src/func_environ.rs @@ -2,7 +2,7 @@ mod gc; use crate::compiler::Compiler; use crate::translate::{ - FuncTranslationState, GlobalVariable, Heap, HeapData, StructFieldsVec, TableData, TableSize, + FuncTranslationStacks, GlobalVariable, Heap, HeapData, StructFieldsVec, TableData, TableSize, TargetEnvironment, }; use crate::{BuiltinFunctionSignatures, TRAP_INTERNAL_ASSERT}; @@ -14,7 +14,7 @@ use cranelift_codegen::ir::types::*; use cranelift_codegen::ir::{self, types}; use cranelift_codegen::ir::{ArgumentPurpose, ConstantData, Function, InstBuilder, MemFlags}; use cranelift_codegen::isa::{TargetFrontendConfig, TargetIsa}; -use cranelift_entity::packed_option::ReservedValue; +use cranelift_entity::packed_option::{PackedOption, ReservedValue}; use cranelift_entity::{EntityRef, PrimaryMap, SecondaryMap}; use cranelift_frontend::Variable; use cranelift_frontend::{FuncInstBuilder, FunctionBuilder}; @@ -95,11 +95,12 @@ wasmtime_environ::foreach_builtin_function!(declare_function_signatures); pub struct FuncEnvironment<'module_environment> { compiler: &'module_environment Compiler, isa: &'module_environment (dyn TargetIsa + 'module_environment), - module: &'module_environment Module, + pub(crate) module: &'module_environment Module, types: &'module_environment ModuleTypesBuilder, wasm_func_ty: &'module_environment WasmFuncType, sig_ref_to_ty: SecondaryMap>, needs_gc_heap: bool, + entities: WasmEntities, #[cfg(feature = "gc")] ty_to_gc_layout: std::collections::HashMap< @@ -123,9 +124,6 @@ pub struct FuncEnvironment<'module_environment> { /// Heaps implementing WebAssembly linear memories. heaps: PrimaryMap, - /// Cranelift tables we have created to implement Wasm tables. - tables: SecondaryMap>, - /// The Cranelift global holding the vmctx address. vmctx: Option, @@ -197,6 +195,7 @@ impl<'module_environment> FuncEnvironment<'module_environment> { wasm_func_ty, sig_ref_to_ty: SecondaryMap::default(), needs_gc_heap: false, + entities: WasmEntities::default(), #[cfg(feature = "gc")] ty_to_gc_layout: std::collections::HashMap::new(), @@ -208,7 +207,6 @@ impl<'module_environment> FuncEnvironment<'module_environment> { gc_heap_bound: None, heaps: PrimaryMap::default(), - tables: SecondaryMap::default(), vmctx: None, vm_store_context: None, pcc_vmctx_memtype: None, @@ -802,89 +800,6 @@ impl<'module_environment> FuncEnvironment<'module_environment> { } } - /// Set up the necessary preamble definitions in `func` to access the table identified - /// by `index`. - /// - /// The index space covers both imported and locally declared tables. - fn ensure_table_exists(&mut self, func: &mut ir::Function, index: TableIndex) { - if self.tables[index].is_some() { - return; - } - - let pointer_type = self.pointer_type(); - - let (ptr, base_offset, current_elements_offset) = { - let vmctx = self.vmctx(func); - if let Some(def_index) = self.module.defined_table_index(index) { - let base_offset = - i32::try_from(self.offsets.vmctx_vmtable_definition_base(def_index)).unwrap(); - let current_elements_offset = i32::try_from( - self.offsets - .vmctx_vmtable_definition_current_elements(def_index), - ) - .unwrap(); - (vmctx, base_offset, current_elements_offset) - } else { - let from_offset = self.offsets.vmctx_vmtable_from(index); - let table = func.create_global_value(ir::GlobalValueData::Load { - base: vmctx, - offset: Offset32::new(i32::try_from(from_offset).unwrap()), - global_type: pointer_type, - flags: MemFlags::trusted().with_readonly().with_can_move(), - }); - let base_offset = i32::from(self.offsets.vmtable_definition_base()); - let current_elements_offset = - i32::from(self.offsets.vmtable_definition_current_elements()); - (table, base_offset, current_elements_offset) - } - }; - - let table = &self.module.tables[index]; - let element_size = if table.ref_type.is_vmgcref_type() { - // For GC-managed references, tables store `Option`s. - ir::types::I32.bytes() - } else { - self.reference_type(table.ref_type.heap_type).0.bytes() - }; - - let base_gv = func.create_global_value(ir::GlobalValueData::Load { - base: ptr, - offset: Offset32::new(base_offset), - global_type: pointer_type, - flags: if Some(table.limits.min) == table.limits.max { - // A fixed-size table can't be resized so its base address won't - // change. - MemFlags::trusted().with_readonly().with_can_move() - } else { - MemFlags::trusted() - }, - }); - - let bound = if Some(table.limits.min) == table.limits.max { - TableSize::Static { - bound: table.limits.min, - } - } else { - TableSize::Dynamic { - bound_gv: func.create_global_value(ir::GlobalValueData::Load { - base: ptr, - offset: Offset32::new(current_elements_offset), - global_type: ir::Type::int( - u16::from(self.offsets.size_of_vmtable_definition_current_elements()) * 8, - ) - .unwrap(), - flags: MemFlags::trusted(), - }), - } - }; - - self.tables[index] = Some(TableData { - base_gv, - bound, - element_size, - }); - } - fn get_or_init_func_ref_table_elem( &mut self, builder: &mut FunctionBuilder, @@ -893,8 +808,7 @@ impl<'module_environment> FuncEnvironment<'module_environment> { cold_blocks: bool, ) -> ir::Value { let pointer_type = self.pointer_type(); - self.ensure_table_exists(builder.func, table_index); - let table_data = self.tables[table_index].clone().unwrap(); + let table_data = self.get_or_create_table(builder.func, table_index); // To support lazy initialization of table // contents, we check for a null entry here, and @@ -1230,173 +1144,614 @@ impl<'module_environment> FuncEnvironment<'module_environment> { pub fn needs_gc_heap(&self) -> bool { self.needs_gc_heap } -} - -struct Call<'a, 'func, 'module_env> { - builder: &'a mut FunctionBuilder<'func>, - env: &'a mut FuncEnvironment<'module_env>, - tail: bool, -} - -enum CheckIndirectCallTypeSignature { - Runtime, - StaticMatch { - /// Whether or not the funcref may be null or if it's statically known - /// to not be null. - may_be_null: bool, - }, - StaticTrap, -} -impl<'a, 'func, 'module_env> Call<'a, 'func, 'module_env> { - /// Create a new `Call` site that will do regular, non-tail calls. - pub fn new( - builder: &'a mut FunctionBuilder<'func>, - env: &'a mut FuncEnvironment<'module_env>, - ) -> Self { - Call { - builder, - env, - tail: false, - } + /// Get the number of Wasm parameters for the given function. + pub(crate) fn num_params_for_func(&self, function_index: FuncIndex) -> usize { + let ty = self.module.functions[function_index] + .signature + .unwrap_module_type_index(); + self.types[ty].unwrap_func().params().len() } - /// Create a new `Call` site that will perform tail calls. - pub fn new_tail( - builder: &'a mut FunctionBuilder<'func>, - env: &'a mut FuncEnvironment<'module_env>, - ) -> Self { - Call { - builder, - env, - tail: true, - } + /// Get the number of Wasm parameters for the given function type. + /// + /// Panics on non-function types. + pub(crate) fn num_params_for_function_type(&self, type_index: TypeIndex) -> usize { + let ty = self.module.types[type_index].unwrap_module_type_index(); + self.types[ty].unwrap_func().params().len() } +} - /// Do a direct call to the given callee function. - pub fn direct_call( - mut self, - callee_index: FuncIndex, - callee: ir::FuncRef, - call_args: &[ir::Value], - ) -> WasmResult { - let mut real_call_args = Vec::with_capacity(call_args.len() + 2); - let caller_vmctx = self - .builder - .func - .special_param(ArgumentPurpose::VMContext) - .unwrap(); - - // Handle direct calls to locally-defined functions. - if !self.env.module.is_imported_function(callee_index) { - // First append the callee vmctx address, which is the same as the caller vmctx in - // this case. - real_call_args.push(caller_vmctx); +#[derive(Default)] +pub(crate) struct WasmEntities { + /// Map from a Wasm global index from this module to its implementation in + /// the Cranelift function we are building. + pub(crate) globals: SecondaryMap>, - // Then append the caller vmctx address. - real_call_args.push(caller_vmctx); + /// Map from a Wasm memory index to its `Heap` implementation in the + /// Cranelift function we are building. + pub(crate) memories: SecondaryMap>, - // Then append the regular call arguments. - real_call_args.extend_from_slice(call_args); + /// Map from an (interned) Wasm type index from this module to its + /// `ir::SigRef` in the Cranelift function we are building. + pub(crate) sig_refs: SecondaryMap>, - // Finally, make the direct call! - return Ok(self.direct_call_inst(callee, &real_call_args)); - } + /// Map from a Wasm function index to its associated function reference in + /// the Cranelift function we are building. + pub(crate) func_refs: SecondaryMap>, - // Handle direct calls to imported functions. We use an indirect call - // so that we don't have to patch the code at runtime. - let pointer_type = self.env.pointer_type(); - let sig_ref = self.builder.func.dfg.ext_funcs[callee].signature; - let vmctx = self.env.vmctx(self.builder.func); - let base = self.builder.ins().global_value(pointer_type, vmctx); + /// Map from a Wasm table index to its associated implementation in the + /// Cranelift function we are building. + pub(crate) tables: SecondaryMap>, +} - let mem_flags = ir::MemFlags::trusted().with_readonly().with_can_move(); +macro_rules! define_get_or_create_methods { + ( $( $name:ident ( $map:ident ) : $create:ident : $key:ty => $val:ty ; )* ) => { + $( + pub(crate) fn $name(&mut self, func: &mut ir::Function, key: $key) -> $val { + match self.entities.$map[key].clone().into() { + Some(val) => val, + None => { + let val = self.$create(func, key); + self.entities.$map[key] = Some(val.clone()).into(); + val + } + } + } + )* + }; +} - // Load the callee address. - let body_offset = i32::try_from( - self.env - .offsets - .vmctx_vmfunction_import_wasm_call(callee_index), - ) - .unwrap(); +impl FuncEnvironment<'_> { + define_get_or_create_methods! { + get_or_create_global(globals) : make_global : GlobalIndex => GlobalVariable; + get_or_create_heap(memories) : make_heap : MemoryIndex => Heap; + get_or_create_interned_sig_ref(sig_refs) : make_sig_ref : ModuleInternedTypeIndex => ir::SigRef; + get_or_create_func_ref(func_refs) : make_func_ref : FuncIndex => ir::FuncRef; + get_or_create_table(tables) : make_table : TableIndex => TableData; + } - // First append the callee vmctx address. - let vmctx_offset = - i32::try_from(self.env.offsets.vmctx_vmfunction_import_vmctx(callee_index)).unwrap(); - let callee_vmctx = self - .builder - .ins() - .load(pointer_type, mem_flags, base, vmctx_offset); - real_call_args.push(callee_vmctx); - real_call_args.push(caller_vmctx); + fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable { + let ty = self.module.globals[index].wasm_ty; - // Then append the regular call arguments. - real_call_args.extend_from_slice(call_args); + if ty.is_vmgcref_type() { + // Although reference-typed globals live at the same memory location as + // any other type of global at the same index would, getting or + // setting them requires ref counting barriers. Therefore, we need + // to use `GlobalVariable::Custom`, as that is the only kind of + // `GlobalVariable` for which translation supports custom + // access translation. + return GlobalVariable::Custom; + } - // If we statically know the imported function (e.g. this is a - // component-to-component call where we statically know both components) - // then we can actually still make a direct call (although we do have to - // pass the callee's vmctx that we just loaded, not our own). Otherwise, - // we really do an indirect call. - if self.env.translation.known_imported_functions[callee_index].is_some() { - Ok(self.direct_call_inst(callee, &real_call_args)) - } else { - let func_addr = self - .builder - .ins() - .load(pointer_type, mem_flags, base, body_offset); - Ok(self.indirect_call_inst(sig_ref, func_addr, &real_call_args)) + let (gv, offset) = self.get_global_location(func, index); + GlobalVariable::Memory { + gv, + offset: offset.into(), + ty: super::value_type(self.isa, ty), } } - /// Do an indirect call through the given funcref table. - pub fn indirect_call( - mut self, - features: &WasmFeatures, - table_index: TableIndex, - ty_index: TypeIndex, - sig_ref: ir::SigRef, - callee: ir::Value, - call_args: &[ir::Value], - ) -> WasmResult> { - let (code_ptr, callee_vmctx) = match self.check_and_load_code_and_callee_vmctx( - features, - table_index, - ty_index, - callee, - false, - )? { - Some(pair) => pair, - None => return Ok(None), - }; - - self.unchecked_call_impl(sig_ref, code_ptr, callee_vmctx, call_args) - .map(Some) + pub(crate) fn get_or_create_sig_ref( + &mut self, + func: &mut ir::Function, + ty: TypeIndex, + ) -> ir::SigRef { + let ty = self.module.types[ty].unwrap_module_type_index(); + self.get_or_create_interned_sig_ref(func, ty) } - fn check_and_load_code_and_callee_vmctx( + fn make_sig_ref( &mut self, - features: &WasmFeatures, - table_index: TableIndex, - ty_index: TypeIndex, - callee: ir::Value, - cold_blocks: bool, - ) -> WasmResult> { - // Get the funcref pointer from the table. - let funcref_ptr = self.env.get_or_init_func_ref_table_elem( - self.builder, - table_index, - callee, - cold_blocks, - ); - - // If necessary, check the signature. - let check = - self.check_indirect_call_type_signature(features, table_index, ty_index, funcref_ptr); + func: &mut ir::Function, + index: ModuleInternedTypeIndex, + ) -> ir::SigRef { + let wasm_func_ty = self.types[index].unwrap_func(); + let sig = crate::wasm_call_signature(self.isa, wasm_func_ty, &self.tunables); + let sig_ref = func.import_signature(sig); + self.sig_ref_to_ty[sig_ref] = Some(wasm_func_ty); + sig_ref + } - let trap_code = match check { - // `funcref_ptr` is checked at runtime that its type matches, - // meaning that if code gets this far it's guaranteed to not be + fn make_func_ref(&mut self, func: &mut ir::Function, index: FuncIndex) -> ir::FuncRef { + let sig = self.module.functions[index] + .signature + .unwrap_module_type_index(); + let wasm_func_ty = self.types[sig].unwrap_func(); + let sig = crate::wasm_call_signature(self.isa, wasm_func_ty, &self.tunables); + let signature = func.import_signature(sig); + self.sig_ref_to_ty[signature] = Some(wasm_func_ty); + let name = + ir::ExternalName::User(func.declare_imported_user_function(ir::UserExternalName { + namespace: crate::NS_WASM_FUNC, + index: index.as_u32(), + })); + func.import_function(ir::ExtFuncData { + name, + signature, + + // the value of this flag determines the codegen for calls to this + // function. if this flag is `false` then absolute relocations will + // be generated for references to the function, which requires + // load-time relocation resolution. if this flag is set to `true` + // then relative relocations are emitted which can be resolved at + // object-link-time, just after all functions are compiled. + // + // this flag is set to `true` for functions defined in the object + // we'll be defining in this compilation unit, or everything local + // to the wasm module. this means that between functions in a wasm + // module there's relative calls encoded. all calls external to a + // wasm module (e.g. imports or libcalls) are either encoded through + // the `vmcontext` as relative jumps (hence no relocations) or + // they're libcalls with absolute relocations. + colocated: self.module.defined_func_index(index).is_some() + || self.translation.known_imported_functions[index].is_some(), + }) + } + + fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> Heap { + let pointer_type = self.pointer_type(); + let memory = self.module.memories[index]; + let is_shared = memory.shared; + + let (base_ptr, base_offset, current_length_offset, ptr_memtype) = { + let vmctx = self.vmctx(func); + if let Some(def_index) = self.module.defined_memory_index(index) { + if is_shared { + // As with imported memory, the `VMMemoryDefinition` for a + // shared memory is stored elsewhere. We store a `*mut + // VMMemoryDefinition` to it and dereference that when + // atomically growing it. + let from_offset = self.offsets.vmctx_vmmemory_pointer(def_index); + let (memory, def_mt) = self.global_load_from_vmctx_with_memory_type( + func, + from_offset, + ir::MemFlags::trusted().with_readonly().with_can_move(), + ); + let base_offset = i32::from(self.offsets.ptr.vmmemory_definition_base()); + let current_length_offset = + i32::from(self.offsets.ptr.vmmemory_definition_current_length()); + (memory, base_offset, current_length_offset, def_mt) + } else { + let owned_index = self.module.owned_memory_index(def_index); + let owned_base_offset = + self.offsets.vmctx_vmmemory_definition_base(owned_index); + let owned_length_offset = self + .offsets + .vmctx_vmmemory_definition_current_length(owned_index); + let current_base_offset = i32::try_from(owned_base_offset).unwrap(); + let current_length_offset = i32::try_from(owned_length_offset).unwrap(); + ( + vmctx, + current_base_offset, + current_length_offset, + self.pcc_vmctx_memtype, + ) + } + } else { + let from_offset = self.offsets.vmctx_vmmemory_import_from(index); + let (memory, def_mt) = self.global_load_from_vmctx_with_memory_type( + func, + from_offset, + ir::MemFlags::trusted().with_readonly().with_can_move(), + ); + let base_offset = i32::from(self.offsets.ptr.vmmemory_definition_base()); + let current_length_offset = + i32::from(self.offsets.ptr.vmmemory_definition_current_length()); + (memory, base_offset, current_length_offset, def_mt) + } + }; + + let bound = func.create_global_value(ir::GlobalValueData::Load { + base: base_ptr, + offset: Offset32::new(current_length_offset), + global_type: pointer_type, + flags: MemFlags::trusted(), + }); + + let (base_fact, pcc_memory_type) = self.make_pcc_base_fact_and_type_for_memory( + func, + memory, + base_offset, + current_length_offset, + ptr_memtype, + bound, + ); + + let base = self.make_heap_base(func, memory, base_ptr, base_offset, base_fact); + + self.heaps.push(HeapData { + base, + bound, + pcc_memory_type, + memory, + }) + } + + pub(crate) fn make_heap_base( + &self, + func: &mut Function, + memory: Memory, + ptr: ir::GlobalValue, + offset: i32, + fact: Option, + ) -> ir::GlobalValue { + let pointer_type = self.pointer_type(); + + let mut flags = ir::MemFlags::trusted().with_checked().with_can_move(); + if !memory.memory_may_move(self.tunables) { + flags.set_readonly(); + } + + let heap_base = func.create_global_value(ir::GlobalValueData::Load { + base: ptr, + offset: Offset32::new(offset), + global_type: pointer_type, + flags, + }); + func.global_value_facts[heap_base] = fact; + heap_base + } + + pub(crate) fn make_pcc_base_fact_and_type_for_memory( + &mut self, + func: &mut Function, + memory: Memory, + base_offset: i32, + current_length_offset: i32, + ptr_memtype: Option, + heap_bound: ir::GlobalValue, + ) -> (Option, Option) { + // If we have a declared maximum, we can make this a "static" heap, which is + // allocated up front and never moved. + let host_page_size_log2 = self.target_config().page_size_align_log2; + let (base_fact, memory_type) = if !memory + .can_elide_bounds_check(self.tunables, host_page_size_log2) + { + if let Some(ptr_memtype) = ptr_memtype { + // Create a memtype representing the untyped memory region. + let data_mt = func.create_memory_type(ir::MemoryTypeData::DynamicMemory { + gv: heap_bound, + size: self.tunables.memory_guard_size, + }); + // This fact applies to any pointer to the start of the memory. + let base_fact = ir::Fact::dynamic_base_ptr(data_mt); + // This fact applies to the length. + let length_fact = ir::Fact::global_value( + u16::try_from(self.isa.pointer_type().bits()).unwrap(), + heap_bound, + ); + // Create a field in the vmctx for the base pointer. + match &mut func.memory_types[ptr_memtype] { + ir::MemoryTypeData::Struct { size, fields } => { + let base_offset = u64::try_from(base_offset).unwrap(); + fields.push(ir::MemoryTypeField { + offset: base_offset, + ty: self.isa.pointer_type(), + // Read-only field from the PoV of PCC checks: + // don't allow stores to this field. (Even if + // it is a dynamic memory whose base can + // change, that update happens inside the + // runtime, not in generated code.) + readonly: true, + fact: Some(base_fact.clone()), + }); + let current_length_offset = u64::try_from(current_length_offset).unwrap(); + fields.push(ir::MemoryTypeField { + offset: current_length_offset, + ty: self.isa.pointer_type(), + // As above, read-only; only the runtime modifies it. + readonly: true, + fact: Some(length_fact), + }); + + let pointer_size = u64::from(self.isa.pointer_type().bytes()); + let fields_end = std::cmp::max( + base_offset + pointer_size, + current_length_offset + pointer_size, + ); + *size = std::cmp::max(*size, fields_end); + } + _ => { + panic!("Bad memtype"); + } + } + // Apply a fact to the base pointer. + (Some(base_fact), Some(data_mt)) + } else { + (None, None) + } + } else { + if let Some(ptr_memtype) = ptr_memtype { + // Create a memtype representing the untyped memory region. + let data_mt = func.create_memory_type(ir::MemoryTypeData::Memory { + size: self + .tunables + .memory_reservation + .checked_add(self.tunables.memory_guard_size) + .expect("Memory plan has overflowing size plus guard"), + }); + // This fact applies to any pointer to the start of the memory. + let base_fact = Fact::Mem { + ty: data_mt, + min_offset: 0, + max_offset: 0, + nullable: false, + }; + // Create a field in the vmctx for the base pointer. + match &mut func.memory_types[ptr_memtype] { + ir::MemoryTypeData::Struct { size, fields } => { + let offset = u64::try_from(base_offset).unwrap(); + fields.push(ir::MemoryTypeField { + offset, + ty: self.isa.pointer_type(), + // Read-only field from the PoV of PCC checks: + // don't allow stores to this field. (Even if + // it is a dynamic memory whose base can + // change, that update happens inside the + // runtime, not in generated code.) + readonly: true, + fact: Some(base_fact.clone()), + }); + *size = std::cmp::max( + *size, + offset + u64::from(self.isa.pointer_type().bytes()), + ); + } + _ => { + panic!("Bad memtype"); + } + } + // Apply a fact to the base pointer. + (Some(base_fact), Some(data_mt)) + } else { + (None, None) + } + }; + (base_fact, memory_type) + } + + fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> TableData { + let pointer_type = self.pointer_type(); + + let (ptr, base_offset, current_elements_offset) = { + let vmctx = self.vmctx(func); + if let Some(def_index) = self.module.defined_table_index(index) { + let base_offset = + i32::try_from(self.offsets.vmctx_vmtable_definition_base(def_index)).unwrap(); + let current_elements_offset = i32::try_from( + self.offsets + .vmctx_vmtable_definition_current_elements(def_index), + ) + .unwrap(); + (vmctx, base_offset, current_elements_offset) + } else { + let from_offset = self.offsets.vmctx_vmtable_from(index); + let table = func.create_global_value(ir::GlobalValueData::Load { + base: vmctx, + offset: Offset32::new(i32::try_from(from_offset).unwrap()), + global_type: pointer_type, + flags: MemFlags::trusted().with_readonly().with_can_move(), + }); + let base_offset = i32::from(self.offsets.vmtable_definition_base()); + let current_elements_offset = + i32::from(self.offsets.vmtable_definition_current_elements()); + (table, base_offset, current_elements_offset) + } + }; + + let table = &self.module.tables[index]; + let element_size = if table.ref_type.is_vmgcref_type() { + // For GC-managed references, tables store `Option`s. + ir::types::I32.bytes() + } else { + self.reference_type(table.ref_type.heap_type).0.bytes() + }; + + let base_gv = func.create_global_value(ir::GlobalValueData::Load { + base: ptr, + offset: Offset32::new(base_offset), + global_type: pointer_type, + flags: if Some(table.limits.min) == table.limits.max { + // A fixed-size table can't be resized so its base address won't + // change. + MemFlags::trusted().with_readonly().with_can_move() + } else { + MemFlags::trusted() + }, + }); + + let bound = if Some(table.limits.min) == table.limits.max { + TableSize::Static { + bound: table.limits.min, + } + } else { + TableSize::Dynamic { + bound_gv: func.create_global_value(ir::GlobalValueData::Load { + base: ptr, + offset: Offset32::new(current_elements_offset), + global_type: ir::Type::int( + u16::from(self.offsets.size_of_vmtable_definition_current_elements()) * 8, + ) + .unwrap(), + flags: MemFlags::trusted(), + }), + } + }; + + TableData { + base_gv, + bound, + element_size, + } + } +} + +struct Call<'a, 'func, 'module_env> { + builder: &'a mut FunctionBuilder<'func>, + env: &'a mut FuncEnvironment<'module_env>, + tail: bool, +} + +enum CheckIndirectCallTypeSignature { + Runtime, + StaticMatch { + /// Whether or not the funcref may be null or if it's statically known + /// to not be null. + may_be_null: bool, + }, + StaticTrap, +} + +impl<'a, 'func, 'module_env> Call<'a, 'func, 'module_env> { + /// Create a new `Call` site that will do regular, non-tail calls. + pub fn new( + builder: &'a mut FunctionBuilder<'func>, + env: &'a mut FuncEnvironment<'module_env>, + ) -> Self { + Call { + builder, + env, + tail: false, + } + } + + /// Create a new `Call` site that will perform tail calls. + pub fn new_tail( + builder: &'a mut FunctionBuilder<'func>, + env: &'a mut FuncEnvironment<'module_env>, + ) -> Self { + Call { + builder, + env, + tail: true, + } + } + + /// Do a direct call to the given callee function. + pub fn direct_call( + mut self, + callee_index: FuncIndex, + callee: ir::FuncRef, + call_args: &[ir::Value], + ) -> WasmResult { + let mut real_call_args = Vec::with_capacity(call_args.len() + 2); + let caller_vmctx = self + .builder + .func + .special_param(ArgumentPurpose::VMContext) + .unwrap(); + + // Handle direct calls to locally-defined functions. + if !self.env.module.is_imported_function(callee_index) { + // First append the callee vmctx address, which is the same as the caller vmctx in + // this case. + real_call_args.push(caller_vmctx); + + // Then append the caller vmctx address. + real_call_args.push(caller_vmctx); + + // Then append the regular call arguments. + real_call_args.extend_from_slice(call_args); + + // Finally, make the direct call! + return Ok(self.direct_call_inst(callee, &real_call_args)); + } + + // Handle direct calls to imported functions. We use an indirect call + // so that we don't have to patch the code at runtime. + let pointer_type = self.env.pointer_type(); + let sig_ref = self.builder.func.dfg.ext_funcs[callee].signature; + let vmctx = self.env.vmctx(self.builder.func); + let base = self.builder.ins().global_value(pointer_type, vmctx); + + let mem_flags = ir::MemFlags::trusted().with_readonly().with_can_move(); + + // Load the callee address. + let body_offset = i32::try_from( + self.env + .offsets + .vmctx_vmfunction_import_wasm_call(callee_index), + ) + .unwrap(); + + // First append the callee vmctx address. + let vmctx_offset = + i32::try_from(self.env.offsets.vmctx_vmfunction_import_vmctx(callee_index)).unwrap(); + let callee_vmctx = self + .builder + .ins() + .load(pointer_type, mem_flags, base, vmctx_offset); + real_call_args.push(callee_vmctx); + real_call_args.push(caller_vmctx); + + // Then append the regular call arguments. + real_call_args.extend_from_slice(call_args); + + // If we statically know the imported function (e.g. this is a + // component-to-component call where we statically know both components) + // then we can actually still make a direct call (although we do have to + // pass the callee's vmctx that we just loaded, not our own). Otherwise, + // we really do an indirect call. + if self.env.translation.known_imported_functions[callee_index].is_some() { + Ok(self.direct_call_inst(callee, &real_call_args)) + } else { + let func_addr = self + .builder + .ins() + .load(pointer_type, mem_flags, base, body_offset); + Ok(self.indirect_call_inst(sig_ref, func_addr, &real_call_args)) + } + } + + /// Do an indirect call through the given funcref table. + pub fn indirect_call( + mut self, + features: &WasmFeatures, + table_index: TableIndex, + ty_index: TypeIndex, + sig_ref: ir::SigRef, + callee: ir::Value, + call_args: &[ir::Value], + ) -> WasmResult> { + let (code_ptr, callee_vmctx) = match self.check_and_load_code_and_callee_vmctx( + features, + table_index, + ty_index, + callee, + false, + )? { + Some(pair) => pair, + None => return Ok(None), + }; + + self.unchecked_call_impl(sig_ref, code_ptr, callee_vmctx, call_args) + .map(Some) + } + + fn check_and_load_code_and_callee_vmctx( + &mut self, + features: &WasmFeatures, + table_index: TableIndex, + ty_index: TypeIndex, + callee: ir::Value, + cold_blocks: bool, + ) -> WasmResult> { + // Get the funcref pointer from the table. + let funcref_ptr = self.env.get_or_init_func_ref_table_elem( + self.builder, + table_index, + callee, + cold_blocks, + ); + + // If necessary, check the signature. + let check = + self.check_indirect_call_type_signature(features, table_index, ty_index, funcref_ptr); + + let trap_code = match check { + // `funcref_ptr` is checked at runtime that its type matches, + // meaning that if code gets this far it's guaranteed to not be // null. That means nothing in `unchecked_call` can fail. CheckIndirectCallTypeSignature::Runtime => None, @@ -1842,8 +2197,7 @@ impl FuncEnvironment<'_> { index: ir::Value, ) -> WasmResult { let table = self.module.tables[table_index]; - self.ensure_table_exists(builder.func, table_index); - let table_data = self.tables[table_index].clone().unwrap(); + let table_data = self.get_or_create_table(builder.func, table_index); let heap_ty = table.ref_type.heap_type; match heap_ty.top() { // GC-managed types. @@ -1876,8 +2230,7 @@ impl FuncEnvironment<'_> { index: ir::Value, ) -> WasmResult<()> { let table = self.module.tables[table_index]; - self.ensure_table_exists(builder.func, table_index); - let table_data = self.tables[table_index].clone().unwrap(); + let table_data = self.get_or_create_table(builder.func, table_index); let heap_ty = table.ref_type.heap_type; match heap_ty.top() { // GC-managed types. @@ -2250,415 +2603,150 @@ impl FuncEnvironment<'_> { test_ty: WasmRefType, gc_ref: ir::Value, gc_ref_ty: WasmRefType, - ) -> WasmResult { - gc::translate_ref_test(self, builder, test_ty, gc_ref, gc_ref_ty) - } - - pub fn translate_ref_null( - &mut self, - mut pos: cranelift_codegen::cursor::FuncCursor, - ht: WasmHeapType, - ) -> WasmResult { - Ok(match ht.top() { - WasmHeapTopType::Func => pos.ins().iconst(self.pointer_type(), 0), - // NB: null GC references don't need to be in stack maps. - WasmHeapTopType::Any | WasmHeapTopType::Extern | WasmHeapTopType::Exn => { - pos.ins().iconst(types::I32, 0) - } - WasmHeapTopType::Cont => todo!(), // FIXME: #10248 stack switching support. - }) - } - - pub fn translate_ref_is_null( - &mut self, - mut pos: cranelift_codegen::cursor::FuncCursor, - value: ir::Value, - ty: WasmRefType, - ) -> WasmResult { - // If we know the type is not nullable, then we don't actually need to - // check for null. - if !ty.nullable { - return Ok(pos.ins().iconst(ir::types::I32, 0)); - } - - let byte_is_null = - pos.ins() - .icmp_imm(cranelift_codegen::ir::condcodes::IntCC::Equal, value, 0); - Ok(pos.ins().uextend(ir::types::I32, byte_is_null)) - } - - pub fn translate_ref_func( - &mut self, - mut pos: cranelift_codegen::cursor::FuncCursor<'_>, - func_index: FuncIndex, - ) -> WasmResult { - let func_index = pos.ins().iconst(I32, func_index.as_u32() as i64); - let ref_func = self.builtin_functions.ref_func(&mut pos.func); - let vmctx = self.vmctx_val(&mut pos); - - let call_inst = pos.ins().call(ref_func, &[vmctx, func_index]); - Ok(pos.func.dfg.first_result(call_inst)) - } - - pub fn translate_custom_global_get( - &mut self, - builder: &mut FunctionBuilder, - index: GlobalIndex, - ) -> WasmResult { - let global_ty = self.module.globals[index]; - let wasm_ty = global_ty.wasm_ty; - debug_assert!( - wasm_ty.is_vmgcref_type(), - "We only use GlobalVariable::Custom for VMGcRef types" - ); - let WasmValType::Ref(ref_ty) = wasm_ty else { - unreachable!() - }; - - let (gv, offset) = self.get_global_location(builder.func, index); - let gv = builder.ins().global_value(self.pointer_type(), gv); - let src = builder.ins().iadd_imm(gv, i64::from(offset)); - - gc::gc_compiler(self)?.translate_read_gc_reference( - self, - builder, - ref_ty, - src, - if global_ty.mutability { - ir::MemFlags::trusted() - } else { - ir::MemFlags::trusted().with_readonly().with_can_move() - }, - ) - } - - pub fn translate_custom_global_set( - &mut self, - builder: &mut FunctionBuilder, - index: GlobalIndex, - value: ir::Value, - ) -> WasmResult<()> { - let ty = self.module.globals[index].wasm_ty; - debug_assert!( - ty.is_vmgcref_type(), - "We only use GlobalVariable::Custom for VMGcRef types" - ); - let WasmValType::Ref(ty) = ty else { - unreachable!() - }; - - let (gv, offset) = self.get_global_location(builder.func, index); - let gv = builder.ins().global_value(self.pointer_type(), gv); - let src = builder.ins().iadd_imm(gv, i64::from(offset)); - - gc::gc_compiler(self)?.translate_write_gc_reference( - self, - builder, - ty, - src, - value, - ir::MemFlags::trusted(), - ) - } - - pub fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> WasmResult { - let pointer_type = self.pointer_type(); - let memory = self.module.memories[index]; - let is_shared = memory.shared; - - let (base_ptr, base_offset, current_length_offset, ptr_memtype) = { - let vmctx = self.vmctx(func); - if let Some(def_index) = self.module.defined_memory_index(index) { - if is_shared { - // As with imported memory, the `VMMemoryDefinition` for a - // shared memory is stored elsewhere. We store a `*mut - // VMMemoryDefinition` to it and dereference that when - // atomically growing it. - let from_offset = self.offsets.vmctx_vmmemory_pointer(def_index); - let (memory, def_mt) = self.global_load_from_vmctx_with_memory_type( - func, - from_offset, - ir::MemFlags::trusted().with_readonly().with_can_move(), - ); - let base_offset = i32::from(self.offsets.ptr.vmmemory_definition_base()); - let current_length_offset = - i32::from(self.offsets.ptr.vmmemory_definition_current_length()); - (memory, base_offset, current_length_offset, def_mt) - } else { - let owned_index = self.module.owned_memory_index(def_index); - let owned_base_offset = - self.offsets.vmctx_vmmemory_definition_base(owned_index); - let owned_length_offset = self - .offsets - .vmctx_vmmemory_definition_current_length(owned_index); - let current_base_offset = i32::try_from(owned_base_offset).unwrap(); - let current_length_offset = i32::try_from(owned_length_offset).unwrap(); - ( - vmctx, - current_base_offset, - current_length_offset, - self.pcc_vmctx_memtype, - ) - } - } else { - let from_offset = self.offsets.vmctx_vmmemory_import_from(index); - let (memory, def_mt) = self.global_load_from_vmctx_with_memory_type( - func, - from_offset, - ir::MemFlags::trusted().with_readonly().with_can_move(), - ); - let base_offset = i32::from(self.offsets.ptr.vmmemory_definition_base()); - let current_length_offset = - i32::from(self.offsets.ptr.vmmemory_definition_current_length()); - (memory, base_offset, current_length_offset, def_mt) - } - }; - - let bound = func.create_global_value(ir::GlobalValueData::Load { - base: base_ptr, - offset: Offset32::new(current_length_offset), - global_type: pointer_type, - flags: MemFlags::trusted(), - }); - - let (base_fact, pcc_memory_type) = self.make_pcc_base_fact_and_type_for_memory( - func, - memory, - base_offset, - current_length_offset, - ptr_memtype, - bound, - ); - - let base = self.make_heap_base(func, memory, base_ptr, base_offset, base_fact); - - Ok(self.heaps.push(HeapData { - base, - bound, - pcc_memory_type, - memory, - })) + ) -> WasmResult { + gc::translate_ref_test(self, builder, test_ty, gc_ref, gc_ref_ty) } - pub(crate) fn make_heap_base( - &self, - func: &mut Function, - memory: Memory, - ptr: ir::GlobalValue, - offset: i32, - fact: Option, - ) -> ir::GlobalValue { - let pointer_type = self.pointer_type(); + pub fn translate_ref_null( + &mut self, + mut pos: cranelift_codegen::cursor::FuncCursor, + ht: WasmHeapType, + ) -> WasmResult { + Ok(match ht.top() { + WasmHeapTopType::Func => pos.ins().iconst(self.pointer_type(), 0), + // NB: null GC references don't need to be in stack maps. + WasmHeapTopType::Any | WasmHeapTopType::Extern | WasmHeapTopType::Exn => { + pos.ins().iconst(types::I32, 0) + } + WasmHeapTopType::Cont => todo!(), // FIXME: #10248 stack switching support. + }) + } - let mut flags = ir::MemFlags::trusted().with_checked().with_can_move(); - if !memory.memory_may_move(self.tunables) { - flags.set_readonly(); + pub fn translate_ref_is_null( + &mut self, + mut pos: cranelift_codegen::cursor::FuncCursor, + value: ir::Value, + ty: WasmRefType, + ) -> WasmResult { + // If we know the type is not nullable, then we don't actually need to + // check for null. + if !ty.nullable { + return Ok(pos.ins().iconst(ir::types::I32, 0)); } - let heap_base = func.create_global_value(ir::GlobalValueData::Load { - base: ptr, - offset: Offset32::new(offset), - global_type: pointer_type, - flags, - }); - func.global_value_facts[heap_base] = fact; - heap_base + let byte_is_null = + pos.ins() + .icmp_imm(cranelift_codegen::ir::condcodes::IntCC::Equal, value, 0); + Ok(pos.ins().uextend(ir::types::I32, byte_is_null)) } - pub(crate) fn make_pcc_base_fact_and_type_for_memory( + pub fn translate_ref_func( &mut self, - func: &mut Function, - memory: Memory, - base_offset: i32, - current_length_offset: i32, - ptr_memtype: Option, - heap_bound: ir::GlobalValue, - ) -> (Option, Option) { - // If we have a declared maximum, we can make this a "static" heap, which is - // allocated up front and never moved. - let host_page_size_log2 = self.target_config().page_size_align_log2; - let (base_fact, memory_type) = if !memory - .can_elide_bounds_check(self.tunables, host_page_size_log2) - { - if let Some(ptr_memtype) = ptr_memtype { - // Create a memtype representing the untyped memory region. - let data_mt = func.create_memory_type(ir::MemoryTypeData::DynamicMemory { - gv: heap_bound, - size: self.tunables.memory_guard_size, - }); - // This fact applies to any pointer to the start of the memory. - let base_fact = ir::Fact::dynamic_base_ptr(data_mt); - // This fact applies to the length. - let length_fact = ir::Fact::global_value( - u16::try_from(self.isa.pointer_type().bits()).unwrap(), - heap_bound, - ); - // Create a field in the vmctx for the base pointer. - match &mut func.memory_types[ptr_memtype] { - ir::MemoryTypeData::Struct { size, fields } => { - let base_offset = u64::try_from(base_offset).unwrap(); - fields.push(ir::MemoryTypeField { - offset: base_offset, - ty: self.isa.pointer_type(), - // Read-only field from the PoV of PCC checks: - // don't allow stores to this field. (Even if - // it is a dynamic memory whose base can - // change, that update happens inside the - // runtime, not in generated code.) - readonly: true, - fact: Some(base_fact.clone()), - }); - let current_length_offset = u64::try_from(current_length_offset).unwrap(); - fields.push(ir::MemoryTypeField { - offset: current_length_offset, - ty: self.isa.pointer_type(), - // As above, read-only; only the runtime modifies it. - readonly: true, - fact: Some(length_fact), - }); + mut pos: cranelift_codegen::cursor::FuncCursor<'_>, + func_index: FuncIndex, + ) -> WasmResult { + let func_index = pos.ins().iconst(I32, func_index.as_u32() as i64); + let ref_func = self.builtin_functions.ref_func(&mut pos.func); + let vmctx = self.vmctx_val(&mut pos); - let pointer_size = u64::from(self.isa.pointer_type().bytes()); - let fields_end = std::cmp::max( - base_offset + pointer_size, - current_length_offset + pointer_size, - ); - *size = std::cmp::max(*size, fields_end); - } - _ => { - panic!("Bad memtype"); - } + let call_inst = pos.ins().call(ref_func, &[vmctx, func_index]); + Ok(pos.func.dfg.first_result(call_inst)) + } + + pub(crate) fn translate_global_get( + &mut self, + builder: &mut FunctionBuilder<'_>, + global_index: GlobalIndex, + ) -> WasmResult { + match self.get_or_create_global(builder.func, global_index) { + GlobalVariable::Memory { gv, offset, ty } => { + let addr = builder.ins().global_value(self.pointer_type(), gv); + let mut flags = ir::MemFlags::trusted(); + // Store vector globals in little-endian format to avoid + // byte swaps on big-endian platforms since at-rest vectors + // should already be in little-endian format anyway. + if ty.is_vector() { + flags.set_endianness(ir::Endianness::Little); } - // Apply a fact to the base pointer. - (Some(base_fact), Some(data_mt)) - } else { - (None, None) + // Put globals in the "table" abstract heap category as well. + flags.set_alias_region(Some(ir::AliasRegion::Table)); + Ok(builder.ins().load(ty, flags, addr, offset)) } - } else { - if let Some(ptr_memtype) = ptr_memtype { - // Create a memtype representing the untyped memory region. - let data_mt = func.create_memory_type(ir::MemoryTypeData::Memory { - size: self - .tunables - .memory_reservation - .checked_add(self.tunables.memory_guard_size) - .expect("Memory plan has overflowing size plus guard"), - }); - // This fact applies to any pointer to the start of the memory. - let base_fact = Fact::Mem { - ty: data_mt, - min_offset: 0, - max_offset: 0, - nullable: false, + GlobalVariable::Custom => { + let global_ty = self.module.globals[global_index]; + let wasm_ty = global_ty.wasm_ty; + debug_assert!( + wasm_ty.is_vmgcref_type(), + "We only use GlobalVariable::Custom for VMGcRef types" + ); + let WasmValType::Ref(ref_ty) = wasm_ty else { + unreachable!() }; - // Create a field in the vmctx for the base pointer. - match &mut func.memory_types[ptr_memtype] { - ir::MemoryTypeData::Struct { size, fields } => { - let offset = u64::try_from(base_offset).unwrap(); - fields.push(ir::MemoryTypeField { - offset, - ty: self.isa.pointer_type(), - // Read-only field from the PoV of PCC checks: - // don't allow stores to this field. (Even if - // it is a dynamic memory whose base can - // change, that update happens inside the - // runtime, not in generated code.) - readonly: true, - fact: Some(base_fact.clone()), - }); - *size = std::cmp::max( - *size, - offset + u64::from(self.isa.pointer_type().bytes()), - ); - } - _ => { - panic!("Bad memtype"); - } - } - // Apply a fact to the base pointer. - (Some(base_fact), Some(data_mt)) - } else { - (None, None) - } - }; - (base_fact, memory_type) - } - pub fn make_global( - &mut self, - func: &mut ir::Function, - index: GlobalIndex, - ) -> WasmResult { - let ty = self.module.globals[index].wasm_ty; + let (gv, offset) = self.get_global_location(builder.func, global_index); + let gv = builder.ins().global_value(self.pointer_type(), gv); + let src = builder.ins().iadd_imm(gv, i64::from(offset)); - if ty.is_vmgcref_type() { - // Although reference-typed globals live at the same memory location as - // any other type of global at the same index would, getting or - // setting them requires ref counting barriers. Therefore, we need - // to use `GlobalVariable::Custom`, as that is the only kind of - // `GlobalVariable` for which translation supports custom - // access translation. - return Ok(GlobalVariable::Custom); + gc::gc_compiler(self)?.translate_read_gc_reference( + self, + builder, + ref_ty, + src, + if global_ty.mutability { + ir::MemFlags::trusted() + } else { + ir::MemFlags::trusted().with_readonly().with_can_move() + }, + ) + } } - - let (gv, offset) = self.get_global_location(func, index); - Ok(GlobalVariable::Memory { - gv, - offset: offset.into(), - ty: super::value_type(self.isa, ty), - }) } - pub fn make_indirect_sig( + pub(crate) fn translate_global_set( &mut self, - func: &mut ir::Function, - index: TypeIndex, - ) -> WasmResult { - let interned_index = self.module.types[index].unwrap_module_type_index(); - let wasm_func_ty = self.types[interned_index].unwrap_func(); - let sig = crate::wasm_call_signature(self.isa, wasm_func_ty, &self.tunables); - let sig_ref = func.import_signature(sig); - self.sig_ref_to_ty[sig_ref] = Some(wasm_func_ty); - Ok(sig_ref) - } + builder: &mut FunctionBuilder<'_>, + global_index: GlobalIndex, + val: ir::Value, + ) -> WasmResult<()> { + match self.get_or_create_global(builder.func, global_index) { + GlobalVariable::Memory { gv, offset, ty } => { + let addr = builder.ins().global_value(self.pointer_type(), gv); + let mut flags = ir::MemFlags::trusted(); + // Like `global.get`, store globals in little-endian format. + if ty.is_vector() { + flags.set_endianness(ir::Endianness::Little); + } + // Put globals in the "table" abstract heap category as well. + flags.set_alias_region(Some(ir::AliasRegion::Table)); + debug_assert_eq!(ty, builder.func.dfg.value_type(val)); + builder.ins().store(flags, val, addr, offset); + self.update_global(builder, global_index, val); + } + GlobalVariable::Custom => { + let ty = self.module.globals[global_index].wasm_ty; + debug_assert!( + ty.is_vmgcref_type(), + "We only use GlobalVariable::Custom for VMGcRef types" + ); + let WasmValType::Ref(ty) = ty else { + unreachable!() + }; - pub fn make_direct_func( - &mut self, - func: &mut ir::Function, - index: FuncIndex, - ) -> WasmResult { - let sig = self.module.functions[index] - .signature - .unwrap_module_type_index(); - let wasm_func_ty = self.types[sig].unwrap_func(); - let sig = crate::wasm_call_signature(self.isa, wasm_func_ty, &self.tunables); - let signature = func.import_signature(sig); - self.sig_ref_to_ty[signature] = Some(wasm_func_ty); - let name = - ir::ExternalName::User(func.declare_imported_user_function(ir::UserExternalName { - namespace: crate::NS_WASM_FUNC, - index: index.as_u32(), - })); - Ok(func.import_function(ir::ExtFuncData { - name, - signature, + let (gv, offset) = self.get_global_location(builder.func, global_index); + let gv = builder.ins().global_value(self.pointer_type(), gv); + let src = builder.ins().iadd_imm(gv, i64::from(offset)); - // the value of this flag determines the codegen for calls to this - // function. if this flag is `false` then absolute relocations will - // be generated for references to the function, which requires - // load-time relocation resolution. if this flag is set to `true` - // then relative relocations are emitted which can be resolved at - // object-link-time, just after all functions are compiled. - // - // this flag is set to `true` for functions defined in the object - // we'll be defining in this compilation unit, or everything local - // to the wasm module. this means that between functions in a wasm - // module there's relative calls encoded. all calls external to a - // wasm module (e.g. imports or libcalls) are either encoded through - // the `vmcontext` as relative jumps (hence no relocations) or - // they're libcalls with absolute relocations. - colocated: self.module.defined_func_index(index).is_some() - || self.translation.known_imported_functions[index].is_some(), - })) + gc::gc_compiler(self)?.translate_write_gc_reference( + self, + builder, + ty, + src, + val, + ir::MemFlags::trusted(), + )? + } + } + Ok(()) } pub fn translate_call_indirect( @@ -2823,7 +2911,6 @@ impl FuncEnvironment<'_> { &mut self, builder: &mut FunctionBuilder<'_>, index: MemoryIndex, - _heap: Heap, val: ir::Value, ) -> WasmResult { let mut pos = builder.cursor(); @@ -2855,7 +2942,6 @@ impl FuncEnvironment<'_> { &mut self, mut pos: FuncCursor<'_>, index: MemoryIndex, - _heap: Heap, ) -> WasmResult { let pointer_type = self.pointer_type(); let vmctx = self.vmctx(&mut pos.func); @@ -2940,9 +3026,7 @@ impl FuncEnvironment<'_> { &mut self, builder: &mut FunctionBuilder<'_>, src_index: MemoryIndex, - _src_heap: Heap, dst_index: MemoryIndex, - _dst_heap: Heap, dst: ir::Value, src: ir::Value, len: ir::Value, @@ -2977,7 +3061,6 @@ impl FuncEnvironment<'_> { &mut self, builder: &mut FunctionBuilder<'_>, memory_index: MemoryIndex, - _heap: Heap, dst: ir::Value, val: ir::Value, len: ir::Value, @@ -3001,7 +3084,6 @@ impl FuncEnvironment<'_> { &mut self, builder: &mut FunctionBuilder<'_>, memory_index: MemoryIndex, - _heap: Heap, seg_index: u32, dst: ir::Value, src: ir::Value, @@ -3038,8 +3120,7 @@ impl FuncEnvironment<'_> { pos: FuncCursor, table_index: TableIndex, ) -> WasmResult { - self.ensure_table_exists(pos.func, table_index); - let table_data = self.tables[table_index].as_ref().unwrap(); + let table_data = self.get_or_create_table(pos.func, table_index); let index_type = index_type_to_ir_type(self.table(table_index).idx_type); Ok(table_data.bound.bound(&*self.isa, pos, index_type)) } @@ -3207,7 +3288,7 @@ impl FuncEnvironment<'_> { op: &Operator, _operand_types: Option<&[WasmValType]>, builder: &mut FunctionBuilder, - state: &FuncTranslationState, + state: &FuncTranslationStacks, ) -> WasmResult<()> { if self.tunables.consume_fuel { self.fuel_before_op(op, builder, state.reachable()); @@ -3220,7 +3301,7 @@ impl FuncEnvironment<'_> { op: &Operator, _operand_types: Option<&[WasmValType]>, builder: &mut FunctionBuilder, - state: &FuncTranslationState, + state: &FuncTranslationStacks, ) -> WasmResult<()> { if self.tunables.consume_fuel && state.reachable() { self.fuel_after_op(op, builder); @@ -3238,7 +3319,7 @@ impl FuncEnvironment<'_> { pub fn before_translate_function( &mut self, builder: &mut FunctionBuilder, - _state: &FuncTranslationState, + _state: &FuncTranslationStacks, ) -> WasmResult<()> { // If an explicit stack limit is requested, emit one here at the start // of the function. @@ -3275,7 +3356,7 @@ impl FuncEnvironment<'_> { pub fn after_translate_function( &mut self, builder: &mut FunctionBuilder, - state: &FuncTranslationState, + state: &FuncTranslationStacks, ) -> WasmResult<()> { if self.tunables.consume_fuel && state.reachable() { self.fuel_function_exit(builder); @@ -3366,12 +3447,12 @@ impl FuncEnvironment<'_> { pub fn update_global( &mut self, builder: &mut FunctionBuilder, - global_index: u32, + global_index: GlobalIndex, value: ir::Value, ) { #[cfg(feature = "wmemcheck")] if self.compiler.wmemcheck { - if global_index == 0 { + if global_index.index() == 0 { // We are making the assumption that global 0 is the auxiliary stack pointer. let update_stack_pointer = self.builtin_functions.update_stack_pointer(builder.func); diff --git a/crates/cranelift/src/func_environ/gc.rs b/crates/cranelift/src/func_environ/gc.rs index 2c5aa19a83..157c8dcac9 100644 --- a/crates/cranelift/src/func_environ/gc.rs +++ b/crates/cranelift/src/func_environ/gc.rs @@ -26,7 +26,7 @@ pub use imp::*; /// How to initialize a newly-allocated array's elements. #[derive(Clone, Copy)] -#[cfg_attr(not(any(feature = "gc-null", feature = "gc-drc")), allow(dead_code))] +#[cfg_attr(not(feature = "gc"), expect(dead_code))] pub enum ArrayInit<'a> { /// Initialize the array's elements with the given values. Elems(&'a [ir::Value]), diff --git a/crates/cranelift/src/func_environ/gc/enabled.rs b/crates/cranelift/src/func_environ/gc/enabled.rs index c6bca5dff0..07afcf7dad 100644 --- a/crates/cranelift/src/func_environ/gc/enabled.rs +++ b/crates/cranelift/src/func_environ/gc/enabled.rs @@ -58,7 +58,10 @@ pub fn gc_compiler(func_env: &mut FuncEnvironment<'_>) -> WasmResult { /// Get the length (as an `i32`-typed `ir::Value`) of these array elements. - #[cfg_attr(not(any(feature = "gc-drc", feature = "gc-null")), allow(dead_code))] + #[cfg_attr( + not(any(feature = "gc-drc", feature = "gc-null")), + expect(dead_code, reason = "easier to define") + )] fn len(self, pos: &mut FuncCursor) -> ir::Value { match self { ArrayInit::Fill { len, .. } => len, @@ -473,7 +482,10 @@ impl ArrayInit<'_> { } /// Initialize a newly-allocated array's elements. - #[cfg_attr(not(any(feature = "gc-drc", feature = "gc-null")), allow(dead_code))] + #[cfg_attr( + not(any(feature = "gc-drc", feature = "gc-null")), + expect(dead_code, reason = "easier to define") + )] fn initialize( self, func_env: &mut FuncEnvironment<'_>, @@ -1140,7 +1152,10 @@ fn uextend_i32_to_pointer_type( /// in its initialization. /// /// Traps if the size overflows. -#[cfg_attr(not(any(feature = "gc-drc", feature = "gc-null")), allow(dead_code))] +#[cfg_attr( + not(any(feature = "gc-drc", feature = "gc-null")), + expect(dead_code, reason = "easier to define") +)] fn emit_array_size( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, @@ -1185,7 +1200,10 @@ fn emit_array_size( /// Common helper for struct-field initialization that can be reused across /// collectors. -#[cfg_attr(not(any(feature = "gc-drc", feature = "gc-null")), allow(dead_code))] +#[cfg_attr( + not(any(feature = "gc-drc", feature = "gc-null")), + expect(dead_code, reason = "easier to define") +)] fn initialize_struct_fields( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, @@ -1386,7 +1404,10 @@ impl FuncEnvironment<'_> { /// reference is null or is an `i31ref`; otherwise, it will be zero. /// /// This method is collector-agnostic. - #[cfg_attr(not(feature = "gc-drc"), allow(dead_code))] + #[cfg_attr( + not(feature = "gc-drc"), + expect(dead_code, reason = "easier to define") + )] fn gc_ref_is_null_or_i31( &mut self, builder: &mut FunctionBuilder, diff --git a/crates/cranelift/src/lib.rs b/crates/cranelift/src/lib.rs index 2ec67f6293..93748ad401 100644 --- a/crates/cranelift/src/lib.rs +++ b/crates/cranelift/src/lib.rs @@ -455,7 +455,7 @@ const I31_REF_DISCRIMINANT: u32 = 1; /// from reachable to unreachable state and the like from callees to callers. /// /// Marked `must_use` to force callers to update -/// `FuncTranslationState::reachable` as necessary. +/// `FuncTranslationStacks::reachable` as necessary. #[derive(PartialEq, Eq)] #[must_use] enum Reachability { @@ -463,6 +463,6 @@ enum Reachability { Reachable(T), /// The Wasm execution state has been determined to be statically /// unreachable. It is the receiver of this value's responsibility to update - /// `FuncTranslationState::reachable` as necessary. + /// `FuncTranslationStacks::reachable` as necessary. Unreachable, } diff --git a/crates/cranelift/src/translate/code_translator.rs b/crates/cranelift/src/translate/code_translator.rs index 7642390975..693c0a7394 100644 --- a/crates/cranelift/src/translate/code_translator.rs +++ b/crates/cranelift/src/translate/code_translator.rs @@ -74,8 +74,8 @@ use crate::Reachability; use crate::bounds_checks::{BoundsCheck, bounds_check_and_compute_addr}; use crate::func_environ::{Extension, FuncEnvironment}; -use crate::translate::environ::{GlobalVariable, StructFieldsVec}; -use crate::translate::state::{ControlStackFrame, ElseData, FuncTranslationState}; +use crate::translate::environ::StructFieldsVec; +use crate::translate::stack::{ControlStackFrame, ElseData, FuncTranslationStacks}; use crate::translate::translation_utils::{ block_with_params, blocktype_params_results, f32_translation, f64_translation, }; @@ -121,13 +121,13 @@ pub fn translate_operator( op: &Operator, operand_types: Option<&[WasmValType]>, builder: &mut FunctionBuilder, - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, environ: &mut FuncEnvironment<'_>, ) -> WasmResult<()> { log::trace!("Translating Wasm opcode: {op:?}"); - if !state.reachable { - translate_unreachable_operator(validator, &op, builder, state, environ)?; + if !stack.reachable { + translate_unreachable_operator(validator, &op, builder, stack, environ)?; return Ok(()); } @@ -146,12 +146,12 @@ pub fn translate_operator( ***********************************************************************************/ Operator::LocalGet { local_index } => { let val = builder.use_var(Variable::from_u32(*local_index)); - state.push1(val); + stack.push1(val); let label = ValueLabel::from_u32(*local_index); builder.set_val_label(val, label); } Operator::LocalSet { local_index } => { - let mut val = state.pop1(); + let mut val = stack.pop1(); // Ensure SIMD values are cast to their default Cranelift type, I8x16. let ty = builder.func.dfg.value_type(val); @@ -164,7 +164,7 @@ pub fn translate_operator( builder.set_val_label(val, label); } Operator::LocalTee { local_index } => { - let mut val = state.peek1(); + let mut val = stack.peek1(); // Ensure SIMD values are cast to their default Cranelift type, I8x16. let ty = builder.func.dfg.value_type(val); @@ -180,90 +180,54 @@ pub fn translate_operator( * `get_global` and `set_global` are handled by the environment. ***********************************************************************************/ Operator::GlobalGet { global_index } => { - let val = match state.get_global(builder.func, *global_index, environ)? { - GlobalVariable::Memory { gv, offset, ty } => { - let addr = builder.ins().global_value(environ.pointer_type(), gv); - let mut flags = ir::MemFlags::trusted(); - // Store vector globals in little-endian format to avoid - // byte swaps on big-endian platforms since at-rest vectors - // should already be in little-endian format anyway. - if ty.is_vector() { - flags.set_endianness(ir::Endianness::Little); - } - // Put globals in the "table" abstract heap category as well. - flags.set_alias_region(Some(ir::AliasRegion::Table)); - builder.ins().load(ty, flags, addr, offset) - } - GlobalVariable::Custom => environ - .translate_custom_global_get(builder, GlobalIndex::from_u32(*global_index))?, - }; - state.push1(val); + let global_index = GlobalIndex::from_u32(*global_index); + let val = environ.translate_global_get(builder, global_index)?; + stack.push1(val); } Operator::GlobalSet { global_index } => { - match state.get_global(builder.func, *global_index, environ)? { - GlobalVariable::Memory { gv, offset, ty } => { - let addr = builder.ins().global_value(environ.pointer_type(), gv); - let mut flags = ir::MemFlags::trusted(); - // Like `global.get`, store globals in little-endian format. - if ty.is_vector() { - flags.set_endianness(ir::Endianness::Little); - } - // Put globals in the "table" abstract heap category as well. - flags.set_alias_region(Some(ir::AliasRegion::Table)); - let mut val = state.pop1(); - // Ensure SIMD values are cast to their default Cranelift type, I8x16. - if ty.is_vector() { - val = optionally_bitcast_vector(val, I8X16, builder); - } - debug_assert_eq!(ty, builder.func.dfg.value_type(val)); - builder.ins().store(flags, val, addr, offset); - environ.update_global(builder, *global_index, val); - } - GlobalVariable::Custom => { - let val = state.pop1(); - environ.translate_custom_global_set( - builder, - GlobalIndex::from_u32(*global_index), - val, - )?; - } + let global_index = GlobalIndex::from_u32(*global_index); + let mut val = stack.pop1(); + // Ensure SIMD values are cast to their default Cranelift type, I8x16. + if builder.func.dfg.value_type(val).is_vector() { + val = optionally_bitcast_vector(val, I8X16, builder); } + environ.translate_global_set(builder, global_index, val)?; } /********************************* Stack misc *************************************** * `drop`, `nop`, `unreachable` and `select`. ***********************************************************************************/ Operator::Drop => { - state.pop1(); + stack.pop1(); } Operator::Select => { - let (mut arg1, mut arg2, cond) = state.pop3(); + let (mut arg1, mut arg2, cond) = stack.pop3(); if builder.func.dfg.value_type(arg1).is_vector() { arg1 = optionally_bitcast_vector(arg1, I8X16, builder); } if builder.func.dfg.value_type(arg2).is_vector() { arg2 = optionally_bitcast_vector(arg2, I8X16, builder); } - state.push1(builder.ins().select(cond, arg1, arg2)); + stack.push1(builder.ins().select(cond, arg1, arg2)); } Operator::TypedSelect { ty: _ } => { // We ignore the explicit type parameter as it is only needed for // validation, which we require to have been performed before // translation. - let (mut arg1, mut arg2, cond) = state.pop3(); + let (mut arg1, mut arg2, cond) = stack.pop3(); if builder.func.dfg.value_type(arg1).is_vector() { arg1 = optionally_bitcast_vector(arg1, I8X16, builder); } if builder.func.dfg.value_type(arg2).is_vector() { arg2 = optionally_bitcast_vector(arg2, I8X16, builder); } - state.push1(builder.ins().select(cond, arg1, arg2)); + stack.push1(builder.ins().select(cond, arg1, arg2)); } Operator::Nop => { // We do nothing } Operator::Unreachable => { environ.trap(builder, crate::TRAP_UNREACHABLE); - state.reachable = false; + stack.reachable = false; } /***************************** Control flow blocks ********************************** * When starting a control flow block, we create a new `Block` that will hold the code @@ -279,19 +243,19 @@ pub fn translate_operator( Operator::Block { blockty } => { let (params, results) = blocktype_params_results(validator, *blockty)?; let next = block_with_params(builder, results.clone(), environ)?; - state.push_block(next, params.len(), results.len()); + stack.push_block(next, params.len(), results.len()); } Operator::Loop { blockty } => { let (params, results) = blocktype_params_results(validator, *blockty)?; let loop_body = block_with_params(builder, params.clone(), environ)?; let next = block_with_params(builder, results.clone(), environ)?; - canonicalise_then_jump(builder, loop_body, state.peekn(params.len())); - state.push_loop(loop_body, next, params.len(), results.len()); + canonicalise_then_jump(builder, loop_body, stack.peekn(params.len())); + stack.push_loop(loop_body, next, params.len(), results.len()); // Pop the initial `Block` actuals and replace them with the `Block`'s // params since control flow joins at the top of the loop. - state.popn(params.len()); - state + stack.popn(params.len()); + stack .stack .extend_from_slice(builder.block_params(loop_body)); @@ -299,7 +263,7 @@ pub fn translate_operator( environ.translate_loop_header(builder)?; } Operator::If { blockty } => { - let val = state.pop1(); + let val = stack.pop1(); let next_block = builder.create_block(); let (params, results) = blocktype_params_results(validator, *blockty)?; @@ -317,7 +281,7 @@ pub fn translate_operator( next_block, &[], destination, - state.peekn(params.len()), + stack.peekn(params.len()), ); ( destination, @@ -337,7 +301,7 @@ pub fn translate_operator( next_block, &[], else_block, - state.peekn(params.len()), + stack.peekn(params.len()), ); builder.seal_block(else_block); (destination, ElseData::WithElse { else_block }) @@ -352,7 +316,7 @@ pub fn translate_operator( // and we add nothing; // - either the If have an Else clause, in that case the destination of this jump // instruction will be changed later when we translate the Else operator. - state.push_if( + stack.push_if( destination, else_data, params.len(), @@ -361,8 +325,8 @@ pub fn translate_operator( ); } Operator::Else => { - let i = state.control_stack.len() - 1; - match state.control_stack[i] { + let i = stack.control_stack.len() - 1; + match stack.control_stack[i] { ControlStackFrame::If { ref else_data, head_is_reachable, @@ -375,11 +339,11 @@ pub fn translate_operator( // We finished the consequent, so record its final // reachability state. debug_assert!(consequent_ends_reachable.is_none()); - *consequent_ends_reachable = Some(state.reachable); + *consequent_ends_reachable = Some(stack.reachable); if head_is_reachable { // We have a branch from the head of the `if` to the `else`. - state.reachable = true; + stack.reachable = true; // Ensure we have a block for the `else` block (it may have // already been pre-allocated, see `ElseData` for details). @@ -396,9 +360,9 @@ pub fn translate_operator( canonicalise_then_jump( builder, destination, - state.peekn(params.len()), + stack.peekn(params.len()), ); - state.popn(params.len()); + stack.popn(params.len()); builder.change_jump_destination( branch_inst, @@ -412,9 +376,9 @@ pub fn translate_operator( canonicalise_then_jump( builder, destination, - state.peekn(num_return_values), + stack.peekn(num_return_values), ); - state.popn(num_return_values); + stack.popn(num_return_values); else_block } }; @@ -439,10 +403,10 @@ pub fn translate_operator( } } Operator::End => { - let frame = state.control_stack.pop().unwrap(); + let frame = stack.control_stack.pop().unwrap(); let next_block = frame.following_code(); let return_count = frame.num_return_values(); - let return_args = state.peekn_mut(return_count); + let return_args = stack.peekn_mut(return_count); canonicalise_then_jump(builder, next_block, return_args); // You might expect that if we just finished an `if` block that @@ -460,8 +424,8 @@ pub fn translate_operator( builder.seal_block(header) } - frame.truncate_value_stack_to_original_size(&mut state.stack); - state + frame.truncate_value_stack_to_original_size(&mut stack.stack); + stack .stack .extend_from_slice(builder.block_params(next_block)); } @@ -487,9 +451,9 @@ pub fn translate_operator( * `br_table`. ***********************************************************************************/ Operator::Br { relative_depth } => { - let i = state.control_stack.len() - 1 - (*relative_depth as usize); + let i = stack.control_stack.len() - 1 - (*relative_depth as usize); let (return_count, br_destination) = { - let frame = &mut state.control_stack[i]; + let frame = &mut stack.control_stack[i]; // We signal that all the code that follows until the next End is unreachable frame.set_branched_to_exit(); let return_count = if frame.is_loop() { @@ -499,12 +463,12 @@ pub fn translate_operator( }; (return_count, frame.br_destination()) }; - let destination_args = state.peekn_mut(return_count); + let destination_args = stack.peekn_mut(return_count); canonicalise_then_jump(builder, br_destination, destination_args); - state.popn(return_count); - state.reachable = false; + stack.popn(return_count); + stack.reachable = false; } - Operator::BrIf { relative_depth } => translate_br_if(*relative_depth, builder, state), + Operator::BrIf { relative_depth } => translate_br_if(*relative_depth, builder, stack), Operator::BrTable { targets } => { let default = targets.default(); let mut min_depth = default; @@ -515,31 +479,31 @@ pub fn translate_operator( } } let jump_args_count = { - let i = state.control_stack.len() - 1 - (min_depth as usize); - let min_depth_frame = &state.control_stack[i]; + let i = stack.control_stack.len() - 1 - (min_depth as usize); + let min_depth_frame = &stack.control_stack[i]; if min_depth_frame.is_loop() { min_depth_frame.num_param_values() } else { min_depth_frame.num_return_values() } }; - let val = state.pop1(); + let val = stack.pop1(); let mut data = Vec::with_capacity(targets.len() as usize); if jump_args_count == 0 { // No jump arguments for depth in targets.targets() { let depth = depth?; let block = { - let i = state.control_stack.len() - 1 - (depth as usize); - let frame = &mut state.control_stack[i]; + let i = stack.control_stack.len() - 1 - (depth as usize); + let frame = &mut stack.control_stack[i]; frame.set_branched_to_exit(); frame.br_destination() }; data.push(builder.func.dfg.block_call(block, &[])); } let block = { - let i = state.control_stack.len() - 1 - (default as usize); - let frame = &mut state.control_stack[i]; + let i = stack.control_stack.len() - 1 - (default as usize); + let frame = &mut stack.control_stack[i]; frame.set_branched_to_exit(); frame.br_destination() }; @@ -579,31 +543,31 @@ pub fn translate_operator( builder.switch_to_block(dest_block); builder.seal_block(dest_block); let real_dest_block = { - let i = state.control_stack.len() - 1 - depth; - let frame = &mut state.control_stack[i]; + let i = stack.control_stack.len() - 1 - depth; + let frame = &mut stack.control_stack[i]; frame.set_branched_to_exit(); frame.br_destination() }; - let destination_args = state.peekn_mut(return_count); + let destination_args = stack.peekn_mut(return_count); canonicalise_then_jump(builder, real_dest_block, destination_args); } - state.popn(return_count); + stack.popn(return_count); } - state.reachable = false; + stack.reachable = false; } Operator::Return => { let return_count = { - let frame = &mut state.control_stack[0]; + let frame = &mut stack.control_stack[0]; frame.num_return_values() }; { - let return_args = state.peekn_mut(return_count); + let return_args = stack.peekn_mut(return_count); environ.handle_before_return(&return_args, builder); bitcast_wasm_returns(return_args, builder); builder.ins().return_(return_args); } - state.popn(return_count); - state.reachable = false; + stack.popn(return_count); + stack.reachable = false; } /********************************** Exception handing **********************************/ Operator::Try { .. } @@ -623,10 +587,12 @@ pub fn translate_operator( * argument referring to an index in the external functions table of the module. ************************************************************************************/ Operator::Call { function_index } => { - let (fref, num_args) = state.get_direct_func(builder.func, *function_index, environ)?; + let function_index = FuncIndex::from_u32(*function_index); + let fref = environ.get_or_create_func_ref(builder.func, function_index); + let num_args = environ.num_params_for_func(function_index); // Bitcast any vector arguments to their default type, I8X16, before calling. - let args = state.peekn_mut(num_args); + let args = stack.peekn_mut(num_args); bitcast_wasm_params( environ, builder.func.dfg.ext_funcs[fref].signature, @@ -634,12 +600,7 @@ pub fn translate_operator( builder, ); - let call = environ.translate_call( - builder, - FuncIndex::from_u32(*function_index), - fref, - args, - )?; + let call = environ.translate_call(builder, function_index, fref, args)?; let inst_results = builder.inst_results(call); debug_assert_eq!( inst_results.len(), @@ -648,8 +609,8 @@ pub fn translate_operator( .len(), "translate_call results should match the call signature" ); - state.popn(num_args); - state.pushn(inst_results); + stack.popn(num_args); + stack.pushn(inst_results); } Operator::CallIndirect { type_index, @@ -658,26 +619,28 @@ pub fn translate_operator( // `type_index` is the index of the function's signature and // `table_index` is the index of the table to search the function // in. - let (sigref, num_args) = state.get_indirect_sig(builder.func, *type_index, environ)?; - let callee = state.pop1(); + let type_index = TypeIndex::from_u32(*type_index); + let sigref = environ.get_or_create_sig_ref(builder.func, type_index); + let num_args = environ.num_params_for_function_type(type_index); + let callee = stack.pop1(); // Bitcast any vector arguments to their default type, I8X16, before calling. - let args = state.peekn_mut(num_args); + let args = stack.peekn_mut(num_args); bitcast_wasm_params(environ, sigref, args, builder); let call = environ.translate_call_indirect( builder, validator.features(), TableIndex::from_u32(*table_index), - TypeIndex::from_u32(*type_index), + type_index, sigref, callee, - state.peekn(num_args), + stack.peekn(num_args), )?; let call = match call { Some(call) => call, None => { - state.reachable = false; + stack.reachable = false; return Ok(()); } }; @@ -687,8 +650,8 @@ pub fn translate_operator( builder.func.dfg.signatures[sigref].returns.len(), "translate_call_indirect results should match the call signature" ); - state.popn(num_args); - state.pushn(inst_results); + stack.popn(num_args); + stack.pushn(inst_results); } /******************************* Tail Calls ****************************************** * The tail call instructions pop their arguments from the stack and @@ -698,10 +661,12 @@ pub fn translate_operator( * VM's runtime state via tables. ************************************************************************************/ Operator::ReturnCall { function_index } => { - let (fref, num_args) = state.get_direct_func(builder.func, *function_index, environ)?; + let function_index = FuncIndex::from_u32(*function_index); + let fref = environ.get_or_create_func_ref(builder.func, function_index); + let num_args = environ.num_params_for_func(function_index); // Bitcast any vector arguments to their default type, I8X16, before calling. - let args = state.peekn_mut(num_args); + let args = stack.peekn_mut(num_args); bitcast_wasm_params( environ, builder.func.dfg.ext_funcs[fref].signature, @@ -709,15 +674,10 @@ pub fn translate_operator( builder, ); - environ.translate_return_call( - builder, - FuncIndex::from_u32(*function_index), - fref, - args, - )?; + environ.translate_return_call(builder, function_index, fref, args)?; - state.popn(num_args); - state.reachable = false; + stack.popn(num_args); + stack.reachable = false; } Operator::ReturnCallIndirect { type_index, @@ -726,41 +686,45 @@ pub fn translate_operator( // `type_index` is the index of the function's signature and // `table_index` is the index of the table to search the function // in. - let (sigref, num_args) = state.get_indirect_sig(builder.func, *type_index, environ)?; - let callee = state.pop1(); + let type_index = TypeIndex::from_u32(*type_index); + let sigref = environ.get_or_create_sig_ref(builder.func, type_index); + let num_args = environ.num_params_for_function_type(type_index); + let callee = stack.pop1(); // Bitcast any vector arguments to their default type, I8X16, before calling. - let args = state.peekn_mut(num_args); + let args = stack.peekn_mut(num_args); bitcast_wasm_params(environ, sigref, args, builder); environ.translate_return_call_indirect( builder, validator.features(), TableIndex::from_u32(*table_index), - TypeIndex::from_u32(*type_index), + type_index, sigref, callee, - state.peekn(num_args), + stack.peekn(num_args), )?; - state.popn(num_args); - state.reachable = false; + stack.popn(num_args); + stack.reachable = false; } Operator::ReturnCallRef { type_index } => { // Get function signature // `index` is the index of the function's signature and `table_index` is the index of // the table to search the function in. - let (sigref, num_args) = state.get_indirect_sig(builder.func, *type_index, environ)?; - let callee = state.pop1(); + let type_index = TypeIndex::from_u32(*type_index); + let sigref = environ.get_or_create_sig_ref(builder.func, type_index); + let num_args = environ.num_params_for_function_type(type_index); + let callee = stack.pop1(); // Bitcast any vector arguments to their default type, I8X16, before calling. - let args = state.peekn_mut(num_args); + let args = stack.peekn_mut(num_args); bitcast_wasm_params(environ, sigref, args, builder); - environ.translate_return_call_ref(builder, sigref, callee, state.peekn(num_args))?; + environ.translate_return_call_ref(builder, sigref, callee, stack.peekn(num_args))?; - state.popn(num_args); - state.reachable = false; + stack.popn(num_args); + stack.reachable = false; } /******************************* Memory management *********************************** * Memory management is handled by environment. It is usually translated into calls to @@ -769,16 +733,16 @@ pub fn translate_operator( Operator::MemoryGrow { mem } => { // The WebAssembly MVP only supports one linear memory, but we expect the reserved // argument to be a memory index. - let heap_index = MemoryIndex::from_u32(*mem); - let heap = state.get_heap(builder.func, *mem, environ)?; - let val = state.pop1(); - environ.before_memory_grow(builder, val, heap_index); - state.push1(environ.translate_memory_grow(builder, heap_index, heap, val)?) + let mem = MemoryIndex::from_u32(*mem); + let _heap = environ.get_or_create_heap(builder.func, mem); + let val = stack.pop1(); + environ.before_memory_grow(builder, val, mem); + stack.push1(environ.translate_memory_grow(builder, mem, val)?) } Operator::MemorySize { mem } => { - let heap_index = MemoryIndex::from_u32(*mem); - let heap = state.get_heap(builder.func, *mem, environ)?; - state.push1(environ.translate_memory_size(builder.cursor(), heap_index, heap)?); + let mem = MemoryIndex::from_u32(*mem); + let _heap = environ.get_or_create_heap(builder.func, mem); + stack.push1(environ.translate_memory_size(builder.cursor(), mem)?); } /******************************* Load instructions *********************************** * Wasm specifies an integer alignment flag but we drop it in Cranelift. @@ -786,142 +750,142 @@ pub fn translate_operator( ************************************************************************************/ Operator::I32Load8U { memarg } => { unwrap_or_return_unreachable_state!( - state, - translate_load(memarg, ir::Opcode::Uload8, I32, builder, state, environ)? + stack, + translate_load(memarg, ir::Opcode::Uload8, I32, builder, stack, environ)? ); } Operator::I32Load16U { memarg } => { unwrap_or_return_unreachable_state!( - state, - translate_load(memarg, ir::Opcode::Uload16, I32, builder, state, environ)? + stack, + translate_load(memarg, ir::Opcode::Uload16, I32, builder, stack, environ)? ); } Operator::I32Load8S { memarg } => { unwrap_or_return_unreachable_state!( - state, - translate_load(memarg, ir::Opcode::Sload8, I32, builder, state, environ)? + stack, + translate_load(memarg, ir::Opcode::Sload8, I32, builder, stack, environ)? ); } Operator::I32Load16S { memarg } => { unwrap_or_return_unreachable_state!( - state, - translate_load(memarg, ir::Opcode::Sload16, I32, builder, state, environ)? + stack, + translate_load(memarg, ir::Opcode::Sload16, I32, builder, stack, environ)? ); } Operator::I64Load8U { memarg } => { unwrap_or_return_unreachable_state!( - state, - translate_load(memarg, ir::Opcode::Uload8, I64, builder, state, environ)? + stack, + translate_load(memarg, ir::Opcode::Uload8, I64, builder, stack, environ)? ); } Operator::I64Load16U { memarg } => { unwrap_or_return_unreachable_state!( - state, - translate_load(memarg, ir::Opcode::Uload16, I64, builder, state, environ)? + stack, + translate_load(memarg, ir::Opcode::Uload16, I64, builder, stack, environ)? ); } Operator::I64Load8S { memarg } => { unwrap_or_return_unreachable_state!( - state, - translate_load(memarg, ir::Opcode::Sload8, I64, builder, state, environ)? + stack, + translate_load(memarg, ir::Opcode::Sload8, I64, builder, stack, environ)? ); } Operator::I64Load16S { memarg } => { unwrap_or_return_unreachable_state!( - state, - translate_load(memarg, ir::Opcode::Sload16, I64, builder, state, environ)? + stack, + translate_load(memarg, ir::Opcode::Sload16, I64, builder, stack, environ)? ); } Operator::I64Load32S { memarg } => { unwrap_or_return_unreachable_state!( - state, - translate_load(memarg, ir::Opcode::Sload32, I64, builder, state, environ)? + stack, + translate_load(memarg, ir::Opcode::Sload32, I64, builder, stack, environ)? ); } Operator::I64Load32U { memarg } => { unwrap_or_return_unreachable_state!( - state, - translate_load(memarg, ir::Opcode::Uload32, I64, builder, state, environ)? + stack, + translate_load(memarg, ir::Opcode::Uload32, I64, builder, stack, environ)? ); } Operator::I32Load { memarg } => { unwrap_or_return_unreachable_state!( - state, - translate_load(memarg, ir::Opcode::Load, I32, builder, state, environ)? + stack, + translate_load(memarg, ir::Opcode::Load, I32, builder, stack, environ)? ); } Operator::F32Load { memarg } => { unwrap_or_return_unreachable_state!( - state, - translate_load(memarg, ir::Opcode::Load, F32, builder, state, environ)? + stack, + translate_load(memarg, ir::Opcode::Load, F32, builder, stack, environ)? ); } Operator::I64Load { memarg } => { unwrap_or_return_unreachable_state!( - state, - translate_load(memarg, ir::Opcode::Load, I64, builder, state, environ)? + stack, + translate_load(memarg, ir::Opcode::Load, I64, builder, stack, environ)? ); } Operator::F64Load { memarg } => { unwrap_or_return_unreachable_state!( - state, - translate_load(memarg, ir::Opcode::Load, F64, builder, state, environ)? + stack, + translate_load(memarg, ir::Opcode::Load, F64, builder, stack, environ)? ); } Operator::V128Load { memarg } => { unwrap_or_return_unreachable_state!( - state, - translate_load(memarg, ir::Opcode::Load, I8X16, builder, state, environ)? + stack, + translate_load(memarg, ir::Opcode::Load, I8X16, builder, stack, environ)? ); } Operator::V128Load8x8S { memarg } => { //TODO(#6829): add before_load() and before_store() hooks for SIMD loads and stores. let (flags, _, base) = unwrap_or_return_unreachable_state!( - state, - prepare_addr(memarg, 8, builder, state, environ)? + stack, + prepare_addr(memarg, 8, builder, stack, environ)? ); let loaded = builder.ins().sload8x8(flags, base, 0); - state.push1(loaded); + stack.push1(loaded); } Operator::V128Load8x8U { memarg } => { let (flags, _, base) = unwrap_or_return_unreachable_state!( - state, - prepare_addr(memarg, 8, builder, state, environ)? + stack, + prepare_addr(memarg, 8, builder, stack, environ)? ); let loaded = builder.ins().uload8x8(flags, base, 0); - state.push1(loaded); + stack.push1(loaded); } Operator::V128Load16x4S { memarg } => { let (flags, _, base) = unwrap_or_return_unreachable_state!( - state, - prepare_addr(memarg, 8, builder, state, environ)? + stack, + prepare_addr(memarg, 8, builder, stack, environ)? ); let loaded = builder.ins().sload16x4(flags, base, 0); - state.push1(loaded); + stack.push1(loaded); } Operator::V128Load16x4U { memarg } => { let (flags, _, base) = unwrap_or_return_unreachable_state!( - state, - prepare_addr(memarg, 8, builder, state, environ)? + stack, + prepare_addr(memarg, 8, builder, stack, environ)? ); let loaded = builder.ins().uload16x4(flags, base, 0); - state.push1(loaded); + stack.push1(loaded); } Operator::V128Load32x2S { memarg } => { let (flags, _, base) = unwrap_or_return_unreachable_state!( - state, - prepare_addr(memarg, 8, builder, state, environ)? + stack, + prepare_addr(memarg, 8, builder, stack, environ)? ); let loaded = builder.ins().sload32x2(flags, base, 0); - state.push1(loaded); + stack.push1(loaded); } Operator::V128Load32x2U { memarg } => { let (flags, _, base) = unwrap_or_return_unreachable_state!( - state, - prepare_addr(memarg, 8, builder, state, environ)? + stack, + prepare_addr(memarg, 8, builder, stack, environ)? ); let loaded = builder.ins().uload32x2(flags, base, 0); - state.push1(loaded); + stack.push1(loaded); } /****************************** Store instructions *********************************** * Wasm specifies an integer alignment flag but we drop it in Cranelift. @@ -931,347 +895,347 @@ pub fn translate_operator( | Operator::I64Store { memarg } | Operator::F32Store { memarg } | Operator::F64Store { memarg } => { - translate_store(memarg, ir::Opcode::Store, builder, state, environ)?; + translate_store(memarg, ir::Opcode::Store, builder, stack, environ)?; } Operator::I32Store8 { memarg } | Operator::I64Store8 { memarg } => { - translate_store(memarg, ir::Opcode::Istore8, builder, state, environ)?; + translate_store(memarg, ir::Opcode::Istore8, builder, stack, environ)?; } Operator::I32Store16 { memarg } | Operator::I64Store16 { memarg } => { - translate_store(memarg, ir::Opcode::Istore16, builder, state, environ)?; + translate_store(memarg, ir::Opcode::Istore16, builder, stack, environ)?; } Operator::I64Store32 { memarg } => { - translate_store(memarg, ir::Opcode::Istore32, builder, state, environ)?; + translate_store(memarg, ir::Opcode::Istore32, builder, stack, environ)?; } Operator::V128Store { memarg } => { - translate_store(memarg, ir::Opcode::Store, builder, state, environ)?; + translate_store(memarg, ir::Opcode::Store, builder, stack, environ)?; } /****************************** Nullary Operators ************************************/ Operator::I32Const { value } => { - state.push1(builder.ins().iconst(I32, i64::from(value.unsigned()))); + stack.push1(builder.ins().iconst(I32, i64::from(value.unsigned()))); } - Operator::I64Const { value } => state.push1(builder.ins().iconst(I64, *value)), + Operator::I64Const { value } => stack.push1(builder.ins().iconst(I64, *value)), Operator::F32Const { value } => { - state.push1(builder.ins().f32const(f32_translation(*value))); + stack.push1(builder.ins().f32const(f32_translation(*value))); } Operator::F64Const { value } => { - state.push1(builder.ins().f64const(f64_translation(*value))); + stack.push1(builder.ins().f64const(f64_translation(*value))); } /******************************* Unary Operators *************************************/ Operator::I32Clz | Operator::I64Clz => { - let arg = state.pop1(); - state.push1(builder.ins().clz(arg)); + let arg = stack.pop1(); + stack.push1(builder.ins().clz(arg)); } Operator::I32Ctz | Operator::I64Ctz => { - let arg = state.pop1(); - state.push1(builder.ins().ctz(arg)); + let arg = stack.pop1(); + stack.push1(builder.ins().ctz(arg)); } Operator::I32Popcnt | Operator::I64Popcnt => { - let arg = state.pop1(); - state.push1(builder.ins().popcnt(arg)); + let arg = stack.pop1(); + stack.push1(builder.ins().popcnt(arg)); } Operator::I64ExtendI32S => { - let val = state.pop1(); - state.push1(builder.ins().sextend(I64, val)); + let val = stack.pop1(); + stack.push1(builder.ins().sextend(I64, val)); } Operator::I64ExtendI32U => { - let val = state.pop1(); - state.push1(builder.ins().uextend(I64, val)); + let val = stack.pop1(); + stack.push1(builder.ins().uextend(I64, val)); } Operator::I32WrapI64 => { - let val = state.pop1(); - state.push1(builder.ins().ireduce(I32, val)); + let val = stack.pop1(); + stack.push1(builder.ins().ireduce(I32, val)); } Operator::F32Sqrt | Operator::F64Sqrt => { - let arg = state.pop1(); - state.push1(builder.ins().sqrt(arg)); + let arg = stack.pop1(); + stack.push1(builder.ins().sqrt(arg)); } Operator::F32Ceil => { - let arg = state.pop1(); - state.push1(environ.ceil_f32(builder, arg)); + let arg = stack.pop1(); + stack.push1(environ.ceil_f32(builder, arg)); } Operator::F64Ceil => { - let arg = state.pop1(); - state.push1(environ.ceil_f64(builder, arg)); + let arg = stack.pop1(); + stack.push1(environ.ceil_f64(builder, arg)); } Operator::F32Floor => { - let arg = state.pop1(); - state.push1(environ.floor_f32(builder, arg)); + let arg = stack.pop1(); + stack.push1(environ.floor_f32(builder, arg)); } Operator::F64Floor => { - let arg = state.pop1(); - state.push1(environ.floor_f64(builder, arg)); + let arg = stack.pop1(); + stack.push1(environ.floor_f64(builder, arg)); } Operator::F32Trunc => { - let arg = state.pop1(); - state.push1(environ.trunc_f32(builder, arg)); + let arg = stack.pop1(); + stack.push1(environ.trunc_f32(builder, arg)); } Operator::F64Trunc => { - let arg = state.pop1(); - state.push1(environ.trunc_f64(builder, arg)); + let arg = stack.pop1(); + stack.push1(environ.trunc_f64(builder, arg)); } Operator::F32Nearest => { - let arg = state.pop1(); - state.push1(environ.nearest_f32(builder, arg)); + let arg = stack.pop1(); + stack.push1(environ.nearest_f32(builder, arg)); } Operator::F64Nearest => { - let arg = state.pop1(); - state.push1(environ.nearest_f64(builder, arg)); + let arg = stack.pop1(); + stack.push1(environ.nearest_f64(builder, arg)); } Operator::F32Abs | Operator::F64Abs => { - let val = state.pop1(); - state.push1(builder.ins().fabs(val)); + let val = stack.pop1(); + stack.push1(builder.ins().fabs(val)); } Operator::F32Neg | Operator::F64Neg => { - let arg = state.pop1(); - state.push1(builder.ins().fneg(arg)); + let arg = stack.pop1(); + stack.push1(builder.ins().fneg(arg)); } Operator::F64ConvertI64U | Operator::F64ConvertI32U => { - let val = state.pop1(); - state.push1(builder.ins().fcvt_from_uint(F64, val)); + let val = stack.pop1(); + stack.push1(builder.ins().fcvt_from_uint(F64, val)); } Operator::F64ConvertI64S | Operator::F64ConvertI32S => { - let val = state.pop1(); - state.push1(builder.ins().fcvt_from_sint(F64, val)); + let val = stack.pop1(); + stack.push1(builder.ins().fcvt_from_sint(F64, val)); } Operator::F32ConvertI64S | Operator::F32ConvertI32S => { - let val = state.pop1(); - state.push1(builder.ins().fcvt_from_sint(F32, val)); + let val = stack.pop1(); + stack.push1(builder.ins().fcvt_from_sint(F32, val)); } Operator::F32ConvertI64U | Operator::F32ConvertI32U => { - let val = state.pop1(); - state.push1(builder.ins().fcvt_from_uint(F32, val)); + let val = stack.pop1(); + stack.push1(builder.ins().fcvt_from_uint(F32, val)); } Operator::F64PromoteF32 => { - let val = state.pop1(); - state.push1(builder.ins().fpromote(F64, val)); + let val = stack.pop1(); + stack.push1(builder.ins().fpromote(F64, val)); } Operator::F32DemoteF64 => { - let val = state.pop1(); - state.push1(builder.ins().fdemote(F32, val)); + let val = stack.pop1(); + stack.push1(builder.ins().fdemote(F32, val)); } Operator::I64TruncF64S | Operator::I64TruncF32S => { - let val = state.pop1(); - state.push1(environ.translate_fcvt_to_sint(builder, I64, val)); + let val = stack.pop1(); + stack.push1(environ.translate_fcvt_to_sint(builder, I64, val)); } Operator::I32TruncF64S | Operator::I32TruncF32S => { - let val = state.pop1(); - state.push1(environ.translate_fcvt_to_sint(builder, I32, val)); + let val = stack.pop1(); + stack.push1(environ.translate_fcvt_to_sint(builder, I32, val)); } Operator::I64TruncF64U | Operator::I64TruncF32U => { - let val = state.pop1(); - state.push1(environ.translate_fcvt_to_uint(builder, I64, val)); + let val = stack.pop1(); + stack.push1(environ.translate_fcvt_to_uint(builder, I64, val)); } Operator::I32TruncF64U | Operator::I32TruncF32U => { - let val = state.pop1(); - state.push1(environ.translate_fcvt_to_uint(builder, I32, val)); + let val = stack.pop1(); + stack.push1(environ.translate_fcvt_to_uint(builder, I32, val)); } Operator::I64TruncSatF64S | Operator::I64TruncSatF32S => { - let val = state.pop1(); - state.push1(builder.ins().fcvt_to_sint_sat(I64, val)); + let val = stack.pop1(); + stack.push1(builder.ins().fcvt_to_sint_sat(I64, val)); } Operator::I32TruncSatF64S | Operator::I32TruncSatF32S => { - let val = state.pop1(); - state.push1(builder.ins().fcvt_to_sint_sat(I32, val)); + let val = stack.pop1(); + stack.push1(builder.ins().fcvt_to_sint_sat(I32, val)); } Operator::I64TruncSatF64U | Operator::I64TruncSatF32U => { - let val = state.pop1(); - state.push1(builder.ins().fcvt_to_uint_sat(I64, val)); + let val = stack.pop1(); + stack.push1(builder.ins().fcvt_to_uint_sat(I64, val)); } Operator::I32TruncSatF64U | Operator::I32TruncSatF32U => { - let val = state.pop1(); - state.push1(builder.ins().fcvt_to_uint_sat(I32, val)); + let val = stack.pop1(); + stack.push1(builder.ins().fcvt_to_uint_sat(I32, val)); } Operator::F32ReinterpretI32 => { - let val = state.pop1(); - state.push1(builder.ins().bitcast(F32, MemFlags::new(), val)); + let val = stack.pop1(); + stack.push1(builder.ins().bitcast(F32, MemFlags::new(), val)); } Operator::F64ReinterpretI64 => { - let val = state.pop1(); - state.push1(builder.ins().bitcast(F64, MemFlags::new(), val)); + let val = stack.pop1(); + stack.push1(builder.ins().bitcast(F64, MemFlags::new(), val)); } Operator::I32ReinterpretF32 => { - let val = state.pop1(); - state.push1(builder.ins().bitcast(I32, MemFlags::new(), val)); + let val = stack.pop1(); + stack.push1(builder.ins().bitcast(I32, MemFlags::new(), val)); } Operator::I64ReinterpretF64 => { - let val = state.pop1(); - state.push1(builder.ins().bitcast(I64, MemFlags::new(), val)); + let val = stack.pop1(); + stack.push1(builder.ins().bitcast(I64, MemFlags::new(), val)); } Operator::I32Extend8S => { - let val = state.pop1(); - state.push1(builder.ins().ireduce(I8, val)); - let val = state.pop1(); - state.push1(builder.ins().sextend(I32, val)); + let val = stack.pop1(); + stack.push1(builder.ins().ireduce(I8, val)); + let val = stack.pop1(); + stack.push1(builder.ins().sextend(I32, val)); } Operator::I32Extend16S => { - let val = state.pop1(); - state.push1(builder.ins().ireduce(I16, val)); - let val = state.pop1(); - state.push1(builder.ins().sextend(I32, val)); + let val = stack.pop1(); + stack.push1(builder.ins().ireduce(I16, val)); + let val = stack.pop1(); + stack.push1(builder.ins().sextend(I32, val)); } Operator::I64Extend8S => { - let val = state.pop1(); - state.push1(builder.ins().ireduce(I8, val)); - let val = state.pop1(); - state.push1(builder.ins().sextend(I64, val)); + let val = stack.pop1(); + stack.push1(builder.ins().ireduce(I8, val)); + let val = stack.pop1(); + stack.push1(builder.ins().sextend(I64, val)); } Operator::I64Extend16S => { - let val = state.pop1(); - state.push1(builder.ins().ireduce(I16, val)); - let val = state.pop1(); - state.push1(builder.ins().sextend(I64, val)); + let val = stack.pop1(); + stack.push1(builder.ins().ireduce(I16, val)); + let val = stack.pop1(); + stack.push1(builder.ins().sextend(I64, val)); } Operator::I64Extend32S => { - let val = state.pop1(); - state.push1(builder.ins().ireduce(I32, val)); - let val = state.pop1(); - state.push1(builder.ins().sextend(I64, val)); + let val = stack.pop1(); + stack.push1(builder.ins().ireduce(I32, val)); + let val = stack.pop1(); + stack.push1(builder.ins().sextend(I64, val)); } /****************************** Binary Operators ************************************/ Operator::I32Add | Operator::I64Add => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().iadd(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().iadd(arg1, arg2)); } Operator::I32And | Operator::I64And => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().band(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().band(arg1, arg2)); } Operator::I32Or | Operator::I64Or => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().bor(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().bor(arg1, arg2)); } Operator::I32Xor | Operator::I64Xor => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().bxor(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().bxor(arg1, arg2)); } Operator::I32Shl | Operator::I64Shl => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().ishl(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().ishl(arg1, arg2)); } Operator::I32ShrS | Operator::I64ShrS => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().sshr(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().sshr(arg1, arg2)); } Operator::I32ShrU | Operator::I64ShrU => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().ushr(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().ushr(arg1, arg2)); } Operator::I32Rotl | Operator::I64Rotl => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().rotl(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().rotl(arg1, arg2)); } Operator::I32Rotr | Operator::I64Rotr => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().rotr(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().rotr(arg1, arg2)); } Operator::F32Add | Operator::F64Add => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().fadd(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().fadd(arg1, arg2)); } Operator::I32Sub | Operator::I64Sub => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().isub(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().isub(arg1, arg2)); } Operator::F32Sub | Operator::F64Sub => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().fsub(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().fsub(arg1, arg2)); } Operator::I32Mul | Operator::I64Mul => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().imul(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().imul(arg1, arg2)); } Operator::F32Mul | Operator::F64Mul => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().fmul(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().fmul(arg1, arg2)); } Operator::F32Div | Operator::F64Div => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().fdiv(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().fdiv(arg1, arg2)); } Operator::I32DivS | Operator::I64DivS => { - let (arg1, arg2) = state.pop2(); - state.push1(environ.translate_sdiv(builder, arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(environ.translate_sdiv(builder, arg1, arg2)); } Operator::I32DivU | Operator::I64DivU => { - let (arg1, arg2) = state.pop2(); - state.push1(environ.translate_udiv(builder, arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(environ.translate_udiv(builder, arg1, arg2)); } Operator::I32RemS | Operator::I64RemS => { - let (arg1, arg2) = state.pop2(); - state.push1(environ.translate_srem(builder, arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(environ.translate_srem(builder, arg1, arg2)); } Operator::I32RemU | Operator::I64RemU => { - let (arg1, arg2) = state.pop2(); - state.push1(environ.translate_urem(builder, arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(environ.translate_urem(builder, arg1, arg2)); } Operator::F32Min | Operator::F64Min => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().fmin(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().fmin(arg1, arg2)); } Operator::F32Max | Operator::F64Max => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().fmax(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().fmax(arg1, arg2)); } Operator::F32Copysign | Operator::F64Copysign => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().fcopysign(arg1, arg2)); + let (arg1, arg2) = stack.pop2(); + stack.push1(builder.ins().fcopysign(arg1, arg2)); } /**************************** Comparison Operators **********************************/ Operator::I32LtS | Operator::I64LtS => { - translate_icmp(IntCC::SignedLessThan, builder, state) + translate_icmp(IntCC::SignedLessThan, builder, stack) } Operator::I32LtU | Operator::I64LtU => { - translate_icmp(IntCC::UnsignedLessThan, builder, state) + translate_icmp(IntCC::UnsignedLessThan, builder, stack) } Operator::I32LeS | Operator::I64LeS => { - translate_icmp(IntCC::SignedLessThanOrEqual, builder, state) + translate_icmp(IntCC::SignedLessThanOrEqual, builder, stack) } Operator::I32LeU | Operator::I64LeU => { - translate_icmp(IntCC::UnsignedLessThanOrEqual, builder, state) + translate_icmp(IntCC::UnsignedLessThanOrEqual, builder, stack) } Operator::I32GtS | Operator::I64GtS => { - translate_icmp(IntCC::SignedGreaterThan, builder, state) + translate_icmp(IntCC::SignedGreaterThan, builder, stack) } Operator::I32GtU | Operator::I64GtU => { - translate_icmp(IntCC::UnsignedGreaterThan, builder, state) + translate_icmp(IntCC::UnsignedGreaterThan, builder, stack) } Operator::I32GeS | Operator::I64GeS => { - translate_icmp(IntCC::SignedGreaterThanOrEqual, builder, state) + translate_icmp(IntCC::SignedGreaterThanOrEqual, builder, stack) } Operator::I32GeU | Operator::I64GeU => { - translate_icmp(IntCC::UnsignedGreaterThanOrEqual, builder, state) + translate_icmp(IntCC::UnsignedGreaterThanOrEqual, builder, stack) } Operator::I32Eqz | Operator::I64Eqz => { - let arg = state.pop1(); + let arg = stack.pop1(); let val = builder.ins().icmp_imm(IntCC::Equal, arg, 0); - state.push1(builder.ins().uextend(I32, val)); + stack.push1(builder.ins().uextend(I32, val)); } - Operator::I32Eq | Operator::I64Eq => translate_icmp(IntCC::Equal, builder, state), - Operator::F32Eq | Operator::F64Eq => translate_fcmp(FloatCC::Equal, builder, state), - Operator::I32Ne | Operator::I64Ne => translate_icmp(IntCC::NotEqual, builder, state), - Operator::F32Ne | Operator::F64Ne => translate_fcmp(FloatCC::NotEqual, builder, state), - Operator::F32Gt | Operator::F64Gt => translate_fcmp(FloatCC::GreaterThan, builder, state), + Operator::I32Eq | Operator::I64Eq => translate_icmp(IntCC::Equal, builder, stack), + Operator::F32Eq | Operator::F64Eq => translate_fcmp(FloatCC::Equal, builder, stack), + Operator::I32Ne | Operator::I64Ne => translate_icmp(IntCC::NotEqual, builder, stack), + Operator::F32Ne | Operator::F64Ne => translate_fcmp(FloatCC::NotEqual, builder, stack), + Operator::F32Gt | Operator::F64Gt => translate_fcmp(FloatCC::GreaterThan, builder, stack), Operator::F32Ge | Operator::F64Ge => { - translate_fcmp(FloatCC::GreaterThanOrEqual, builder, state) + translate_fcmp(FloatCC::GreaterThanOrEqual, builder, stack) } - Operator::F32Lt | Operator::F64Lt => translate_fcmp(FloatCC::LessThan, builder, state), + Operator::F32Lt | Operator::F64Lt => translate_fcmp(FloatCC::LessThan, builder, stack), Operator::F32Le | Operator::F64Le => { - translate_fcmp(FloatCC::LessThanOrEqual, builder, state) + translate_fcmp(FloatCC::LessThanOrEqual, builder, stack) } Operator::RefNull { hty } => { let hty = environ.convert_heap_type(*hty)?; - state.push1(environ.translate_ref_null(builder.cursor(), hty)?) + stack.push1(environ.translate_ref_null(builder.cursor(), hty)?) } Operator::RefIsNull => { - let value = state.pop1(); + let value = stack.pop1(); let [WasmValType::Ref(ty)] = operand_types else { unreachable!("validation") }; - state.push1(environ.translate_ref_is_null(builder.cursor(), value, *ty)?); + stack.push1(environ.translate_ref_is_null(builder.cursor(), value, *ty)?); } Operator::RefFunc { function_index } => { let index = FuncIndex::from_u32(*function_index); - state.push1(environ.translate_ref_func(builder.cursor(), index)?); + stack.push1(environ.translate_ref_func(builder.cursor(), index)?); } Operator::MemoryAtomicWait32 { memarg } | Operator::MemoryAtomicWait64 { memarg } => { // The WebAssembly MVP only supports one linear memory and @@ -1282,12 +1246,12 @@ pub fn translate_operator( Operator::MemoryAtomicWait32 { .. } => I32, _ => unreachable!(), }; - let heap_index = MemoryIndex::from_u32(memarg.memory); - let heap = state.get_heap(builder.func, memarg.memory, environ)?; - let timeout = state.pop1(); // 64 (fixed) - let expected = state.pop1(); // 32 or 64 (per the `Ixx` in `IxxAtomicWait`) + let memory_index = MemoryIndex::from_u32(memarg.memory); + let heap = environ.get_or_create_heap(builder.func, memory_index); + let timeout = stack.pop1(); // 64 (fixed) + let expected = stack.pop1(); // 32 or 64 (per the `Ixx` in `IxxAtomicWait`) assert!(builder.func.dfg.value_type(expected) == implied_ty); - let addr = state.pop1(); + let addr = stack.pop1(); let effective_addr = if memarg.offset == 0 { addr } else { @@ -1299,19 +1263,19 @@ pub fn translate_operator( // code it needs to generate, if it wants. let res = environ.translate_atomic_wait( builder, - heap_index, + memory_index, heap, effective_addr, expected, timeout, )?; - state.push1(res); + stack.push1(res); } Operator::MemoryAtomicNotify { memarg } => { - let heap_index = MemoryIndex::from_u32(memarg.memory); - let heap = state.get_heap(builder.func, memarg.memory, environ)?; - let count = state.pop1(); // 32 (fixed) - let addr = state.pop1(); + let memory_index = MemoryIndex::from_u32(memarg.memory); + let heap = environ.get_or_create_heap(builder.func, memory_index); + let count = stack.pop1(); // 32 (fixed) + let addr = stack.pop1(); let effective_addr = if memarg.offset == 0 { addr } else { @@ -1321,209 +1285,209 @@ pub fn translate_operator( }; let res = environ.translate_atomic_notify( builder, - heap_index, + memory_index, heap, effective_addr, count, )?; - state.push1(res); + stack.push1(res); } Operator::I32AtomicLoad { memarg } => { - translate_atomic_load(I32, I32, memarg, builder, state, environ)? + translate_atomic_load(I32, I32, memarg, builder, stack, environ)? } Operator::I64AtomicLoad { memarg } => { - translate_atomic_load(I64, I64, memarg, builder, state, environ)? + translate_atomic_load(I64, I64, memarg, builder, stack, environ)? } Operator::I32AtomicLoad8U { memarg } => { - translate_atomic_load(I32, I8, memarg, builder, state, environ)? + translate_atomic_load(I32, I8, memarg, builder, stack, environ)? } Operator::I32AtomicLoad16U { memarg } => { - translate_atomic_load(I32, I16, memarg, builder, state, environ)? + translate_atomic_load(I32, I16, memarg, builder, stack, environ)? } Operator::I64AtomicLoad8U { memarg } => { - translate_atomic_load(I64, I8, memarg, builder, state, environ)? + translate_atomic_load(I64, I8, memarg, builder, stack, environ)? } Operator::I64AtomicLoad16U { memarg } => { - translate_atomic_load(I64, I16, memarg, builder, state, environ)? + translate_atomic_load(I64, I16, memarg, builder, stack, environ)? } Operator::I64AtomicLoad32U { memarg } => { - translate_atomic_load(I64, I32, memarg, builder, state, environ)? + translate_atomic_load(I64, I32, memarg, builder, stack, environ)? } Operator::I32AtomicStore { memarg } => { - translate_atomic_store(I32, memarg, builder, state, environ)? + translate_atomic_store(I32, memarg, builder, stack, environ)? } Operator::I64AtomicStore { memarg } => { - translate_atomic_store(I64, memarg, builder, state, environ)? + translate_atomic_store(I64, memarg, builder, stack, environ)? } Operator::I32AtomicStore8 { memarg } => { - translate_atomic_store(I8, memarg, builder, state, environ)? + translate_atomic_store(I8, memarg, builder, stack, environ)? } Operator::I32AtomicStore16 { memarg } => { - translate_atomic_store(I16, memarg, builder, state, environ)? + translate_atomic_store(I16, memarg, builder, stack, environ)? } Operator::I64AtomicStore8 { memarg } => { - translate_atomic_store(I8, memarg, builder, state, environ)? + translate_atomic_store(I8, memarg, builder, stack, environ)? } Operator::I64AtomicStore16 { memarg } => { - translate_atomic_store(I16, memarg, builder, state, environ)? + translate_atomic_store(I16, memarg, builder, stack, environ)? } Operator::I64AtomicStore32 { memarg } => { - translate_atomic_store(I32, memarg, builder, state, environ)? + translate_atomic_store(I32, memarg, builder, stack, environ)? } Operator::I32AtomicRmwAdd { memarg } => { - translate_atomic_rmw(I32, I32, AtomicRmwOp::Add, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I32, AtomicRmwOp::Add, memarg, builder, stack, environ)? } Operator::I64AtomicRmwAdd { memarg } => { - translate_atomic_rmw(I64, I64, AtomicRmwOp::Add, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I64, AtomicRmwOp::Add, memarg, builder, stack, environ)? } Operator::I32AtomicRmw8AddU { memarg } => { - translate_atomic_rmw(I32, I8, AtomicRmwOp::Add, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I8, AtomicRmwOp::Add, memarg, builder, stack, environ)? } Operator::I32AtomicRmw16AddU { memarg } => { - translate_atomic_rmw(I32, I16, AtomicRmwOp::Add, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I16, AtomicRmwOp::Add, memarg, builder, stack, environ)? } Operator::I64AtomicRmw8AddU { memarg } => { - translate_atomic_rmw(I64, I8, AtomicRmwOp::Add, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I8, AtomicRmwOp::Add, memarg, builder, stack, environ)? } Operator::I64AtomicRmw16AddU { memarg } => { - translate_atomic_rmw(I64, I16, AtomicRmwOp::Add, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I16, AtomicRmwOp::Add, memarg, builder, stack, environ)? } Operator::I64AtomicRmw32AddU { memarg } => { - translate_atomic_rmw(I64, I32, AtomicRmwOp::Add, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I32, AtomicRmwOp::Add, memarg, builder, stack, environ)? } Operator::I32AtomicRmwSub { memarg } => { - translate_atomic_rmw(I32, I32, AtomicRmwOp::Sub, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I32, AtomicRmwOp::Sub, memarg, builder, stack, environ)? } Operator::I64AtomicRmwSub { memarg } => { - translate_atomic_rmw(I64, I64, AtomicRmwOp::Sub, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I64, AtomicRmwOp::Sub, memarg, builder, stack, environ)? } Operator::I32AtomicRmw8SubU { memarg } => { - translate_atomic_rmw(I32, I8, AtomicRmwOp::Sub, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I8, AtomicRmwOp::Sub, memarg, builder, stack, environ)? } Operator::I32AtomicRmw16SubU { memarg } => { - translate_atomic_rmw(I32, I16, AtomicRmwOp::Sub, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I16, AtomicRmwOp::Sub, memarg, builder, stack, environ)? } Operator::I64AtomicRmw8SubU { memarg } => { - translate_atomic_rmw(I64, I8, AtomicRmwOp::Sub, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I8, AtomicRmwOp::Sub, memarg, builder, stack, environ)? } Operator::I64AtomicRmw16SubU { memarg } => { - translate_atomic_rmw(I64, I16, AtomicRmwOp::Sub, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I16, AtomicRmwOp::Sub, memarg, builder, stack, environ)? } Operator::I64AtomicRmw32SubU { memarg } => { - translate_atomic_rmw(I64, I32, AtomicRmwOp::Sub, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I32, AtomicRmwOp::Sub, memarg, builder, stack, environ)? } Operator::I32AtomicRmwAnd { memarg } => { - translate_atomic_rmw(I32, I32, AtomicRmwOp::And, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I32, AtomicRmwOp::And, memarg, builder, stack, environ)? } Operator::I64AtomicRmwAnd { memarg } => { - translate_atomic_rmw(I64, I64, AtomicRmwOp::And, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I64, AtomicRmwOp::And, memarg, builder, stack, environ)? } Operator::I32AtomicRmw8AndU { memarg } => { - translate_atomic_rmw(I32, I8, AtomicRmwOp::And, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I8, AtomicRmwOp::And, memarg, builder, stack, environ)? } Operator::I32AtomicRmw16AndU { memarg } => { - translate_atomic_rmw(I32, I16, AtomicRmwOp::And, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I16, AtomicRmwOp::And, memarg, builder, stack, environ)? } Operator::I64AtomicRmw8AndU { memarg } => { - translate_atomic_rmw(I64, I8, AtomicRmwOp::And, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I8, AtomicRmwOp::And, memarg, builder, stack, environ)? } Operator::I64AtomicRmw16AndU { memarg } => { - translate_atomic_rmw(I64, I16, AtomicRmwOp::And, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I16, AtomicRmwOp::And, memarg, builder, stack, environ)? } Operator::I64AtomicRmw32AndU { memarg } => { - translate_atomic_rmw(I64, I32, AtomicRmwOp::And, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I32, AtomicRmwOp::And, memarg, builder, stack, environ)? } Operator::I32AtomicRmwOr { memarg } => { - translate_atomic_rmw(I32, I32, AtomicRmwOp::Or, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I32, AtomicRmwOp::Or, memarg, builder, stack, environ)? } Operator::I64AtomicRmwOr { memarg } => { - translate_atomic_rmw(I64, I64, AtomicRmwOp::Or, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I64, AtomicRmwOp::Or, memarg, builder, stack, environ)? } Operator::I32AtomicRmw8OrU { memarg } => { - translate_atomic_rmw(I32, I8, AtomicRmwOp::Or, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I8, AtomicRmwOp::Or, memarg, builder, stack, environ)? } Operator::I32AtomicRmw16OrU { memarg } => { - translate_atomic_rmw(I32, I16, AtomicRmwOp::Or, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I16, AtomicRmwOp::Or, memarg, builder, stack, environ)? } Operator::I64AtomicRmw8OrU { memarg } => { - translate_atomic_rmw(I64, I8, AtomicRmwOp::Or, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I8, AtomicRmwOp::Or, memarg, builder, stack, environ)? } Operator::I64AtomicRmw16OrU { memarg } => { - translate_atomic_rmw(I64, I16, AtomicRmwOp::Or, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I16, AtomicRmwOp::Or, memarg, builder, stack, environ)? } Operator::I64AtomicRmw32OrU { memarg } => { - translate_atomic_rmw(I64, I32, AtomicRmwOp::Or, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I32, AtomicRmwOp::Or, memarg, builder, stack, environ)? } Operator::I32AtomicRmwXor { memarg } => { - translate_atomic_rmw(I32, I32, AtomicRmwOp::Xor, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I32, AtomicRmwOp::Xor, memarg, builder, stack, environ)? } Operator::I64AtomicRmwXor { memarg } => { - translate_atomic_rmw(I64, I64, AtomicRmwOp::Xor, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I64, AtomicRmwOp::Xor, memarg, builder, stack, environ)? } Operator::I32AtomicRmw8XorU { memarg } => { - translate_atomic_rmw(I32, I8, AtomicRmwOp::Xor, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I8, AtomicRmwOp::Xor, memarg, builder, stack, environ)? } Operator::I32AtomicRmw16XorU { memarg } => { - translate_atomic_rmw(I32, I16, AtomicRmwOp::Xor, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I16, AtomicRmwOp::Xor, memarg, builder, stack, environ)? } Operator::I64AtomicRmw8XorU { memarg } => { - translate_atomic_rmw(I64, I8, AtomicRmwOp::Xor, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I8, AtomicRmwOp::Xor, memarg, builder, stack, environ)? } Operator::I64AtomicRmw16XorU { memarg } => { - translate_atomic_rmw(I64, I16, AtomicRmwOp::Xor, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I16, AtomicRmwOp::Xor, memarg, builder, stack, environ)? } Operator::I64AtomicRmw32XorU { memarg } => { - translate_atomic_rmw(I64, I32, AtomicRmwOp::Xor, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I32, AtomicRmwOp::Xor, memarg, builder, stack, environ)? } Operator::I32AtomicRmwXchg { memarg } => { - translate_atomic_rmw(I32, I32, AtomicRmwOp::Xchg, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I32, AtomicRmwOp::Xchg, memarg, builder, stack, environ)? } Operator::I64AtomicRmwXchg { memarg } => { - translate_atomic_rmw(I64, I64, AtomicRmwOp::Xchg, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I64, AtomicRmwOp::Xchg, memarg, builder, stack, environ)? } Operator::I32AtomicRmw8XchgU { memarg } => { - translate_atomic_rmw(I32, I8, AtomicRmwOp::Xchg, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I8, AtomicRmwOp::Xchg, memarg, builder, stack, environ)? } Operator::I32AtomicRmw16XchgU { memarg } => { - translate_atomic_rmw(I32, I16, AtomicRmwOp::Xchg, memarg, builder, state, environ)? + translate_atomic_rmw(I32, I16, AtomicRmwOp::Xchg, memarg, builder, stack, environ)? } Operator::I64AtomicRmw8XchgU { memarg } => { - translate_atomic_rmw(I64, I8, AtomicRmwOp::Xchg, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I8, AtomicRmwOp::Xchg, memarg, builder, stack, environ)? } Operator::I64AtomicRmw16XchgU { memarg } => { - translate_atomic_rmw(I64, I16, AtomicRmwOp::Xchg, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I16, AtomicRmwOp::Xchg, memarg, builder, stack, environ)? } Operator::I64AtomicRmw32XchgU { memarg } => { - translate_atomic_rmw(I64, I32, AtomicRmwOp::Xchg, memarg, builder, state, environ)? + translate_atomic_rmw(I64, I32, AtomicRmwOp::Xchg, memarg, builder, stack, environ)? } Operator::I32AtomicRmwCmpxchg { memarg } => { - translate_atomic_cas(I32, I32, memarg, builder, state, environ)? + translate_atomic_cas(I32, I32, memarg, builder, stack, environ)? } Operator::I64AtomicRmwCmpxchg { memarg } => { - translate_atomic_cas(I64, I64, memarg, builder, state, environ)? + translate_atomic_cas(I64, I64, memarg, builder, stack, environ)? } Operator::I32AtomicRmw8CmpxchgU { memarg } => { - translate_atomic_cas(I32, I8, memarg, builder, state, environ)? + translate_atomic_cas(I32, I8, memarg, builder, stack, environ)? } Operator::I32AtomicRmw16CmpxchgU { memarg } => { - translate_atomic_cas(I32, I16, memarg, builder, state, environ)? + translate_atomic_cas(I32, I16, memarg, builder, stack, environ)? } Operator::I64AtomicRmw8CmpxchgU { memarg } => { - translate_atomic_cas(I64, I8, memarg, builder, state, environ)? + translate_atomic_cas(I64, I8, memarg, builder, stack, environ)? } Operator::I64AtomicRmw16CmpxchgU { memarg } => { - translate_atomic_cas(I64, I16, memarg, builder, state, environ)? + translate_atomic_cas(I64, I16, memarg, builder, stack, environ)? } Operator::I64AtomicRmw32CmpxchgU { memarg } => { - translate_atomic_cas(I64, I32, memarg, builder, state, environ)? + translate_atomic_cas(I64, I32, memarg, builder, stack, environ)? } Operator::AtomicFence { .. } => { @@ -1531,72 +1495,64 @@ pub fn translate_operator( } Operator::MemoryCopy { src_mem, dst_mem } => { let src_index = MemoryIndex::from_u32(*src_mem); + let _src_heap = environ.get_or_create_heap(builder.func, src_index); + let dst_index = MemoryIndex::from_u32(*dst_mem); - let src_heap = state.get_heap(builder.func, *src_mem, environ)?; - let dst_heap = state.get_heap(builder.func, *dst_mem, environ)?; - let len = state.pop1(); - let src_pos = state.pop1(); - let dst_pos = state.pop1(); - environ.translate_memory_copy( - builder, src_index, src_heap, dst_index, dst_heap, dst_pos, src_pos, len, - )?; + let _dst_heap = environ.get_or_create_heap(builder.func, dst_index); + + let len = stack.pop1(); + let src_pos = stack.pop1(); + let dst_pos = stack.pop1(); + environ.translate_memory_copy(builder, src_index, dst_index, dst_pos, src_pos, len)?; } Operator::MemoryFill { mem } => { - let heap_index = MemoryIndex::from_u32(*mem); - let heap = state.get_heap(builder.func, *mem, environ)?; - let len = state.pop1(); - let val = state.pop1(); - let dest = state.pop1(); - environ.translate_memory_fill(builder, heap_index, heap, dest, val, len)?; + let mem = MemoryIndex::from_u32(*mem); + let _heap = environ.get_or_create_heap(builder.func, mem); + let len = stack.pop1(); + let val = stack.pop1(); + let dest = stack.pop1(); + environ.translate_memory_fill(builder, mem, dest, val, len)?; } Operator::MemoryInit { data_index, mem } => { - let heap_index = MemoryIndex::from_u32(*mem); - let heap = state.get_heap(builder.func, *mem, environ)?; - let len = state.pop1(); - let src = state.pop1(); - let dest = state.pop1(); - environ.translate_memory_init( - builder, - heap_index, - heap, - *data_index, - dest, - src, - len, - )?; + let mem = MemoryIndex::from_u32(*mem); + let _heap = environ.get_or_create_heap(builder.func, mem); + let len = stack.pop1(); + let src = stack.pop1(); + let dest = stack.pop1(); + environ.translate_memory_init(builder, mem, *data_index, dest, src, len)?; } Operator::DataDrop { data_index } => { environ.translate_data_drop(builder.cursor(), *data_index)?; } Operator::TableSize { table: index } => { - state.push1( + stack.push1( environ.translate_table_size(builder.cursor(), TableIndex::from_u32(*index))?, ); } Operator::TableGrow { table: index } => { let table_index = TableIndex::from_u32(*index); - let delta = state.pop1(); - let init_value = state.pop1(); - state.push1(environ.translate_table_grow(builder, table_index, delta, init_value)?); + let delta = stack.pop1(); + let init_value = stack.pop1(); + stack.push1(environ.translate_table_grow(builder, table_index, delta, init_value)?); } Operator::TableGet { table: index } => { let table_index = TableIndex::from_u32(*index); - let index = state.pop1(); - state.push1(environ.translate_table_get(builder, table_index, index)?); + let index = stack.pop1(); + stack.push1(environ.translate_table_get(builder, table_index, index)?); } Operator::TableSet { table: index } => { let table_index = TableIndex::from_u32(*index); - let value = state.pop1(); - let index = state.pop1(); + let value = stack.pop1(); + let index = stack.pop1(); environ.translate_table_set(builder, table_index, value, index)?; } Operator::TableCopy { dst_table: dst_table_index, src_table: src_table_index, } => { - let len = state.pop1(); - let src = state.pop1(); - let dest = state.pop1(); + let len = stack.pop1(); + let src = stack.pop1(); + let dest = stack.pop1(); environ.translate_table_copy( builder, TableIndex::from_u32(*dst_table_index), @@ -1608,18 +1564,18 @@ pub fn translate_operator( } Operator::TableFill { table } => { let table_index = TableIndex::from_u32(*table); - let len = state.pop1(); - let val = state.pop1(); - let dest = state.pop1(); + let len = stack.pop1(); + let val = stack.pop1(); + let dest = stack.pop1(); environ.translate_table_fill(builder, table_index, dest, val, len)?; } Operator::TableInit { elem_index, table: table_index, } => { - let len = state.pop1(); - let src = state.pop1(); - let dest = state.pop1(); + let len = stack.pop1(); + let src = stack.pop1(); + let dest = stack.pop1(); environ.translate_table_init( builder, *elem_index, @@ -1638,89 +1594,89 @@ pub fn translate_operator( let value = builder.ins().vconst(I8X16, handle); // the v128.const is typed in CLIF as a I8x16 but bitcast to a different type // before use - state.push1(value) + stack.push1(value) } Operator::I8x16Splat | Operator::I16x8Splat => { - let reduced = builder.ins().ireduce(type_of(op).lane_type(), state.pop1()); + let reduced = builder.ins().ireduce(type_of(op).lane_type(), stack.pop1()); let splatted = builder.ins().splat(type_of(op), reduced); - state.push1(splatted) + stack.push1(splatted) } Operator::I32x4Splat | Operator::I64x2Splat | Operator::F32x4Splat | Operator::F64x2Splat => { - let splatted = builder.ins().splat(type_of(op), state.pop1()); - state.push1(splatted) + let splatted = builder.ins().splat(type_of(op), stack.pop1()); + stack.push1(splatted) } Operator::V128Load8Splat { memarg } | Operator::V128Load16Splat { memarg } | Operator::V128Load32Splat { memarg } | Operator::V128Load64Splat { memarg } => { unwrap_or_return_unreachable_state!( - state, + stack, translate_load( memarg, ir::Opcode::Load, type_of(op).lane_type(), builder, - state, + stack, environ, )? ); - let splatted = builder.ins().splat(type_of(op), state.pop1()); - state.push1(splatted) + let splatted = builder.ins().splat(type_of(op), stack.pop1()); + stack.push1(splatted) } Operator::V128Load32Zero { memarg } | Operator::V128Load64Zero { memarg } => { unwrap_or_return_unreachable_state!( - state, + stack, translate_load( memarg, ir::Opcode::Load, type_of(op).lane_type(), builder, - state, + stack, environ, )? ); - let as_vector = builder.ins().scalar_to_vector(type_of(op), state.pop1()); - state.push1(as_vector) + let as_vector = builder.ins().scalar_to_vector(type_of(op), stack.pop1()); + stack.push1(as_vector) } Operator::V128Load8Lane { memarg, lane } | Operator::V128Load16Lane { memarg, lane } | Operator::V128Load32Lane { memarg, lane } | Operator::V128Load64Lane { memarg, lane } => { - let vector = pop1_with_bitcast(state, type_of(op), builder); + let vector = pop1_with_bitcast(stack, type_of(op), builder); unwrap_or_return_unreachable_state!( - state, + stack, translate_load( memarg, ir::Opcode::Load, type_of(op).lane_type(), builder, - state, + stack, environ, )? ); - let replacement = state.pop1(); - state.push1(builder.ins().insertlane(vector, replacement, *lane)) + let replacement = stack.pop1(); + stack.push1(builder.ins().insertlane(vector, replacement, *lane)) } Operator::V128Store8Lane { memarg, lane } | Operator::V128Store16Lane { memarg, lane } | Operator::V128Store32Lane { memarg, lane } | Operator::V128Store64Lane { memarg, lane } => { - let vector = pop1_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().extractlane(vector, *lane)); - translate_store(memarg, ir::Opcode::Store, builder, state, environ)?; + let vector = pop1_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().extractlane(vector, *lane)); + translate_store(memarg, ir::Opcode::Store, builder, stack, environ)?; } Operator::I8x16ExtractLaneS { lane } | Operator::I16x8ExtractLaneS { lane } => { - let vector = pop1_with_bitcast(state, type_of(op), builder); + let vector = pop1_with_bitcast(stack, type_of(op), builder); let extracted = builder.ins().extractlane(vector, *lane); - state.push1(builder.ins().sextend(I32, extracted)) + stack.push1(builder.ins().sextend(I32, extracted)) } Operator::I8x16ExtractLaneU { lane } | Operator::I16x8ExtractLaneU { lane } => { - let vector = pop1_with_bitcast(state, type_of(op), builder); + let vector = pop1_with_bitcast(stack, type_of(op), builder); let extracted = builder.ins().extractlane(vector, *lane); - state.push1(builder.ins().uextend(I32, extracted)); + stack.push1(builder.ins().uextend(I32, extracted)); // On x86, PEXTRB zeroes the upper bits of the destination register of extractlane so // uextend could be elided; for now, uextend is needed for Cranelift's type checks to // work. @@ -1729,233 +1685,233 @@ pub fn translate_operator( | Operator::I64x2ExtractLane { lane } | Operator::F32x4ExtractLane { lane } | Operator::F64x2ExtractLane { lane } => { - let vector = pop1_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().extractlane(vector, *lane)) + let vector = pop1_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().extractlane(vector, *lane)) } Operator::I8x16ReplaceLane { lane } | Operator::I16x8ReplaceLane { lane } => { - let (vector, replacement) = state.pop2(); + let (vector, replacement) = stack.pop2(); let ty = type_of(op); let reduced = builder.ins().ireduce(ty.lane_type(), replacement); let vector = optionally_bitcast_vector(vector, ty, builder); - state.push1(builder.ins().insertlane(vector, reduced, *lane)) + stack.push1(builder.ins().insertlane(vector, reduced, *lane)) } Operator::I32x4ReplaceLane { lane } | Operator::I64x2ReplaceLane { lane } | Operator::F32x4ReplaceLane { lane } | Operator::F64x2ReplaceLane { lane } => { - let (vector, replacement) = state.pop2(); + let (vector, replacement) = stack.pop2(); let vector = optionally_bitcast_vector(vector, type_of(op), builder); - state.push1(builder.ins().insertlane(vector, replacement, *lane)) + stack.push1(builder.ins().insertlane(vector, replacement, *lane)) } Operator::I8x16Shuffle { lanes, .. } => { - let (a, b) = pop2_with_bitcast(state, I8X16, builder); - state.push1(environ.i8x16_shuffle(builder, a, b, lanes)); + let (a, b) = pop2_with_bitcast(stack, I8X16, builder); + stack.push1(environ.i8x16_shuffle(builder, a, b, lanes)); // At this point the original types of a and b are lost; users of this value (i.e. this // WASM-to-CLIF translator) may need to bitcast for type-correctness. This is due // to WASM using the less specific v128 type for certain operations and more specific // types (e.g. i8x16) for others. } Operator::I8x16Swizzle => { - let (a, b) = pop2_with_bitcast(state, I8X16, builder); - state.push1(environ.swizzle(builder, a, b)); + let (a, b) = pop2_with_bitcast(stack, I8X16, builder); + stack.push1(environ.swizzle(builder, a, b)); } Operator::I8x16Add | Operator::I16x8Add | Operator::I32x4Add | Operator::I64x2Add => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().iadd(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().iadd(a, b)) } Operator::I8x16AddSatS | Operator::I16x8AddSatS => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().sadd_sat(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().sadd_sat(a, b)) } Operator::I8x16AddSatU | Operator::I16x8AddSatU => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().uadd_sat(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().uadd_sat(a, b)) } Operator::I8x16Sub | Operator::I16x8Sub | Operator::I32x4Sub | Operator::I64x2Sub => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().isub(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().isub(a, b)) } Operator::I8x16SubSatS | Operator::I16x8SubSatS => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().ssub_sat(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().ssub_sat(a, b)) } Operator::I8x16SubSatU | Operator::I16x8SubSatU => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().usub_sat(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().usub_sat(a, b)) } Operator::I8x16MinS | Operator::I16x8MinS | Operator::I32x4MinS => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().smin(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().smin(a, b)) } Operator::I8x16MinU | Operator::I16x8MinU | Operator::I32x4MinU => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().umin(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().umin(a, b)) } Operator::I8x16MaxS | Operator::I16x8MaxS | Operator::I32x4MaxS => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().smax(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().smax(a, b)) } Operator::I8x16MaxU | Operator::I16x8MaxU | Operator::I32x4MaxU => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().umax(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().umax(a, b)) } Operator::I8x16AvgrU | Operator::I16x8AvgrU => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().avg_round(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().avg_round(a, b)) } Operator::I8x16Neg | Operator::I16x8Neg | Operator::I32x4Neg | Operator::I64x2Neg => { - let a = pop1_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().ineg(a)) + let a = pop1_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().ineg(a)) } Operator::I8x16Abs | Operator::I16x8Abs | Operator::I32x4Abs | Operator::I64x2Abs => { - let a = pop1_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().iabs(a)) + let a = pop1_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().iabs(a)) } Operator::I16x8Mul | Operator::I32x4Mul | Operator::I64x2Mul => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().imul(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().imul(a, b)) } Operator::V128Or => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().bor(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().bor(a, b)) } Operator::V128Xor => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().bxor(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().bxor(a, b)) } Operator::V128And => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().band(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().band(a, b)) } Operator::V128AndNot => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().band_not(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().band_not(a, b)) } Operator::V128Not => { - let a = state.pop1(); - state.push1(builder.ins().bnot(a)); + let a = stack.pop1(); + stack.push1(builder.ins().bnot(a)); } Operator::I8x16Shl | Operator::I16x8Shl | Operator::I32x4Shl | Operator::I64x2Shl => { - let (a, b) = state.pop2(); + let (a, b) = stack.pop2(); let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder); // The spec expects to shift with `b mod lanewidth`; This is directly compatible // with cranelift's instruction. - state.push1(builder.ins().ishl(bitcast_a, b)) + stack.push1(builder.ins().ishl(bitcast_a, b)) } Operator::I8x16ShrU | Operator::I16x8ShrU | Operator::I32x4ShrU | Operator::I64x2ShrU => { - let (a, b) = state.pop2(); + let (a, b) = stack.pop2(); let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder); // The spec expects to shift with `b mod lanewidth`; This is directly compatible // with cranelift's instruction. - state.push1(builder.ins().ushr(bitcast_a, b)) + stack.push1(builder.ins().ushr(bitcast_a, b)) } Operator::I8x16ShrS | Operator::I16x8ShrS | Operator::I32x4ShrS | Operator::I64x2ShrS => { - let (a, b) = state.pop2(); + let (a, b) = stack.pop2(); let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder); // The spec expects to shift with `b mod lanewidth`; This is directly compatible // with cranelift's instruction. - state.push1(builder.ins().sshr(bitcast_a, b)) + stack.push1(builder.ins().sshr(bitcast_a, b)) } Operator::V128Bitselect => { - let (a, b, c) = pop3_with_bitcast(state, I8X16, builder); + let (a, b, c) = pop3_with_bitcast(stack, I8X16, builder); // The CLIF operand ordering is slightly different and the types of all three // operands must match (hence the bitcast). - state.push1(builder.ins().bitselect(c, a, b)) + stack.push1(builder.ins().bitselect(c, a, b)) } Operator::V128AnyTrue => { - let a = pop1_with_bitcast(state, type_of(op), builder); + let a = pop1_with_bitcast(stack, type_of(op), builder); let bool_result = builder.ins().vany_true(a); - state.push1(builder.ins().uextend(I32, bool_result)) + stack.push1(builder.ins().uextend(I32, bool_result)) } Operator::I8x16AllTrue | Operator::I16x8AllTrue | Operator::I32x4AllTrue | Operator::I64x2AllTrue => { - let a = pop1_with_bitcast(state, type_of(op), builder); + let a = pop1_with_bitcast(stack, type_of(op), builder); let bool_result = builder.ins().vall_true(a); - state.push1(builder.ins().uextend(I32, bool_result)) + stack.push1(builder.ins().uextend(I32, bool_result)) } Operator::I8x16Bitmask | Operator::I16x8Bitmask | Operator::I32x4Bitmask | Operator::I64x2Bitmask => { - let a = pop1_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().vhigh_bits(I32, a)); + let a = pop1_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().vhigh_bits(I32, a)); } Operator::I8x16Eq | Operator::I16x8Eq | Operator::I32x4Eq | Operator::I64x2Eq => { - translate_vector_icmp(IntCC::Equal, type_of(op), builder, state) + translate_vector_icmp(IntCC::Equal, type_of(op), builder, stack) } Operator::I8x16Ne | Operator::I16x8Ne | Operator::I32x4Ne | Operator::I64x2Ne => { - translate_vector_icmp(IntCC::NotEqual, type_of(op), builder, state) + translate_vector_icmp(IntCC::NotEqual, type_of(op), builder, stack) } Operator::I8x16GtS | Operator::I16x8GtS | Operator::I32x4GtS | Operator::I64x2GtS => { - translate_vector_icmp(IntCC::SignedGreaterThan, type_of(op), builder, state) + translate_vector_icmp(IntCC::SignedGreaterThan, type_of(op), builder, stack) } Operator::I8x16LtS | Operator::I16x8LtS | Operator::I32x4LtS | Operator::I64x2LtS => { - translate_vector_icmp(IntCC::SignedLessThan, type_of(op), builder, state) + translate_vector_icmp(IntCC::SignedLessThan, type_of(op), builder, stack) } Operator::I8x16GtU | Operator::I16x8GtU | Operator::I32x4GtU => { - translate_vector_icmp(IntCC::UnsignedGreaterThan, type_of(op), builder, state) + translate_vector_icmp(IntCC::UnsignedGreaterThan, type_of(op), builder, stack) } Operator::I8x16LtU | Operator::I16x8LtU | Operator::I32x4LtU => { - translate_vector_icmp(IntCC::UnsignedLessThan, type_of(op), builder, state) + translate_vector_icmp(IntCC::UnsignedLessThan, type_of(op), builder, stack) } Operator::I8x16GeS | Operator::I16x8GeS | Operator::I32x4GeS | Operator::I64x2GeS => { - translate_vector_icmp(IntCC::SignedGreaterThanOrEqual, type_of(op), builder, state) + translate_vector_icmp(IntCC::SignedGreaterThanOrEqual, type_of(op), builder, stack) } Operator::I8x16LeS | Operator::I16x8LeS | Operator::I32x4LeS | Operator::I64x2LeS => { - translate_vector_icmp(IntCC::SignedLessThanOrEqual, type_of(op), builder, state) + translate_vector_icmp(IntCC::SignedLessThanOrEqual, type_of(op), builder, stack) } Operator::I8x16GeU | Operator::I16x8GeU | Operator::I32x4GeU => translate_vector_icmp( IntCC::UnsignedGreaterThanOrEqual, type_of(op), builder, - state, + stack, ), Operator::I8x16LeU | Operator::I16x8LeU | Operator::I32x4LeU => { - translate_vector_icmp(IntCC::UnsignedLessThanOrEqual, type_of(op), builder, state) + translate_vector_icmp(IntCC::UnsignedLessThanOrEqual, type_of(op), builder, stack) } Operator::F32x4Eq | Operator::F64x2Eq => { - translate_vector_fcmp(FloatCC::Equal, type_of(op), builder, state) + translate_vector_fcmp(FloatCC::Equal, type_of(op), builder, stack) } Operator::F32x4Ne | Operator::F64x2Ne => { - translate_vector_fcmp(FloatCC::NotEqual, type_of(op), builder, state) + translate_vector_fcmp(FloatCC::NotEqual, type_of(op), builder, stack) } Operator::F32x4Lt | Operator::F64x2Lt => { - translate_vector_fcmp(FloatCC::LessThan, type_of(op), builder, state) + translate_vector_fcmp(FloatCC::LessThan, type_of(op), builder, stack) } Operator::F32x4Gt | Operator::F64x2Gt => { - translate_vector_fcmp(FloatCC::GreaterThan, type_of(op), builder, state) + translate_vector_fcmp(FloatCC::GreaterThan, type_of(op), builder, stack) } Operator::F32x4Le | Operator::F64x2Le => { - translate_vector_fcmp(FloatCC::LessThanOrEqual, type_of(op), builder, state) + translate_vector_fcmp(FloatCC::LessThanOrEqual, type_of(op), builder, stack) } Operator::F32x4Ge | Operator::F64x2Ge => { - translate_vector_fcmp(FloatCC::GreaterThanOrEqual, type_of(op), builder, state) + translate_vector_fcmp(FloatCC::GreaterThanOrEqual, type_of(op), builder, stack) } Operator::F32x4Add | Operator::F64x2Add => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().fadd(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().fadd(a, b)) } Operator::F32x4Sub | Operator::F64x2Sub => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().fsub(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().fsub(a, b)) } Operator::F32x4Mul | Operator::F64x2Mul => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().fmul(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().fmul(a, b)) } Operator::F32x4Div | Operator::F64x2Div => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().fdiv(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().fdiv(a, b)) } Operator::F32x4Max | Operator::F64x2Max => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().fmax(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().fmax(a, b)) } Operator::F32x4Min | Operator::F64x2Min => { - let (a, b) = pop2_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().fmin(a, b)) + let (a, b) = pop2_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().fmin(a, b)) } Operator::F32x4PMax | Operator::F64x2PMax => { // Note the careful ordering here with respect to `fcmp` and @@ -1965,10 +1921,10 @@ pub fn translate_operator( // * If z1 is less than z2 then return z2. // * Else return z1. let ty = type_of(op); - let (a, b) = pop2_with_bitcast(state, ty, builder); + let (a, b) = pop2_with_bitcast(stack, ty, builder); let cmp = builder.ins().fcmp(FloatCC::LessThan, a, b); let cmp = optionally_bitcast_vector(cmp, ty, builder); - state.push1(builder.ins().bitselect(cmp, b, a)) + stack.push1(builder.ins().bitselect(cmp, b, a)) } Operator::F32x4PMin | Operator::F64x2PMin => { // Note the careful ordering here which is similar to `pmax` above: @@ -1977,60 +1933,60 @@ pub fn translate_operator( // * If z2 is less than z1 then return z2. // * Else return z1. let ty = type_of(op); - let (a, b) = pop2_with_bitcast(state, ty, builder); + let (a, b) = pop2_with_bitcast(stack, ty, builder); let cmp = builder.ins().fcmp(FloatCC::LessThan, b, a); let cmp = optionally_bitcast_vector(cmp, ty, builder); - state.push1(builder.ins().bitselect(cmp, b, a)) + stack.push1(builder.ins().bitselect(cmp, b, a)) } Operator::F32x4Sqrt | Operator::F64x2Sqrt => { - let a = pop1_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().sqrt(a)) + let a = pop1_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().sqrt(a)) } Operator::F32x4Neg | Operator::F64x2Neg => { - let a = pop1_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().fneg(a)) + let a = pop1_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().fneg(a)) } Operator::F32x4Abs | Operator::F64x2Abs => { - let a = pop1_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().fabs(a)) + let a = pop1_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().fabs(a)) } Operator::F32x4ConvertI32x4S => { - let a = pop1_with_bitcast(state, I32X4, builder); - state.push1(builder.ins().fcvt_from_sint(F32X4, a)) + let a = pop1_with_bitcast(stack, I32X4, builder); + stack.push1(builder.ins().fcvt_from_sint(F32X4, a)) } Operator::F32x4ConvertI32x4U => { - let a = pop1_with_bitcast(state, I32X4, builder); - state.push1(builder.ins().fcvt_from_uint(F32X4, a)) + let a = pop1_with_bitcast(stack, I32X4, builder); + stack.push1(builder.ins().fcvt_from_uint(F32X4, a)) } Operator::F64x2ConvertLowI32x4S => { - let a = pop1_with_bitcast(state, I32X4, builder); + let a = pop1_with_bitcast(stack, I32X4, builder); let widened_a = builder.ins().swiden_low(a); - state.push1(builder.ins().fcvt_from_sint(F64X2, widened_a)); + stack.push1(builder.ins().fcvt_from_sint(F64X2, widened_a)); } Operator::F64x2ConvertLowI32x4U => { - let a = pop1_with_bitcast(state, I32X4, builder); + let a = pop1_with_bitcast(stack, I32X4, builder); let widened_a = builder.ins().uwiden_low(a); - state.push1(builder.ins().fcvt_from_uint(F64X2, widened_a)); + stack.push1(builder.ins().fcvt_from_uint(F64X2, widened_a)); } Operator::F64x2PromoteLowF32x4 => { - let a = pop1_with_bitcast(state, F32X4, builder); - state.push1(builder.ins().fvpromote_low(a)); + let a = pop1_with_bitcast(stack, F32X4, builder); + stack.push1(builder.ins().fvpromote_low(a)); } Operator::F32x4DemoteF64x2Zero => { - let a = pop1_with_bitcast(state, F64X2, builder); - state.push1(builder.ins().fvdemote(a)); + let a = pop1_with_bitcast(stack, F64X2, builder); + stack.push1(builder.ins().fvdemote(a)); } Operator::I32x4TruncSatF32x4S => { - let a = pop1_with_bitcast(state, F32X4, builder); - state.push1(builder.ins().fcvt_to_sint_sat(I32X4, a)) + let a = pop1_with_bitcast(stack, F32X4, builder); + stack.push1(builder.ins().fcvt_to_sint_sat(I32X4, a)) } Operator::I32x4TruncSatF64x2SZero => { - let a = pop1_with_bitcast(state, F64X2, builder); + let a = pop1_with_bitcast(stack, F64X2, builder); let converted_a = builder.ins().fcvt_to_sint_sat(I64X2, a); let handle = builder.func.dfg.constants.insert(vec![0u8; 16].into()); let zero = builder.ins().vconst(I64X2, handle); - state.push1(builder.ins().snarrow(converted_a, zero)); + stack.push1(builder.ins().snarrow(converted_a, zero)); } // FIXME(#5913): the relaxed instructions here are translated the same @@ -2045,11 +2001,11 @@ pub fn translate_operator( // perhaps for other backends too) should be added and the codegen for // the relaxed instruction should conditionally be different. Operator::I32x4RelaxedTruncF32x4U | Operator::I32x4TruncSatF32x4U => { - let a = pop1_with_bitcast(state, F32X4, builder); - state.push1(builder.ins().fcvt_to_uint_sat(I32X4, a)) + let a = pop1_with_bitcast(stack, F32X4, builder); + stack.push1(builder.ins().fcvt_to_uint_sat(I32X4, a)) } Operator::I32x4RelaxedTruncF64x2UZero | Operator::I32x4TruncSatF64x2UZero => { - let a = pop1_with_bitcast(state, F64X2, builder); + let a = pop1_with_bitcast(stack, F64X2, builder); let zero_constant = builder.func.dfg.constants.insert(vec![0u8; 16].into()); let result = if environ.is_x86() && !environ.isa().has_round() { // On x86 the vector lowering for `fcvt_to_uint_sat` requires @@ -2069,218 +2025,218 @@ pub fn translate_operator( let zero = builder.ins().vconst(I64X2, zero_constant); builder.ins().uunarrow(converted_a, zero) }; - state.push1(result); + stack.push1(result); } Operator::I8x16NarrowI16x8S => { - let (a, b) = pop2_with_bitcast(state, I16X8, builder); - state.push1(builder.ins().snarrow(a, b)) + let (a, b) = pop2_with_bitcast(stack, I16X8, builder); + stack.push1(builder.ins().snarrow(a, b)) } Operator::I16x8NarrowI32x4S => { - let (a, b) = pop2_with_bitcast(state, I32X4, builder); - state.push1(builder.ins().snarrow(a, b)) + let (a, b) = pop2_with_bitcast(stack, I32X4, builder); + stack.push1(builder.ins().snarrow(a, b)) } Operator::I8x16NarrowI16x8U => { - let (a, b) = pop2_with_bitcast(state, I16X8, builder); - state.push1(builder.ins().unarrow(a, b)) + let (a, b) = pop2_with_bitcast(stack, I16X8, builder); + stack.push1(builder.ins().unarrow(a, b)) } Operator::I16x8NarrowI32x4U => { - let (a, b) = pop2_with_bitcast(state, I32X4, builder); - state.push1(builder.ins().unarrow(a, b)) + let (a, b) = pop2_with_bitcast(stack, I32X4, builder); + stack.push1(builder.ins().unarrow(a, b)) } Operator::I16x8ExtendLowI8x16S => { - let a = pop1_with_bitcast(state, I8X16, builder); - state.push1(builder.ins().swiden_low(a)) + let a = pop1_with_bitcast(stack, I8X16, builder); + stack.push1(builder.ins().swiden_low(a)) } Operator::I16x8ExtendHighI8x16S => { - let a = pop1_with_bitcast(state, I8X16, builder); - state.push1(builder.ins().swiden_high(a)) + let a = pop1_with_bitcast(stack, I8X16, builder); + stack.push1(builder.ins().swiden_high(a)) } Operator::I16x8ExtendLowI8x16U => { - let a = pop1_with_bitcast(state, I8X16, builder); - state.push1(builder.ins().uwiden_low(a)) + let a = pop1_with_bitcast(stack, I8X16, builder); + stack.push1(builder.ins().uwiden_low(a)) } Operator::I16x8ExtendHighI8x16U => { - let a = pop1_with_bitcast(state, I8X16, builder); - state.push1(builder.ins().uwiden_high(a)) + let a = pop1_with_bitcast(stack, I8X16, builder); + stack.push1(builder.ins().uwiden_high(a)) } Operator::I32x4ExtendLowI16x8S => { - let a = pop1_with_bitcast(state, I16X8, builder); - state.push1(builder.ins().swiden_low(a)) + let a = pop1_with_bitcast(stack, I16X8, builder); + stack.push1(builder.ins().swiden_low(a)) } Operator::I32x4ExtendHighI16x8S => { - let a = pop1_with_bitcast(state, I16X8, builder); - state.push1(builder.ins().swiden_high(a)) + let a = pop1_with_bitcast(stack, I16X8, builder); + stack.push1(builder.ins().swiden_high(a)) } Operator::I32x4ExtendLowI16x8U => { - let a = pop1_with_bitcast(state, I16X8, builder); - state.push1(builder.ins().uwiden_low(a)) + let a = pop1_with_bitcast(stack, I16X8, builder); + stack.push1(builder.ins().uwiden_low(a)) } Operator::I32x4ExtendHighI16x8U => { - let a = pop1_with_bitcast(state, I16X8, builder); - state.push1(builder.ins().uwiden_high(a)) + let a = pop1_with_bitcast(stack, I16X8, builder); + stack.push1(builder.ins().uwiden_high(a)) } Operator::I64x2ExtendLowI32x4S => { - let a = pop1_with_bitcast(state, I32X4, builder); - state.push1(builder.ins().swiden_low(a)) + let a = pop1_with_bitcast(stack, I32X4, builder); + stack.push1(builder.ins().swiden_low(a)) } Operator::I64x2ExtendHighI32x4S => { - let a = pop1_with_bitcast(state, I32X4, builder); - state.push1(builder.ins().swiden_high(a)) + let a = pop1_with_bitcast(stack, I32X4, builder); + stack.push1(builder.ins().swiden_high(a)) } Operator::I64x2ExtendLowI32x4U => { - let a = pop1_with_bitcast(state, I32X4, builder); - state.push1(builder.ins().uwiden_low(a)) + let a = pop1_with_bitcast(stack, I32X4, builder); + stack.push1(builder.ins().uwiden_low(a)) } Operator::I64x2ExtendHighI32x4U => { - let a = pop1_with_bitcast(state, I32X4, builder); - state.push1(builder.ins().uwiden_high(a)) + let a = pop1_with_bitcast(stack, I32X4, builder); + stack.push1(builder.ins().uwiden_high(a)) } Operator::I16x8ExtAddPairwiseI8x16S => { - let a = pop1_with_bitcast(state, I8X16, builder); + let a = pop1_with_bitcast(stack, I8X16, builder); let widen_low = builder.ins().swiden_low(a); let widen_high = builder.ins().swiden_high(a); - state.push1(builder.ins().iadd_pairwise(widen_low, widen_high)); + stack.push1(builder.ins().iadd_pairwise(widen_low, widen_high)); } Operator::I32x4ExtAddPairwiseI16x8S => { - let a = pop1_with_bitcast(state, I16X8, builder); + let a = pop1_with_bitcast(stack, I16X8, builder); let widen_low = builder.ins().swiden_low(a); let widen_high = builder.ins().swiden_high(a); - state.push1(builder.ins().iadd_pairwise(widen_low, widen_high)); + stack.push1(builder.ins().iadd_pairwise(widen_low, widen_high)); } Operator::I16x8ExtAddPairwiseI8x16U => { - let a = pop1_with_bitcast(state, I8X16, builder); + let a = pop1_with_bitcast(stack, I8X16, builder); let widen_low = builder.ins().uwiden_low(a); let widen_high = builder.ins().uwiden_high(a); - state.push1(builder.ins().iadd_pairwise(widen_low, widen_high)); + stack.push1(builder.ins().iadd_pairwise(widen_low, widen_high)); } Operator::I32x4ExtAddPairwiseI16x8U => { - let a = pop1_with_bitcast(state, I16X8, builder); + let a = pop1_with_bitcast(stack, I16X8, builder); let widen_low = builder.ins().uwiden_low(a); let widen_high = builder.ins().uwiden_high(a); - state.push1(builder.ins().iadd_pairwise(widen_low, widen_high)); + stack.push1(builder.ins().iadd_pairwise(widen_low, widen_high)); } Operator::F32x4Ceil => { - let arg = pop1_with_bitcast(state, F32X4, builder); - state.push1(environ.ceil_f32x4(builder, arg)); + let arg = pop1_with_bitcast(stack, F32X4, builder); + stack.push1(environ.ceil_f32x4(builder, arg)); } Operator::F64x2Ceil => { - let arg = pop1_with_bitcast(state, F64X2, builder); - state.push1(environ.ceil_f64x2(builder, arg)); + let arg = pop1_with_bitcast(stack, F64X2, builder); + stack.push1(environ.ceil_f64x2(builder, arg)); } Operator::F32x4Floor => { - let arg = pop1_with_bitcast(state, F32X4, builder); - state.push1(environ.floor_f32x4(builder, arg)); + let arg = pop1_with_bitcast(stack, F32X4, builder); + stack.push1(environ.floor_f32x4(builder, arg)); } Operator::F64x2Floor => { - let arg = pop1_with_bitcast(state, F64X2, builder); - state.push1(environ.floor_f64x2(builder, arg)); + let arg = pop1_with_bitcast(stack, F64X2, builder); + stack.push1(environ.floor_f64x2(builder, arg)); } Operator::F32x4Trunc => { - let arg = pop1_with_bitcast(state, F32X4, builder); - state.push1(environ.trunc_f32x4(builder, arg)); + let arg = pop1_with_bitcast(stack, F32X4, builder); + stack.push1(environ.trunc_f32x4(builder, arg)); } Operator::F64x2Trunc => { - let arg = pop1_with_bitcast(state, F64X2, builder); - state.push1(environ.trunc_f64x2(builder, arg)); + let arg = pop1_with_bitcast(stack, F64X2, builder); + stack.push1(environ.trunc_f64x2(builder, arg)); } Operator::F32x4Nearest => { - let arg = pop1_with_bitcast(state, F32X4, builder); - state.push1(environ.nearest_f32x4(builder, arg)); + let arg = pop1_with_bitcast(stack, F32X4, builder); + stack.push1(environ.nearest_f32x4(builder, arg)); } Operator::F64x2Nearest => { - let arg = pop1_with_bitcast(state, F64X2, builder); - state.push1(environ.nearest_f64x2(builder, arg)); + let arg = pop1_with_bitcast(stack, F64X2, builder); + stack.push1(environ.nearest_f64x2(builder, arg)); } Operator::I32x4DotI16x8S => { - let (a, b) = pop2_with_bitcast(state, I16X8, builder); + let (a, b) = pop2_with_bitcast(stack, I16X8, builder); let alow = builder.ins().swiden_low(a); let blow = builder.ins().swiden_low(b); let low = builder.ins().imul(alow, blow); let ahigh = builder.ins().swiden_high(a); let bhigh = builder.ins().swiden_high(b); let high = builder.ins().imul(ahigh, bhigh); - state.push1(builder.ins().iadd_pairwise(low, high)); + stack.push1(builder.ins().iadd_pairwise(low, high)); } Operator::I8x16Popcnt => { - let arg = pop1_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().popcnt(arg)); + let arg = pop1_with_bitcast(stack, type_of(op), builder); + stack.push1(builder.ins().popcnt(arg)); } Operator::I16x8Q15MulrSatS => { - let (a, b) = pop2_with_bitcast(state, I16X8, builder); - state.push1(builder.ins().sqmul_round_sat(a, b)) + let (a, b) = pop2_with_bitcast(stack, I16X8, builder); + stack.push1(builder.ins().sqmul_round_sat(a, b)) } Operator::I16x8ExtMulLowI8x16S => { - let (a, b) = pop2_with_bitcast(state, I8X16, builder); + let (a, b) = pop2_with_bitcast(stack, I8X16, builder); let a_low = builder.ins().swiden_low(a); let b_low = builder.ins().swiden_low(b); - state.push1(builder.ins().imul(a_low, b_low)); + stack.push1(builder.ins().imul(a_low, b_low)); } Operator::I16x8ExtMulHighI8x16S => { - let (a, b) = pop2_with_bitcast(state, I8X16, builder); + let (a, b) = pop2_with_bitcast(stack, I8X16, builder); let a_high = builder.ins().swiden_high(a); let b_high = builder.ins().swiden_high(b); - state.push1(builder.ins().imul(a_high, b_high)); + stack.push1(builder.ins().imul(a_high, b_high)); } Operator::I16x8ExtMulLowI8x16U => { - let (a, b) = pop2_with_bitcast(state, I8X16, builder); + let (a, b) = pop2_with_bitcast(stack, I8X16, builder); let a_low = builder.ins().uwiden_low(a); let b_low = builder.ins().uwiden_low(b); - state.push1(builder.ins().imul(a_low, b_low)); + stack.push1(builder.ins().imul(a_low, b_low)); } Operator::I16x8ExtMulHighI8x16U => { - let (a, b) = pop2_with_bitcast(state, I8X16, builder); + let (a, b) = pop2_with_bitcast(stack, I8X16, builder); let a_high = builder.ins().uwiden_high(a); let b_high = builder.ins().uwiden_high(b); - state.push1(builder.ins().imul(a_high, b_high)); + stack.push1(builder.ins().imul(a_high, b_high)); } Operator::I32x4ExtMulLowI16x8S => { - let (a, b) = pop2_with_bitcast(state, I16X8, builder); + let (a, b) = pop2_with_bitcast(stack, I16X8, builder); let a_low = builder.ins().swiden_low(a); let b_low = builder.ins().swiden_low(b); - state.push1(builder.ins().imul(a_low, b_low)); + stack.push1(builder.ins().imul(a_low, b_low)); } Operator::I32x4ExtMulHighI16x8S => { - let (a, b) = pop2_with_bitcast(state, I16X8, builder); + let (a, b) = pop2_with_bitcast(stack, I16X8, builder); let a_high = builder.ins().swiden_high(a); let b_high = builder.ins().swiden_high(b); - state.push1(builder.ins().imul(a_high, b_high)); + stack.push1(builder.ins().imul(a_high, b_high)); } Operator::I32x4ExtMulLowI16x8U => { - let (a, b) = pop2_with_bitcast(state, I16X8, builder); + let (a, b) = pop2_with_bitcast(stack, I16X8, builder); let a_low = builder.ins().uwiden_low(a); let b_low = builder.ins().uwiden_low(b); - state.push1(builder.ins().imul(a_low, b_low)); + stack.push1(builder.ins().imul(a_low, b_low)); } Operator::I32x4ExtMulHighI16x8U => { - let (a, b) = pop2_with_bitcast(state, I16X8, builder); + let (a, b) = pop2_with_bitcast(stack, I16X8, builder); let a_high = builder.ins().uwiden_high(a); let b_high = builder.ins().uwiden_high(b); - state.push1(builder.ins().imul(a_high, b_high)); + stack.push1(builder.ins().imul(a_high, b_high)); } Operator::I64x2ExtMulLowI32x4S => { - let (a, b) = pop2_with_bitcast(state, I32X4, builder); + let (a, b) = pop2_with_bitcast(stack, I32X4, builder); let a_low = builder.ins().swiden_low(a); let b_low = builder.ins().swiden_low(b); - state.push1(builder.ins().imul(a_low, b_low)); + stack.push1(builder.ins().imul(a_low, b_low)); } Operator::I64x2ExtMulHighI32x4S => { - let (a, b) = pop2_with_bitcast(state, I32X4, builder); + let (a, b) = pop2_with_bitcast(stack, I32X4, builder); let a_high = builder.ins().swiden_high(a); let b_high = builder.ins().swiden_high(b); - state.push1(builder.ins().imul(a_high, b_high)); + stack.push1(builder.ins().imul(a_high, b_high)); } Operator::I64x2ExtMulLowI32x4U => { - let (a, b) = pop2_with_bitcast(state, I32X4, builder); + let (a, b) = pop2_with_bitcast(stack, I32X4, builder); let a_low = builder.ins().uwiden_low(a); let b_low = builder.ins().uwiden_low(b); - state.push1(builder.ins().imul(a_low, b_low)); + stack.push1(builder.ins().imul(a_low, b_low)); } Operator::I64x2ExtMulHighI32x4U => { - let (a, b) = pop2_with_bitcast(state, I32X4, builder); + let (a, b) = pop2_with_bitcast(stack, I32X4, builder); let a_high = builder.ins().uwiden_high(a); let b_high = builder.ins().uwiden_high(b); - state.push1(builder.ins().imul(a_high, b_high)); + stack.push1(builder.ins().imul(a_high, b_high)); } Operator::MemoryDiscard { .. } => { return Err(wasm_unsupported!( @@ -2291,8 +2247,8 @@ pub fn translate_operator( Operator::F32x4RelaxedMax | Operator::F64x2RelaxedMax => { let ty = type_of(op); - let (a, b) = pop2_with_bitcast(state, ty, builder); - state.push1( + let (a, b) = pop2_with_bitcast(stack, ty, builder); + stack.push1( if environ.relaxed_simd_deterministic() || !environ.is_x86() { // Deterministic semantics match the `fmax` instruction, or // the `fAAxBB.max` wasm instruction. @@ -2310,8 +2266,8 @@ pub fn translate_operator( Operator::F32x4RelaxedMin | Operator::F64x2RelaxedMin => { let ty = type_of(op); - let (a, b) = pop2_with_bitcast(state, ty, builder); - state.push1( + let (a, b) = pop2_with_bitcast(stack, ty, builder); + stack.push1( if environ.relaxed_simd_deterministic() || !environ.is_x86() { // Deterministic semantics match the `fmin` instruction, or // the `fAAxBB.min` wasm instruction. @@ -2328,27 +2284,27 @@ pub fn translate_operator( } Operator::I8x16RelaxedSwizzle => { - let (a, b) = pop2_with_bitcast(state, I8X16, builder); - state.push1(environ.relaxed_swizzle(builder, a, b)); + let (a, b) = pop2_with_bitcast(stack, I8X16, builder); + stack.push1(environ.relaxed_swizzle(builder, a, b)); } Operator::F32x4RelaxedMadd => { - let (a, b, c) = pop3_with_bitcast(state, type_of(op), builder); - state.push1(environ.fma_f32x4(builder, a, b, c)); + let (a, b, c) = pop3_with_bitcast(stack, type_of(op), builder); + stack.push1(environ.fma_f32x4(builder, a, b, c)); } Operator::F64x2RelaxedMadd => { - let (a, b, c) = pop3_with_bitcast(state, type_of(op), builder); - state.push1(environ.fma_f64x2(builder, a, b, c)); + let (a, b, c) = pop3_with_bitcast(stack, type_of(op), builder); + stack.push1(environ.fma_f64x2(builder, a, b, c)); } Operator::F32x4RelaxedNmadd => { - let (a, b, c) = pop3_with_bitcast(state, type_of(op), builder); + let (a, b, c) = pop3_with_bitcast(stack, type_of(op), builder); let a = builder.ins().fneg(a); - state.push1(environ.fma_f32x4(builder, a, b, c)); + stack.push1(environ.fma_f32x4(builder, a, b, c)); } Operator::F64x2RelaxedNmadd => { - let (a, b, c) = pop3_with_bitcast(state, type_of(op), builder); + let (a, b, c) = pop3_with_bitcast(stack, type_of(op), builder); let a = builder.ins().fneg(a); - state.push1(environ.fma_f64x2(builder, a, b, c)); + stack.push1(environ.fma_f64x2(builder, a, b, c)); } Operator::I8x16RelaxedLaneselect @@ -2356,11 +2312,11 @@ pub fn translate_operator( | Operator::I32x4RelaxedLaneselect | Operator::I64x2RelaxedLaneselect => { let ty = type_of(op); - let (a, b, c) = pop3_with_bitcast(state, ty, builder); + let (a, b, c) = pop3_with_bitcast(stack, ty, builder); // Note that the variable swaps here are intentional due to // the difference of the order of the wasm op and the clif // op. - state.push1( + stack.push1( if environ.relaxed_simd_deterministic() || !environ.use_x86_blendv_for_relaxed_laneselect(ty) { @@ -2374,8 +2330,8 @@ pub fn translate_operator( } Operator::I32x4RelaxedTruncF32x4S => { - let a = pop1_with_bitcast(state, F32X4, builder); - state.push1( + let a = pop1_with_bitcast(stack, F32X4, builder); + stack.push1( if environ.relaxed_simd_deterministic() || !environ.is_x86() { // Deterministic semantics are to match the // `i32x4.trunc_sat_f32x4_s` instruction. @@ -2386,7 +2342,7 @@ pub fn translate_operator( ) } Operator::I32x4RelaxedTruncF64x2SZero => { - let a = pop1_with_bitcast(state, F64X2, builder); + let a = pop1_with_bitcast(stack, F64X2, builder); let converted_a = if environ.relaxed_simd_deterministic() || !environ.is_x86() { // Deterministic semantics are to match the // `i32x4.trunc_sat_f64x2_s_zero` instruction. @@ -2397,11 +2353,11 @@ pub fn translate_operator( let handle = builder.func.dfg.constants.insert(vec![0u8; 16].into()); let zero = builder.ins().vconst(I64X2, handle); - state.push1(builder.ins().snarrow(converted_a, zero)); + stack.push1(builder.ins().snarrow(converted_a, zero)); } Operator::I16x8RelaxedQ15mulrS => { - let (a, b) = pop2_with_bitcast(state, I16X8, builder); - state.push1( + let (a, b) = pop2_with_bitcast(stack, I16X8, builder); + stack.push1( if environ.relaxed_simd_deterministic() || !environ.use_x86_pmulhrsw_for_relaxed_q15mul() { @@ -2414,8 +2370,8 @@ pub fn translate_operator( ); } Operator::I16x8RelaxedDotI8x16I7x16S => { - let (a, b) = pop2_with_bitcast(state, I8X16, builder); - state.push1( + let (a, b) = pop2_with_bitcast(stack, I8X16, builder); + stack.push1( if environ.relaxed_simd_deterministic() || !environ.use_x86_pmaddubsw_for_dot() { // Deterministic semantics are to treat both operands as // signed integers and perform the dot product. @@ -2433,8 +2389,8 @@ pub fn translate_operator( } Operator::I32x4RelaxedDotI8x16I7x16AddS => { - let c = pop1_with_bitcast(state, I32X4, builder); - let (a, b) = pop2_with_bitcast(state, I8X16, builder); + let c = pop1_with_bitcast(stack, I32X4, builder); + let (a, b) = pop2_with_bitcast(stack, I8X16, builder); let dot = if environ.relaxed_simd_deterministic() || !environ.use_x86_pmaddubsw_for_dot() { // Deterministic semantics are to treat both operands as @@ -2452,22 +2408,22 @@ pub fn translate_operator( let dotlo = builder.ins().swiden_low(dot); let dothi = builder.ins().swiden_high(dot); let dot32 = builder.ins().iadd_pairwise(dotlo, dothi); - state.push1(builder.ins().iadd(dot32, c)); + stack.push1(builder.ins().iadd(dot32, c)); } Operator::BrOnNull { relative_depth } => { - let r = state.pop1(); + let r = stack.pop1(); let [.., WasmValType::Ref(r_ty)] = operand_types else { unreachable!("validation") }; - let (br_destination, inputs) = translate_br_if_args(*relative_depth, state); + let (br_destination, inputs) = translate_br_if_args(*relative_depth, stack); let is_null = environ.translate_ref_is_null(builder.cursor(), r, *r_ty)?; let else_block = builder.create_block(); canonicalise_brif(builder, is_null, br_destination, inputs, else_block, &[]); builder.seal_block(else_block); // The only predecessor is the current block. builder.switch_to_block(else_block); - state.push1(r); + stack.push1(r); } Operator::BrOnNonNull { relative_depth } => { // We write this a bit differently from the spec to avoid an extra @@ -2476,17 +2432,17 @@ pub fn translate_operator( // Peek the value val from the stack. // If val is ref.null ht, then: pop the value val from the stack. // Else: Execute the instruction (br relative_depth). - let r = state.peek1(); + let r = stack.peek1(); let [.., WasmValType::Ref(r_ty)] = operand_types else { unreachable!("validation") }; let is_null = environ.translate_ref_is_null(builder.cursor(), r, *r_ty)?; - let (br_destination, inputs) = translate_br_if_args(*relative_depth, state); + let (br_destination, inputs) = translate_br_if_args(*relative_depth, stack); let else_block = builder.create_block(); canonicalise_brif(builder, is_null, else_block, &[], br_destination, inputs); // In the null case, pop the ref - state.pop1(); + stack.pop1(); builder.seal_block(else_block); // The only predecessor is the current block. @@ -2498,15 +2454,17 @@ pub fn translate_operator( // Get function signature // `index` is the index of the function's signature and `table_index` is the index of // the table to search the function in. - let (sigref, num_args) = state.get_indirect_sig(builder.func, *type_index, environ)?; - let callee = state.pop1(); + let type_index = TypeIndex::from_u32(*type_index); + let sigref = environ.get_or_create_sig_ref(builder.func, type_index); + let num_args = environ.num_params_for_function_type(type_index); + let callee = stack.pop1(); // Bitcast any vector arguments to their default type, I8X16, before calling. - let args = state.peekn_mut(num_args); + let args = stack.peekn_mut(num_args); bitcast_wasm_params(environ, sigref, args, builder); let call = - environ.translate_call_ref(builder, sigref, callee, state.peekn(num_args))?; + environ.translate_call_ref(builder, sigref, callee, stack.peekn(num_args))?; let inst_results = builder.inst_results(call); debug_assert_eq!( @@ -2514,48 +2472,48 @@ pub fn translate_operator( builder.func.dfg.signatures[sigref].returns.len(), "translate_call_ref results should match the call signature" ); - state.popn(num_args); - state.pushn(inst_results); + stack.popn(num_args); + stack.pushn(inst_results); } Operator::RefAsNonNull => { - let r = state.pop1(); + let r = stack.pop1(); let [.., WasmValType::Ref(r_ty)] = operand_types else { unreachable!("validation") }; let is_null = environ.translate_ref_is_null(builder.cursor(), r, *r_ty)?; environ.trapnz(builder, is_null, crate::TRAP_NULL_REFERENCE); - state.push1(r); + stack.push1(r); } Operator::RefI31 => { - let val = state.pop1(); + let val = stack.pop1(); let i31ref = environ.translate_ref_i31(builder.cursor(), val)?; - state.push1(i31ref); + stack.push1(i31ref); } Operator::I31GetS => { - let i31ref = state.pop1(); + let i31ref = stack.pop1(); let val = environ.translate_i31_get_s(builder, i31ref)?; - state.push1(val); + stack.push1(val); } Operator::I31GetU => { - let i31ref = state.pop1(); + let i31ref = stack.pop1(); let val = environ.translate_i31_get_u(builder, i31ref)?; - state.push1(val); + stack.push1(val); } Operator::StructNew { struct_type_index } => { let struct_type_index = TypeIndex::from_u32(*struct_type_index); let arity = environ.struct_fields_len(struct_type_index)?; - let fields: StructFieldsVec = state.peekn(arity).iter().copied().collect(); - state.popn(arity); + let fields: StructFieldsVec = stack.peekn(arity).iter().copied().collect(); + stack.popn(arity); let struct_ref = environ.translate_struct_new(builder, struct_type_index, fields)?; - state.push1(struct_ref); + stack.push1(struct_ref); } Operator::StructNewDefault { struct_type_index } => { let struct_type_index = TypeIndex::from_u32(*struct_type_index); let struct_ref = environ.translate_struct_new_default(builder, struct_type_index)?; - state.push1(struct_ref); + stack.push1(struct_ref); } Operator::StructSet { @@ -2563,8 +2521,8 @@ pub fn translate_operator( field_index, } => { let struct_type_index = TypeIndex::from_u32(*struct_type_index); - let val = state.pop1(); - let struct_ref = state.pop1(); + let val = stack.pop1(); + let struct_ref = stack.pop1(); environ.translate_struct_set( builder, struct_type_index, @@ -2579,7 +2537,7 @@ pub fn translate_operator( field_index, } => { let struct_type_index = TypeIndex::from_u32(*struct_type_index); - let struct_ref = state.pop1(); + let struct_ref = stack.pop1(); let val = environ.translate_struct_get( builder, struct_type_index, @@ -2587,7 +2545,7 @@ pub fn translate_operator( struct_ref, Some(Extension::Sign), )?; - state.push1(val); + stack.push1(val); } Operator::StructGetU { @@ -2595,7 +2553,7 @@ pub fn translate_operator( field_index, } => { let struct_type_index = TypeIndex::from_u32(*struct_type_index); - let struct_ref = state.pop1(); + let struct_ref = stack.pop1(); let val = environ.translate_struct_get( builder, struct_type_index, @@ -2603,7 +2561,7 @@ pub fn translate_operator( struct_ref, Some(Extension::Zero), )?; - state.push1(val); + stack.push1(val); } Operator::StructGet { @@ -2611,7 +2569,7 @@ pub fn translate_operator( field_index, } => { let struct_type_index = TypeIndex::from_u32(*struct_type_index); - let struct_ref = state.pop1(); + let struct_ref = stack.pop1(); let val = environ.translate_struct_get( builder, struct_type_index, @@ -2619,7 +2577,7 @@ pub fn translate_operator( struct_ref, None, )?; - state.push1(val); + stack.push1(val); } Operator::TryTable { .. } | Operator::ThrowRef => { @@ -2630,15 +2588,15 @@ pub fn translate_operator( Operator::ArrayNew { array_type_index } => { let array_type_index = TypeIndex::from_u32(*array_type_index); - let (elem, len) = state.pop2(); + let (elem, len) = stack.pop2(); let array_ref = environ.translate_array_new(builder, array_type_index, elem, len)?; - state.push1(array_ref); + stack.push1(array_ref); } Operator::ArrayNewDefault { array_type_index } => { let array_type_index = TypeIndex::from_u32(*array_type_index); - let len = state.pop1(); + let len = stack.pop1(); let array_ref = environ.translate_array_new_default(builder, array_type_index, len)?; - state.push1(array_ref); + stack.push1(array_ref); } Operator::ArrayNewFixed { array_type_index, @@ -2646,10 +2604,10 @@ pub fn translate_operator( } => { let array_type_index = TypeIndex::from_u32(*array_type_index); let array_size = usize::try_from(*array_size).unwrap(); - let elems = state.peekn(array_size); + let elems = stack.peekn(array_size); let array_ref = environ.translate_array_new_fixed(builder, array_type_index, elems)?; - state.popn(array_size); - state.push1(array_ref); + stack.popn(array_size); + stack.push1(array_ref); } Operator::ArrayNewData { array_type_index, @@ -2657,7 +2615,7 @@ pub fn translate_operator( } => { let array_type_index = TypeIndex::from_u32(*array_type_index); let array_data_index = DataIndex::from_u32(*array_data_index); - let (data_offset, len) = state.pop2(); + let (data_offset, len) = stack.pop2(); let array_ref = environ.translate_array_new_data( builder, array_type_index, @@ -2665,7 +2623,7 @@ pub fn translate_operator( data_offset, len, )?; - state.push1(array_ref); + stack.push1(array_ref); } Operator::ArrayNewElem { array_type_index, @@ -2673,7 +2631,7 @@ pub fn translate_operator( } => { let array_type_index = TypeIndex::from_u32(*array_type_index); let array_elem_index = ElemIndex::from_u32(*array_elem_index); - let (elem_offset, len) = state.pop2(); + let (elem_offset, len) = stack.pop2(); let array_ref = environ.translate_array_new_elem( builder, array_type_index, @@ -2681,7 +2639,7 @@ pub fn translate_operator( elem_offset, len, )?; - state.push1(array_ref); + stack.push1(array_ref); } Operator::ArrayCopy { array_type_index_dst, @@ -2689,7 +2647,7 @@ pub fn translate_operator( } => { let array_type_index_dst = TypeIndex::from_u32(*array_type_index_dst); let array_type_index_src = TypeIndex::from_u32(*array_type_index_src); - let (dst_array, dst_index, src_array, src_index, len) = state.pop5(); + let (dst_array, dst_index, src_array, src_index, len) = stack.pop5(); environ.translate_array_copy( builder, array_type_index_dst, @@ -2703,7 +2661,7 @@ pub fn translate_operator( } Operator::ArrayFill { array_type_index } => { let array_type_index = TypeIndex::from_u32(*array_type_index); - let (array, index, val, len) = state.pop4(); + let (array, index, val, len) = stack.pop4(); environ.translate_array_fill(builder, array_type_index, array, index, val, len)?; } Operator::ArrayInitData { @@ -2712,7 +2670,7 @@ pub fn translate_operator( } => { let array_type_index = TypeIndex::from_u32(*array_type_index); let array_data_index = DataIndex::from_u32(*array_data_index); - let (array, dst_index, src_index, len) = state.pop4(); + let (array, dst_index, src_index, len) = stack.pop4(); environ.translate_array_init_data( builder, array_type_index, @@ -2729,7 +2687,7 @@ pub fn translate_operator( } => { let array_type_index = TypeIndex::from_u32(*array_type_index); let array_elem_index = ElemIndex::from_u32(*array_elem_index); - let (array, dst_index, src_index, len) = state.pop4(); + let (array, dst_index, src_index, len) = stack.pop4(); environ.translate_array_init_elem( builder, array_type_index, @@ -2741,20 +2699,20 @@ pub fn translate_operator( )?; } Operator::ArrayLen => { - let array = state.pop1(); + let array = stack.pop1(); let len = environ.translate_array_len(builder, array)?; - state.push1(len); + stack.push1(len); } Operator::ArrayGet { array_type_index } => { let array_type_index = TypeIndex::from_u32(*array_type_index); - let (array, index) = state.pop2(); + let (array, index) = stack.pop2(); let elem = environ.translate_array_get(builder, array_type_index, array, index, None)?; - state.push1(elem); + stack.push1(elem); } Operator::ArrayGetS { array_type_index } => { let array_type_index = TypeIndex::from_u32(*array_type_index); - let (array, index) = state.pop2(); + let (array, index) = stack.pop2(); let elem = environ.translate_array_get( builder, array_type_index, @@ -2762,11 +2720,11 @@ pub fn translate_operator( index, Some(Extension::Sign), )?; - state.push1(elem); + stack.push1(elem); } Operator::ArrayGetU { array_type_index } => { let array_type_index = TypeIndex::from_u32(*array_type_index); - let (array, index) = state.pop2(); + let (array, index) = stack.pop2(); let elem = environ.translate_array_get( builder, array_type_index, @@ -2774,21 +2732,21 @@ pub fn translate_operator( index, Some(Extension::Zero), )?; - state.push1(elem); + stack.push1(elem); } Operator::ArraySet { array_type_index } => { let array_type_index = TypeIndex::from_u32(*array_type_index); - let (array, index, elem) = state.pop3(); + let (array, index, elem) = stack.pop3(); environ.translate_array_set(builder, array_type_index, array, index, elem)?; } Operator::RefEq => { - let (r1, r2) = state.pop2(); + let (r1, r2) = stack.pop2(); let eq = builder.ins().icmp(ir::condcodes::IntCC::Equal, r1, r2); let eq = builder.ins().uextend(ir::types::I32, eq); - state.push1(eq); + stack.push1(eq); } Operator::RefTestNonNull { hty } => { - let r = state.pop1(); + let r = stack.pop1(); let [.., WasmValType::Ref(r_ty)] = operand_types else { unreachable!("validation") }; @@ -2802,10 +2760,10 @@ pub fn translate_operator( r, *r_ty, )?; - state.push1(result); + stack.push1(result); } Operator::RefTestNullable { hty } => { - let r = state.pop1(); + let r = stack.pop1(); let [.., WasmValType::Ref(r_ty)] = operand_types else { unreachable!("validation") }; @@ -2819,10 +2777,10 @@ pub fn translate_operator( r, *r_ty, )?; - state.push1(result); + stack.push1(result); } Operator::RefCastNonNull { hty } => { - let r = state.pop1(); + let r = stack.pop1(); let [.., WasmValType::Ref(r_ty)] = operand_types else { unreachable!("validation") }; @@ -2837,10 +2795,10 @@ pub fn translate_operator( *r_ty, )?; environ.trapz(builder, cast_okay, crate::TRAP_CAST_FAILURE); - state.push1(r); + stack.push1(r); } Operator::RefCastNullable { hty } => { - let r = state.pop1(); + let r = stack.pop1(); let [.., WasmValType::Ref(r_ty)] = operand_types else { unreachable!("validation") }; @@ -2855,14 +2813,14 @@ pub fn translate_operator( *r_ty, )?; environ.trapz(builder, cast_okay, crate::TRAP_CAST_FAILURE); - state.push1(r); + stack.push1(r); } Operator::BrOnCast { relative_depth, to_ref_type, from_ref_type: _, } => { - let r = state.peek1(); + let r = stack.peek1(); let [.., WasmValType::Ref(r_ty)] = operand_types else { unreachable!("validation") }; @@ -2870,7 +2828,7 @@ pub fn translate_operator( let to_ref_type = environ.convert_ref_type(*to_ref_type)?; let cast_is_okay = environ.translate_ref_test(builder, to_ref_type, r, *r_ty)?; - let (cast_succeeds_block, inputs) = translate_br_if_args(*relative_depth, state); + let (cast_succeeds_block, inputs) = translate_br_if_args(*relative_depth, stack); let cast_fails_block = builder.create_block(); canonicalise_brif( builder, @@ -2896,7 +2854,7 @@ pub fn translate_operator( to_ref_type, from_ref_type: _, } => { - let r = state.peek1(); + let r = stack.peek1(); let [.., WasmValType::Ref(r_ty)] = operand_types else { unreachable!("validation") }; @@ -2904,7 +2862,7 @@ pub fn translate_operator( let to_ref_type = environ.convert_ref_type(*to_ref_type)?; let cast_is_okay = environ.translate_ref_test(builder, to_ref_type, r, *r_ty)?; - let (cast_fails_block, inputs) = translate_br_if_args(*relative_depth, state); + let (cast_fails_block, inputs) = translate_br_if_args(*relative_depth, stack); let cast_succeeds_block = builder.create_block(); canonicalise_brif( builder, @@ -3027,36 +2985,36 @@ pub fn translate_operator( } Operator::I64MulWideS => { - let (arg1, arg2) = state.pop2(); + let (arg1, arg2) = stack.pop2(); let arg1 = builder.ins().sextend(I128, arg1); let arg2 = builder.ins().sextend(I128, arg2); let result = builder.ins().imul(arg1, arg2); let (lo, hi) = builder.ins().isplit(result); - state.push2(lo, hi); + stack.push2(lo, hi); } Operator::I64MulWideU => { - let (arg1, arg2) = state.pop2(); + let (arg1, arg2) = stack.pop2(); let arg1 = builder.ins().uextend(I128, arg1); let arg2 = builder.ins().uextend(I128, arg2); let result = builder.ins().imul(arg1, arg2); let (lo, hi) = builder.ins().isplit(result); - state.push2(lo, hi); + stack.push2(lo, hi); } Operator::I64Add128 => { - let (arg1, arg2, arg3, arg4) = state.pop4(); + let (arg1, arg2, arg3, arg4) = stack.pop4(); let arg1 = builder.ins().iconcat(arg1, arg2); let arg2 = builder.ins().iconcat(arg3, arg4); let result = builder.ins().iadd(arg1, arg2); let (res1, res2) = builder.ins().isplit(result); - state.push2(res1, res2); + stack.push2(res1, res2); } Operator::I64Sub128 => { - let (arg1, arg2, arg3, arg4) = state.pop4(); + let (arg1, arg2, arg3, arg4) = stack.pop4(); let arg1 = builder.ins().iconcat(arg1, arg2); let arg2 = builder.ins().iconcat(arg3, arg4); let result = builder.ins().isub(arg1, arg2); let (res1, res2) = builder.ins().isplit(result); - state.push2(res1, res2); + stack.push2(res1, res2); } // catch-all as `Operator` is `#[non_exhaustive]` @@ -3072,15 +3030,15 @@ fn translate_unreachable_operator( validator: &FuncValidator, op: &Operator, builder: &mut FunctionBuilder, - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, environ: &mut FuncEnvironment<'_>, ) -> WasmResult<()> { - debug_assert!(!state.reachable); + debug_assert!(!stack.reachable); match *op { Operator::If { blockty } => { // Push a placeholder control stack entry. The if isn't reachable, // so we don't have any branches anywhere. - state.push_if( + stack.push_if( ir::Block::reserved_value(), ElseData::NoElse { branch_inst: ir::Inst::reserved_value(), @@ -3092,11 +3050,11 @@ fn translate_unreachable_operator( ); } Operator::Loop { blockty: _ } | Operator::Block { blockty: _ } => { - state.push_block(ir::Block::reserved_value(), 0, 0); + stack.push_block(ir::Block::reserved_value(), 0, 0); } Operator::Else => { - let i = state.control_stack.len() - 1; - match state.control_stack[i] { + let i = stack.control_stack.len() - 1; + match stack.control_stack[i] { ControlStackFrame::If { ref else_data, head_is_reachable, @@ -3105,11 +3063,11 @@ fn translate_unreachable_operator( .. } => { debug_assert!(consequent_ends_reachable.is_none()); - *consequent_ends_reachable = Some(state.reachable); + *consequent_ends_reachable = Some(stack.reachable); if head_is_reachable { // We have a branch from the head of the `if` to the `else`. - state.reachable = true; + stack.reachable = true; let else_block = match *else_data { ElseData::NoElse { @@ -3119,8 +3077,8 @@ fn translate_unreachable_operator( let (params, _results) = blocktype_params_results(validator, blocktype)?; let else_block = block_with_params(builder, params, environ)?; - let frame = state.control_stack.last().unwrap(); - frame.truncate_value_stack_to_else_params(&mut state.stack); + let frame = stack.control_stack.last().unwrap(); + frame.truncate_value_stack_to_else_params(&mut stack.stack); // We change the target of the branch instruction. builder.change_jump_destination( @@ -3132,8 +3090,8 @@ fn translate_unreachable_operator( else_block } ElseData::WithElse { else_block } => { - let frame = state.control_stack.last().unwrap(); - frame.truncate_value_stack_to_else_params(&mut state.stack); + let frame = stack.control_stack.last().unwrap(); + frame.truncate_value_stack_to_else_params(&mut stack.stack); else_block } }; @@ -3150,12 +3108,12 @@ fn translate_unreachable_operator( } } Operator::End => { - let stack = &mut state.stack; - let control_stack = &mut state.control_stack; + let value_stack = &mut stack.stack; + let control_stack = &mut stack.control_stack; let frame = control_stack.pop().unwrap(); // Pop unused parameters from stack. - frame.truncate_value_stack_to_original_size(stack); + frame.truncate_value_stack_to_original_size(value_stack); let reachable_anyway = match frame { // If it is a loop we also have to seal the body loop block @@ -3192,8 +3150,8 @@ fn translate_unreachable_operator( // And add the return values of the block but only if the next block is reachable // (which corresponds to testing if the stack depth is 1) - stack.extend_from_slice(builder.block_params(frame.following_code())); - state.reachable = true; + value_stack.extend_from_slice(builder.block_params(frame.following_code())); + stack.reachable = true; } } _ => { @@ -3220,11 +3178,13 @@ fn prepare_addr( memarg: &MemArg, access_size: u8, builder: &mut FunctionBuilder, - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, environ: &mut FuncEnvironment<'_>, ) -> WasmResult> { - let index = state.pop1(); - let heap = state.get_heap(builder.func, memarg.memory, environ)?; + let index = stack.pop1(); + + let memory_index = MemoryIndex::from_u32(memarg.memory); + let heap = environ.get_or_create_heap(builder.func, memory_index); // How exactly the bounds check is performed here and what it's performed // on is a bit tricky. Generally we want to rely on access violations (e.g. @@ -3391,7 +3351,7 @@ fn align_atomic_addr( memarg: &MemArg, loaded_bytes: u8, builder: &mut FunctionBuilder, - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, environ: &mut FuncEnvironment<'_>, ) { // Atomic addresses must all be aligned correctly, and for now we check @@ -3405,8 +3365,8 @@ fn align_atomic_addr( // alignment check itself. This can probably be optimized better and we // should do so in the future as well. if loaded_bytes > 1 { - let addr = state.pop1(); // "peek" via pop then push - state.push1(addr); + let addr = stack.pop1(); // "peek" via pop then push + stack.push1(addr); let effective_addr = if memarg.offset == 0 { addr } else { @@ -3428,11 +3388,11 @@ fn prepare_atomic_addr( memarg: &MemArg, loaded_bytes: u8, builder: &mut FunctionBuilder, - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, environ: &mut FuncEnvironment<'_>, ) -> WasmResult> { - align_atomic_addr(memarg, loaded_bytes, builder, state, environ); - prepare_addr(memarg, loaded_bytes, builder, state, environ) + align_atomic_addr(memarg, loaded_bytes, builder, stack, environ); + prepare_addr(memarg, loaded_bytes, builder, stack, environ) } /// Translate a load instruction. @@ -3443,12 +3403,12 @@ fn translate_load( opcode: ir::Opcode, result_ty: Type, builder: &mut FunctionBuilder, - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, environ: &mut FuncEnvironment<'_>, ) -> WasmResult> { let mem_op_size = mem_op_size(opcode, result_ty); let (flags, wasm_index, base) = - match prepare_addr(memarg, mem_op_size, builder, state, environ)? { + match prepare_addr(memarg, mem_op_size, builder, stack, environ)? { Reachability::Unreachable => return Ok(Reachability::Unreachable), Reachability::Reachable((f, i, b)) => (f, i, b), }; @@ -3458,7 +3418,7 @@ fn translate_load( let (load, dfg) = builder .ins() .Load(opcode, result_ty, flags, Offset32::new(0), base); - state.push1(dfg.first_result(load)); + stack.push1(dfg.first_result(load)); Ok(Reachability::Reachable(())) } @@ -3467,16 +3427,16 @@ fn translate_store( memarg: &MemArg, opcode: ir::Opcode, builder: &mut FunctionBuilder, - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, environ: &mut FuncEnvironment<'_>, ) -> WasmResult<()> { - let val = state.pop1(); + let val = stack.pop1(); let val_ty = builder.func.dfg.value_type(val); let mem_op_size = mem_op_size(opcode, val_ty); let (flags, wasm_index, base) = unwrap_or_return_unreachable_state!( - state, - prepare_addr(memarg, mem_op_size, builder, state, environ)? + stack, + prepare_addr(memarg, mem_op_size, builder, stack, environ)? ); environ.before_store(builder, mem_op_size, wasm_index, memarg.offset); @@ -3497,10 +3457,10 @@ fn mem_op_size(opcode: ir::Opcode, ty: Type) -> u8 { } } -fn translate_icmp(cc: IntCC, builder: &mut FunctionBuilder, state: &mut FuncTranslationState) { - let (arg0, arg1) = state.pop2(); +fn translate_icmp(cc: IntCC, builder: &mut FunctionBuilder, stack: &mut FuncTranslationStacks) { + let (arg0, arg1) = stack.pop2(); let val = builder.ins().icmp(cc, arg0, arg1); - state.push1(builder.ins().uextend(I32, val)); + stack.push1(builder.ins().uextend(I32, val)); } fn translate_atomic_rmw( @@ -3509,10 +3469,10 @@ fn translate_atomic_rmw( op: AtomicRmwOp, memarg: &MemArg, builder: &mut FunctionBuilder, - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, environ: &mut FuncEnvironment<'_>, ) -> WasmResult<()> { - let mut arg2 = state.pop1(); + let mut arg2 = stack.pop1(); let arg2_ty = builder.func.dfg.value_type(arg2); // The operation is performed at type `access_ty`, and the old value is zero-extended @@ -3538,12 +3498,12 @@ fn translate_atomic_rmw( } let (flags, _, addr) = unwrap_or_return_unreachable_state!( - state, + stack, prepare_atomic_addr( memarg, u8::try_from(access_ty.bytes()).unwrap(), builder, - state, + stack, environ, )? ); @@ -3552,7 +3512,7 @@ fn translate_atomic_rmw( if access_ty != widened_ty { res = builder.ins().uextend(widened_ty, res); } - state.push1(res); + stack.push1(res); Ok(()) } @@ -3561,10 +3521,10 @@ fn translate_atomic_cas( access_ty: Type, memarg: &MemArg, builder: &mut FunctionBuilder, - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, environ: &mut FuncEnvironment<'_>, ) -> WasmResult<()> { - let (mut expected, mut replacement) = state.pop2(); + let (mut expected, mut replacement) = stack.pop2(); let expected_ty = builder.func.dfg.value_type(expected); let replacement_ty = builder.func.dfg.value_type(replacement); @@ -3595,12 +3555,12 @@ fn translate_atomic_cas( } let (flags, _, addr) = unwrap_or_return_unreachable_state!( - state, + stack, prepare_atomic_addr( memarg, u8::try_from(access_ty.bytes()).unwrap(), builder, - state, + stack, environ, )? ); @@ -3608,7 +3568,7 @@ fn translate_atomic_cas( if access_ty != widened_ty { res = builder.ins().uextend(widened_ty, res); } - state.push1(res); + stack.push1(res); Ok(()) } @@ -3617,7 +3577,7 @@ fn translate_atomic_load( access_ty: Type, memarg: &MemArg, builder: &mut FunctionBuilder, - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, environ: &mut FuncEnvironment<'_>, ) -> WasmResult<()> { // The load is performed at type `access_ty`, and the loaded value is zero extended @@ -3638,12 +3598,12 @@ fn translate_atomic_load( assert!(w_ty_ok && widened_ty.bytes() >= access_ty.bytes()); let (flags, _, addr) = unwrap_or_return_unreachable_state!( - state, + stack, prepare_atomic_addr( memarg, u8::try_from(access_ty.bytes()).unwrap(), builder, - state, + stack, environ, )? ); @@ -3651,7 +3611,7 @@ fn translate_atomic_load( if access_ty != widened_ty { res = builder.ins().uextend(widened_ty, res); } - state.push1(res); + stack.push1(res); Ok(()) } @@ -3659,10 +3619,10 @@ fn translate_atomic_store( access_ty: Type, memarg: &MemArg, builder: &mut FunctionBuilder, - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, environ: &mut FuncEnvironment<'_>, ) -> WasmResult<()> { - let mut data = state.pop1(); + let mut data = stack.pop1(); let data_ty = builder.func.dfg.value_type(data); // The operation is performed at type `access_ty`, and the data to be stored may first @@ -3687,12 +3647,12 @@ fn translate_atomic_store( } let (flags, _, addr) = unwrap_or_return_unreachable_state!( - state, + stack, prepare_atomic_addr( memarg, u8::try_from(access_ty.bytes()).unwrap(), builder, - state, + stack, environ, )? ); @@ -3704,39 +3664,39 @@ fn translate_vector_icmp( cc: IntCC, needed_type: Type, builder: &mut FunctionBuilder, - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, ) { - let (a, b) = state.pop2(); + let (a, b) = stack.pop2(); let bitcast_a = optionally_bitcast_vector(a, needed_type, builder); let bitcast_b = optionally_bitcast_vector(b, needed_type, builder); - state.push1(builder.ins().icmp(cc, bitcast_a, bitcast_b)) + stack.push1(builder.ins().icmp(cc, bitcast_a, bitcast_b)) } -fn translate_fcmp(cc: FloatCC, builder: &mut FunctionBuilder, state: &mut FuncTranslationState) { - let (arg0, arg1) = state.pop2(); +fn translate_fcmp(cc: FloatCC, builder: &mut FunctionBuilder, stack: &mut FuncTranslationStacks) { + let (arg0, arg1) = stack.pop2(); let val = builder.ins().fcmp(cc, arg0, arg1); - state.push1(builder.ins().uextend(I32, val)); + stack.push1(builder.ins().uextend(I32, val)); } fn translate_vector_fcmp( cc: FloatCC, needed_type: Type, builder: &mut FunctionBuilder, - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, ) { - let (a, b) = state.pop2(); + let (a, b) = stack.pop2(); let bitcast_a = optionally_bitcast_vector(a, needed_type, builder); let bitcast_b = optionally_bitcast_vector(b, needed_type, builder); - state.push1(builder.ins().fcmp(cc, bitcast_a, bitcast_b)) + stack.push1(builder.ins().fcmp(cc, bitcast_a, bitcast_b)) } fn translate_br_if( relative_depth: u32, builder: &mut FunctionBuilder, - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, ) { - let val = state.pop1(); - let (br_destination, inputs) = translate_br_if_args(relative_depth, state); + let val = stack.pop1(); + let (br_destination, inputs) = translate_br_if_args(relative_depth, stack); let next_block = builder.create_block(); canonicalise_brif(builder, val, br_destination, inputs, next_block, &[]); @@ -3746,11 +3706,11 @@ fn translate_br_if( fn translate_br_if_args( relative_depth: u32, - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, ) -> (ir::Block, &mut [ir::Value]) { - let i = state.control_stack.len() - 1 - (relative_depth as usize); + let i = stack.control_stack.len() - 1 - (relative_depth as usize); let (return_count, br_destination) = { - let frame = &mut state.control_stack[i]; + let frame = &mut stack.control_stack[i]; // The values returned by the branch are still available for the reachable // code that comes after it frame.set_branched_to_exit(); @@ -3761,7 +3721,7 @@ fn translate_br_if_args( }; (return_count, frame.br_destination()) }; - let inputs = state.peekn_mut(return_count); + let inputs = stack.peekn_mut(return_count); (br_destination, inputs) } @@ -4073,33 +4033,33 @@ fn canonicalise_brif( /// using v128 (i.e. CLIF's I8x16) we must re-type the values using a bitcast to avoid CLIF /// typing issues. fn pop1_with_bitcast( - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, needed_type: Type, builder: &mut FunctionBuilder, ) -> Value { - optionally_bitcast_vector(state.pop1(), needed_type, builder) + optionally_bitcast_vector(stack.pop1(), needed_type, builder) } /// A helper for popping and bitcasting two values; since SIMD values can lose their type by /// using v128 (i.e. CLIF's I8x16) we must re-type the values using a bitcast to avoid CLIF /// typing issues. fn pop2_with_bitcast( - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, needed_type: Type, builder: &mut FunctionBuilder, ) -> (Value, Value) { - let (a, b) = state.pop2(); + let (a, b) = stack.pop2(); let bitcast_a = optionally_bitcast_vector(a, needed_type, builder); let bitcast_b = optionally_bitcast_vector(b, needed_type, builder); (bitcast_a, bitcast_b) } fn pop3_with_bitcast( - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, needed_type: Type, builder: &mut FunctionBuilder, ) -> (Value, Value, Value) { - let (a, b, c) = state.pop3(); + let (a, b, c) = stack.pop3(); let bitcast_a = optionally_bitcast_vector(a, needed_type, builder); let bitcast_b = optionally_bitcast_vector(b, needed_type, builder); let bitcast_c = optionally_bitcast_vector(c, needed_type, builder); diff --git a/crates/cranelift/src/translate/func_translator.rs b/crates/cranelift/src/translate/func_translator.rs index b792af25cc..fb205e067f 100644 --- a/crates/cranelift/src/translate/func_translator.rs +++ b/crates/cranelift/src/translate/func_translator.rs @@ -7,7 +7,7 @@ use crate::func_environ::FuncEnvironment; use crate::translate::TargetEnvironment; use crate::translate::code_translator::{bitcast_wasm_returns, translate_operator}; -use crate::translate::state::FuncTranslationState; +use crate::translate::stack::FuncTranslationStacks; use crate::translate::translation_utils::get_vmctx_value_label; use cranelift_codegen::entity::EntityRef; use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel}; @@ -23,7 +23,7 @@ use wasmtime_environ::{TypeConvert, WasmResult}; /// functions which will reduce heap allocation traffic. pub struct FuncTranslator { func_ctx: FunctionBuilderContext, - state: FuncTranslationState, + state: FuncTranslationStacks, } impl FuncTranslator { @@ -31,7 +31,7 @@ impl FuncTranslator { pub fn new() -> Self { Self { func_ctx: FunctionBuilderContext::new(), - state: FuncTranslationState::new(), + state: FuncTranslationStacks::new(), } } @@ -234,13 +234,13 @@ fn parse_function_body( validator: &mut FuncValidator, reader: BinaryReader, builder: &mut FunctionBuilder, - state: &mut FuncTranslationState, + stack: &mut FuncTranslationStacks, environ: &mut FuncEnvironment<'_>, ) -> WasmResult<()> { // The control stack is initialized with a single block representing the whole function. - debug_assert_eq!(state.control_stack.len(), 1, "State not initialized"); + debug_assert_eq!(stack.control_stack.len(), 1, "State not initialized"); - environ.before_translate_function(builder, state)?; + environ.before_translate_function(builder, stack)?; let mut reader = OperatorsReader::new(reader); let mut operand_types = vec![]; @@ -253,11 +253,11 @@ fn parse_function_body( let operand_types = validate_op_and_get_operand_types(validator, environ, &mut operand_types, &op, pos)?; - environ.before_translate_operator(&op, operand_types, builder, state)?; - translate_operator(validator, &op, operand_types, builder, state, environ)?; - environ.after_translate_operator(&op, operand_types, builder, state)?; + environ.before_translate_operator(&op, operand_types, builder, stack)?; + translate_operator(validator, &op, operand_types, builder, stack, environ)?; + environ.after_translate_operator(&op, operand_types, builder, stack)?; } - environ.after_translate_function(builder, state)?; + environ.after_translate_function(builder, stack)?; reader.finish()?; // The final `End` operator left us in the exit block where we need to manually add a return @@ -265,17 +265,17 @@ fn parse_function_body( // // If the exit block is unreachable, it may not have the correct arguments, so we would // generate a return instruction that doesn't match the signature. - if state.reachable { + if stack.reachable { if !builder.is_unreachable() { - environ.handle_before_return(&state.stack, builder); - bitcast_wasm_returns(&mut state.stack, builder); - builder.ins().return_(&state.stack); + environ.handle_before_return(&stack.stack, builder); + bitcast_wasm_returns(&mut stack.stack, builder); + builder.ins().return_(&stack.stack); } } // Discard any remaining values on the stack. Either we just returned them, // or the end of the function is unreachable. - state.stack.clear(); + stack.stack.clear(); Ok(()) } diff --git a/crates/cranelift/src/translate/mod.rs b/crates/cranelift/src/translate/mod.rs index 3b9dd8bc6c..7effeb759b 100644 --- a/crates/cranelift/src/translate/mod.rs +++ b/crates/cranelift/src/translate/mod.rs @@ -14,13 +14,13 @@ mod code_translator; mod environ; mod func_translator; mod heap; -mod state; +mod stack; mod table; mod translation_utils; pub use self::environ::{GlobalVariable, StructFieldsVec, TargetEnvironment}; pub use self::func_translator::FuncTranslator; pub use self::heap::{Heap, HeapData}; -pub use self::state::FuncTranslationState; +pub use self::stack::FuncTranslationStacks; pub use self::table::{TableData, TableSize}; pub use self::translation_utils::*; diff --git a/crates/cranelift/src/translate/state.rs b/crates/cranelift/src/translate/stack.rs similarity index 73% rename from crates/cranelift/src/translate/state.rs rename to crates/cranelift/src/translate/stack.rs index 8e320dc64b..a7848b717e 100644 --- a/crates/cranelift/src/translate/state.rs +++ b/crates/cranelift/src/translate/stack.rs @@ -1,15 +1,11 @@ -//! WebAssembly module and function translation state. +//! State of the Wasm stack for translation into CLIF. //! -//! The `FuncTranslationState` struct defined in this module is used to keep track of the WebAssembly -//! value and control stacks during the translation of a single function. +//! The `FuncTranslationStacks` struct defined in this module is used to keep +//! track of the WebAssembly value and control stacks during the translation of +//! a single function. -use crate::func_environ::FuncEnvironment; -use crate::translate::Heap; -use crate::translate::environ::GlobalVariable; use cranelift_codegen::ir::{self, Block, Inst, Value}; -use std::collections::hash_map::{Entry::Occupied, Entry::Vacant, HashMap}; use std::vec::Vec; -use wasmtime_environ::{FuncIndex, GlobalIndex, MemoryIndex, TypeIndex, WasmResult}; /// Information about the presence of an associated `else` for an `if`, or the /// lack thereof. @@ -103,6 +99,7 @@ impl ControlStackFrame { } => num_return_values, } } + pub fn num_param_values(&self) -> usize { match *self { Self::If { @@ -116,6 +113,7 @@ impl ControlStackFrame { } => num_param_values, } } + pub fn following_code(&self) -> Block { match *self { Self::If { destination, .. } @@ -123,12 +121,14 @@ impl ControlStackFrame { | Self::Loop { destination, .. } => destination, } } + pub fn br_destination(&self) -> Block { match *self { Self::If { destination, .. } | Self::Block { destination, .. } => destination, Self::Loop { header, .. } => header, } } + /// Private helper. Use `truncate_value_stack_to_else_params()` or /// `truncate_value_stack_to_original_size()` to restore value-stack state. fn original_stack_size(&self) -> usize { @@ -147,6 +147,7 @@ impl ControlStackFrame { } => original_stack_size, } } + pub fn is_loop(&self) -> bool { match *self { Self::If { .. } | Self::Block { .. } => false, @@ -193,7 +194,7 @@ impl ControlStackFrame { /// before this control-flow frame. pub fn truncate_value_stack_to_original_size(&self, stack: &mut Vec) { // The "If" frame pushes its parameters twice, so they're available to the else block - // (see also `FuncTranslationState::push_if`). + // (see also `FuncTranslationStacks::push_if`). // Yet, the original_stack_size member accounts for them only once, so that the else // block can see the same number of parameters as the consequent block. As a matter of // fact, we need to subtract an extra number of parameter values for if blocks. @@ -210,12 +211,9 @@ impl ControlStackFrame { } } -/// Contains information passed along during a function's translation and that records: -/// -/// - The current value and control stacks. -/// - The depth of the two unreachable control blocks stacks, that are manipulated when translating -/// unreachable code; -pub struct FuncTranslationState { +/// Keeps track of Wasm's operand and control stacks, as well as reachability +/// for each control frame. +pub struct FuncTranslationStacks { /// A stack of values corresponding to the active values in the input wasm function at this /// point. pub(crate) stack: Vec, @@ -224,26 +222,10 @@ pub struct FuncTranslationState { /// Is the current translation state still reachable? This is false when translating operators /// like End, Return, or Unreachable. pub(crate) reachable: bool, - - // Map of global variables that have already been created by `FuncEnvironment::make_global`. - globals: HashMap, - - // Map of heaps that have been created by `FuncEnvironment::make_heap`. - memory_to_heap: HashMap, - - // Map of indirect call signatures that have been created by - // `FuncEnvironment::make_indirect_sig()`. - // Stores both the signature reference and the number of WebAssembly arguments - signatures: HashMap, - - // Imported and local functions that have been created by - // `FuncEnvironment::make_direct_func()`. - // Stores both the function reference and the number of WebAssembly arguments - functions: HashMap, } // Public methods that are exposed to non- API consumers. -impl FuncTranslationState { +impl FuncTranslationStacks { /// True if the current translation state expresses reachable code, false if it is unreachable. #[inline] pub fn reachable(&self) -> bool { @@ -251,17 +233,13 @@ impl FuncTranslationState { } } -impl FuncTranslationState { - /// Construct a new, empty, `FuncTranslationState` +impl FuncTranslationStacks { + /// Construct a new, empty, `FuncTranslationStacks` pub(crate) fn new() -> Self { Self { stack: Vec::new(), control_stack: Vec::new(), reachable: true, - globals: HashMap::new(), - memory_to_heap: HashMap::new(), - signatures: HashMap::new(), - functions: HashMap::new(), } } @@ -269,10 +247,6 @@ impl FuncTranslationState { debug_assert!(self.stack.is_empty()); debug_assert!(self.control_stack.is_empty()); self.reachable = true; - self.globals.clear(); - self.memory_to_heap.clear(); - self.signatures.clear(); - self.functions.clear(); } /// Initialize the state for compiling a function with the given signature. @@ -460,87 +434,3 @@ impl FuncTranslationState { }); } } - -/// Methods for handling entity references. -impl FuncTranslationState { - /// Get the `GlobalVariable` reference that should be used to access the global variable - /// `index`. Create the reference if necessary. - /// Also return the WebAssembly type of the global. - pub(crate) fn get_global( - &mut self, - func: &mut ir::Function, - index: u32, - environ: &mut FuncEnvironment<'_>, - ) -> WasmResult { - let index = GlobalIndex::from_u32(index); - match self.globals.entry(index) { - Occupied(entry) => Ok(*entry.get()), - Vacant(entry) => Ok(*entry.insert(environ.make_global(func, index)?)), - } - } - - /// Get the `Heap` reference that should be used to access linear memory `index`. - /// Create the reference if necessary. - pub(crate) fn get_heap( - &mut self, - func: &mut ir::Function, - index: u32, - environ: &mut FuncEnvironment<'_>, - ) -> WasmResult { - let index = MemoryIndex::from_u32(index); - match self.memory_to_heap.entry(index) { - Occupied(entry) => Ok(*entry.get()), - Vacant(entry) => Ok(*entry.insert(environ.make_heap(func, index)?)), - } - } - - /// Get the `SigRef` reference that should be used to make an indirect call with signature - /// `index`. Also return the number of WebAssembly arguments in the signature. - /// - /// Create the signature if necessary. - pub(crate) fn get_indirect_sig( - &mut self, - func: &mut ir::Function, - index: u32, - environ: &mut FuncEnvironment<'_>, - ) -> WasmResult<(ir::SigRef, usize)> { - let index = TypeIndex::from_u32(index); - match self.signatures.entry(index) { - Occupied(entry) => Ok(*entry.get()), - Vacant(entry) => { - let sig = environ.make_indirect_sig(func, index)?; - Ok(*entry.insert((sig, num_wasm_parameters(environ, &func.dfg.signatures[sig])))) - } - } - } - - /// Get the `FuncRef` reference that should be used to make a direct call to function - /// `index`. Also return the number of WebAssembly arguments in the signature. - /// - /// Create the function reference if necessary. - pub(crate) fn get_direct_func( - &mut self, - func: &mut ir::Function, - index: u32, - environ: &mut FuncEnvironment<'_>, - ) -> WasmResult<(ir::FuncRef, usize)> { - let index = FuncIndex::from_u32(index); - match self.functions.entry(index) { - Occupied(entry) => Ok(*entry.get()), - Vacant(entry) => { - let fref = environ.make_direct_func(func, index)?; - let sig = func.dfg.ext_funcs[fref].signature; - Ok(*entry.insert(( - fref, - num_wasm_parameters(environ, &func.dfg.signatures[sig]), - ))) - } - } - } -} - -fn num_wasm_parameters(environ: &FuncEnvironment<'_>, signature: &ir::Signature) -> usize { - (0..signature.params.len()) - .filter(|index| environ.is_wasm_parameter(signature, *index)) - .count() -} diff --git a/crates/environ/src/compile/mod.rs b/crates/environ/src/compile/mod.rs index 45d0759e28..c4059cd96b 100644 --- a/crates/environ/src/compile/mod.rs +++ b/crates/environ/src/compile/mod.rs @@ -180,7 +180,7 @@ pub enum SettingKind { pub struct CompiledFunctionBody { /// The code. This is whatever type the `Compiler` implementation wants it /// to be, we just shepherd it around. - pub code: Box, + pub code: Box, /// Whether the compiled function needs a GC heap to run; that is, whether /// it reads a struct field, allocates, an array, or etc... pub needs_gc_heap: bool, @@ -188,7 +188,69 @@ pub struct CompiledFunctionBody { /// An implementation of a compiler which can compile WebAssembly functions to /// machine code and perform other miscellaneous tasks needed by the JIT runtime. +/// +/// The diagram below depicts typical usage of this trait: +/// +/// ```ignore +/// +------+ +/// | Wasm | +/// +------+ +/// | +/// | +/// Compiler::compile_function() +/// | +/// | +/// V +/// +----------------------+ +/// | CompiledFunctionBody | +/// +----------------------+ +/// | | +/// | | +/// | When +/// | Compiler::inlining_compiler() +/// | is some +/// | | +/// When | +/// Compiler::inlining_compiler() |-----------------. +/// is none | | +/// | | | +/// | Optionally call | +/// | InliningCompiler::inline() | +/// | | | +/// | | | +/// | |-----------------' +/// | | +/// | | +/// | V +/// | InliningCompiler::finish_compiling() +/// | | +/// | | +/// |------------------' +/// | +/// | +/// Compiler::append_code() +/// | +/// | +/// V +/// +--------+ +/// | Object | +/// +--------+ +/// ``` pub trait Compiler: Send + Sync { + /// Get this compiler's inliner. + /// + /// Consumers of this trait **must** check for when when this method returns + /// `Some(_)`, and **must** call `InliningCompiler::finish_compiling` on all + /// `CompiledFunctionBody`s produced by this compiler in that case before + /// passing the the compiled functions to `Compiler::append_code`, even if + /// the consumer does not actually intend to do any inlining. This allows + /// implementations of the trait to only translate to an internal + /// representation in `Compiler::compile_*` methods so that they can then + /// perform inlining afterwards if the consumer desires, and then finally + /// proceed with compilng that internal representation to native code in + /// `InliningCompiler::finish_compiling`. + fn inlining_compiler(&self) -> Option<&dyn InliningCompiler>; + /// Compiles the function `index` within `translation`. /// /// The body of the function is available in `data` and configuration @@ -279,7 +341,7 @@ pub trait Compiler: Send + Sync { fn append_code( &self, obj: &mut Object<'static>, - funcs: &[(String, Box)], + funcs: &[(String, Box)], resolve_reloc: &dyn Fn(usize, RelocationTarget) -> usize, ) -> Result>; @@ -390,7 +452,7 @@ pub trait Compiler: Send + Sync { get_func: &'a dyn Fn( StaticModuleIndex, DefinedFuncIndex, - ) -> (SymbolId, &'a (dyn Any + Send)), + ) -> (SymbolId, &'a (dyn Any + Send + Sync)), dwarf_package_bytes: Option<&'a [u8]>, tunables: &'a Tunables, ) -> Result<()>; @@ -403,3 +465,36 @@ pub trait Compiler: Send + Sync { None } } + +/// An inlining compiler. +pub trait InliningCompiler: Sync + Send { + /// Enumerate the function calls that the given `func` makes. + fn calls(&self, func: &CompiledFunctionBody, calls: &mut IndexSet) -> Result<()>; + + /// Get the abstract size of the given function, for the purposes of + /// inlining heuristics. + fn size(&self, func: &CompiledFunctionBody) -> u32; + + /// Process this function for inlining. + /// + /// Implementations should call `get_callee` for each of their direct + /// function call sites and if `get_callee` returns `Some(_)`, they should + /// inline the given function body into that call site. + fn inline<'a>( + &self, + func: &mut CompiledFunctionBody, + get_callee: &'a mut dyn FnMut(FuncIndex) -> Option<&'a CompiledFunctionBody>, + ) -> Result<()>; + + /// Finish compiling the given function. + /// + /// This method **must** be called before passing the + /// `CompiledFunctionBody`'s contents to `Compiler::append_code`, even if no + /// inlining was performed. + fn finish_compiling( + &self, + func: &mut CompiledFunctionBody, + input: Option>, + symbol: &str, + ) -> Result<()>; +} diff --git a/crates/environ/src/component.rs b/crates/environ/src/component.rs index 3e0a3692ab..10589c2eff 100644 --- a/crates/environ/src/component.rs +++ b/crates/environ/src/component.rs @@ -100,15 +100,15 @@ macro_rules! foreach_builtin_component_function { #[cfg(feature = "component-model-async")] backpressure_set(vmctx: vmctx, caller_instance: u32, enabled: u32) -> bool; #[cfg(feature = "component-model-async")] - task_return(vmctx: vmctx, ty: u32, memory: ptr_u8, string_encoding: u8, storage: ptr_u8, storage_len: size) -> bool; + task_return(vmctx: vmctx, ty: u32, options: u32, storage: ptr_u8, storage_len: size) -> bool; #[cfg(feature = "component-model-async")] task_cancel(vmctx: vmctx, caller_instance: u32) -> bool; #[cfg(feature = "component-model-async")] waitable_set_new(vmctx: vmctx, caller_instance: u32) -> u64; #[cfg(feature = "component-model-async")] - waitable_set_wait(vmctx: vmctx, caller_instance: u32, async_: u8, memory: ptr_u8, set: u32, payload: u32) -> u64; + waitable_set_wait(vmctx: vmctx, options: u32, set: u32, payload: u32) -> u64; #[cfg(feature = "component-model-async")] - waitable_set_poll(vmctx: vmctx, caller_instance: u32, async_: u8, memory: ptr_u8, set: u32, payload: u32) -> u64; + waitable_set_poll(vmctx: vmctx, options: u32, set: u32, payload: u32) -> u64; #[cfg(feature = "component-model-async")] waitable_set_drop(vmctx: vmctx, caller_instance: u32, set: u32) -> bool; #[cfg(feature = "component-model-async")] @@ -134,15 +134,15 @@ macro_rules! foreach_builtin_component_function { storage_len: size ) -> bool; #[cfg(feature = "component-model-async")] - sync_start(vmctx: vmctx, callback: ptr_u8, callee: ptr_u8, param_count: u32, storage: ptr_u8, storage_len: size) -> bool; + sync_start(vmctx: vmctx, callback: ptr_u8, storage: ptr_u8, storage_len: size, callee: ptr_u8, param_count: u32) -> bool; #[cfg(feature = "component-model-async")] async_start(vmctx: vmctx, callback: ptr_u8, post_return: ptr_u8, callee: ptr_u8, param_count: u32, result_count: u32, flags: u32) -> u64; #[cfg(feature = "component-model-async")] future_new(vmctx: vmctx, ty: u32) -> u64; #[cfg(feature = "component-model-async")] - future_write(vmctx: vmctx, memory: ptr_u8, realloc: ptr_u8, string_encoding: u8, async_: u8, ty: u32, future: u32, address: u32) -> u64; + future_write(vmctx: vmctx, ty: u32, options: u32, future: u32, address: u32) -> u64; #[cfg(feature = "component-model-async")] - future_read(vmctx: vmctx, memory: ptr_u8, realloc: ptr_u8, string_encoding: u8, async_: u8, ty: u32, future: u32, address: u32) -> u64; + future_read(vmctx: vmctx, ty: u32, options: u32, future: u32, address: u32) -> u64; #[cfg(feature = "component-model-async")] future_cancel_write(vmctx: vmctx, ty: u32, async_: u8, writer: u32) -> u64; #[cfg(feature = "component-model-async")] @@ -154,9 +154,9 @@ macro_rules! foreach_builtin_component_function { #[cfg(feature = "component-model-async")] stream_new(vmctx: vmctx, ty: u32) -> u64; #[cfg(feature = "component-model-async")] - stream_write(vmctx: vmctx, memory: ptr_u8, realloc: ptr_u8, string_encoding: u8, async_: u8, ty: u32, stream: u32, address: u32, count: u32) -> u64; + stream_write(vmctx: vmctx, ty: u32, options: u32, stream: u32, address: u32, count: u32) -> u64; #[cfg(feature = "component-model-async")] - stream_read(vmctx: vmctx, memory: ptr_u8, realloc: ptr_u8, string_encoding: u8, async_: u8, ty: u32, stream: u32, address: u32, count: u32) -> u64; + stream_read(vmctx: vmctx, ty: u32, options: u32, stream: u32, address: u32, count: u32) -> u64; #[cfg(feature = "component-model-async")] stream_cancel_write(vmctx: vmctx, ty: u32, async_: u8, writer: u32) -> u64; #[cfg(feature = "component-model-async")] @@ -166,13 +166,13 @@ macro_rules! foreach_builtin_component_function { #[cfg(feature = "component-model-async")] stream_drop_readable(vmctx: vmctx, ty: u32, reader: u32) -> bool; #[cfg(feature = "component-model-async")] - flat_stream_write(vmctx: vmctx, memory: ptr_u8, realloc: ptr_u8, async_: u8, ty: u32, payload_size: u32, payload_align: u32, stream: u32, address: u32, count: u32) -> u64; + flat_stream_write(vmctx: vmctx, ty: u32, options:u32, payload_size: u32, payload_align: u32, stream: u32, address: u32, count: u32) -> u64; #[cfg(feature = "component-model-async")] - flat_stream_read(vmctx: vmctx, memory: ptr_u8, realloc: ptr_u8, async_: u8, ty: u32, payload_size: u32, payload_align: u32, stream: u32, address: u32, count: u32) -> u64; + flat_stream_read(vmctx: vmctx, ty: u32, options: u32, payload_size: u32, payload_align: u32, stream: u32, address: u32, count: u32) -> u64; #[cfg(feature = "component-model-async")] - error_context_new(vmctx: vmctx, memory: ptr_u8, realloc: ptr_u8, string_encoding: u8, ty: u32, debug_msg_address: u32, debug_msg_len: u32) -> u64; + error_context_new(vmctx: vmctx, ty: u32, options: u32, debug_msg_address: u32, debug_msg_len: u32) -> u64; #[cfg(feature = "component-model-async")] - error_context_debug_message(vmctx: vmctx, memory: ptr_u8, realloc: ptr_u8, string_encoding: u8, ty: u32, err_ctx_handle: u32, debug_msg_address: u32) -> bool; + error_context_debug_message(vmctx: vmctx, ty: u32, options: u32, err_ctx_handle: u32, debug_msg_address: u32) -> bool; #[cfg(feature = "component-model-async")] error_context_drop(vmctx: vmctx, ty: u32, err_ctx_handle: u32) -> bool; #[cfg(feature = "component-model-async")] @@ -186,7 +186,7 @@ macro_rules! foreach_builtin_component_function { #[cfg(feature = "component-model-async")] context_set(vmctx: vmctx, slot: u32, val: u32) -> bool; - trap(vmctx: vmctx, code: u8); + trap(vmctx: vmctx, code: u8) -> bool; utf8_to_utf8(vmctx: vmctx, src: ptr_u8, len: size, dst: ptr_u8) -> bool; utf16_to_utf16(vmctx: vmctx, src: ptr_u16, len: size, dst: ptr_u16) -> bool; diff --git a/crates/environ/src/component/dfg.rs b/crates/environ/src/component/dfg.rs index 10bf599db5..cbdb7be53c 100644 --- a/crates/environ/src/component/dfg.rs +++ b/crates/environ/src/component/dfg.rs @@ -138,6 +138,10 @@ pub struct ComponentDfg { /// of this component by idnicating what order operations should be /// performed during instantiation. pub side_effects: Vec, + + /// Interned map of id-to-`CanonicalOptions`, or all sets-of-options used by + /// this component. + pub options: Intern, } /// Possible side effects that are possible with instantiating this component. @@ -211,6 +215,7 @@ id! { pub struct AdapterId(u32); pub struct PostReturnId(u32); pub struct AdapterModuleId(u32); + pub struct OptionsId(u32); } /// Same as `info::InstantiateModule` @@ -229,7 +234,7 @@ pub enum Export { LiftedFunction { ty: TypeFuncIndex, func: CoreDef, - options: CanonicalOptions, + options: OptionsId, }, ModuleStatic { ty: ComponentCoreModuleTypeId, @@ -300,7 +305,7 @@ impl CoreExport { pub enum Trampoline { LowerImport { import: RuntimeImportIndex, - options: CanonicalOptions, + options: OptionsId, lower_ty: TypeFuncIndex, }, Transcoder { @@ -319,7 +324,7 @@ pub enum Trampoline { }, TaskReturn { results: TypeTupleIndex, - options: CanonicalOptions, + options: OptionsId, }, TaskCancel { instance: RuntimeComponentInstanceIndex, @@ -328,14 +333,10 @@ pub enum Trampoline { instance: RuntimeComponentInstanceIndex, }, WaitableSetWait { - instance: RuntimeComponentInstanceIndex, - async_: bool, - memory: MemoryId, + options: OptionsId, }, WaitableSetPoll { - instance: RuntimeComponentInstanceIndex, - async_: bool, - memory: MemoryId, + options: OptionsId, }, WaitableSetDrop { instance: RuntimeComponentInstanceIndex, @@ -358,11 +359,11 @@ pub enum Trampoline { }, StreamRead { ty: TypeStreamTableIndex, - options: CanonicalOptions, + options: OptionsId, }, StreamWrite { ty: TypeStreamTableIndex, - options: CanonicalOptions, + options: OptionsId, }, StreamCancelRead { ty: TypeStreamTableIndex, @@ -383,11 +384,11 @@ pub enum Trampoline { }, FutureRead { ty: TypeFutureTableIndex, - options: CanonicalOptions, + options: OptionsId, }, FutureWrite { ty: TypeFutureTableIndex, - options: CanonicalOptions, + options: OptionsId, }, FutureCancelRead { ty: TypeFutureTableIndex, @@ -405,11 +406,11 @@ pub enum Trampoline { }, ErrorContextNew { ty: TypeComponentLocalErrorContextTableIndex, - options: CanonicalOptions, + options: OptionsId, }, ErrorContextDebugMessage { ty: TypeComponentLocalErrorContextTableIndex, - options: CanonicalOptions, + options: OptionsId, }, ErrorContextDrop { ty: TypeComponentLocalErrorContextTableIndex, @@ -554,6 +555,8 @@ impl ComponentDfg { trampolines: Default::default(), trampoline_defs: Default::default(), trampoline_map: Default::default(), + options: Default::default(), + options_map: Default::default(), }; // Handle all side effects of this component in the order that they're @@ -585,6 +588,7 @@ impl ComponentDfg { initializers: linearize.initializers, trampolines: linearize.trampolines, num_lowerings: linearize.num_lowerings, + options: linearize.options, num_runtime_memories: linearize.runtime_memories.len() as u32, num_runtime_tables: linearize.runtime_tables.len() as u32, @@ -621,6 +625,7 @@ struct LinearizeDfg<'a> { initializers: Vec, trampolines: PrimaryMap, trampoline_defs: PrimaryMap, + options: PrimaryMap, trampoline_map: HashMap, runtime_memories: HashMap, runtime_tables: HashMap, @@ -628,6 +633,7 @@ struct LinearizeDfg<'a> { runtime_callbacks: HashMap, runtime_post_return: HashMap, runtime_instances: HashMap, + options_map: HashMap, num_lowerings: u32, } @@ -699,7 +705,7 @@ impl LinearizeDfg<'_> { let item = match export { Export::LiftedFunction { ty, func, options } => { let func = self.core_def(func); - let options = self.options(options); + let options = self.options(*options); info::Export::LiftedFunction { ty: *ty, func, @@ -731,7 +737,16 @@ impl LinearizeDfg<'_> { Ok(items.push(item)) } - fn options(&mut self, options: &CanonicalOptions) -> info::CanonicalOptions { + fn options(&mut self, options: OptionsId) -> OptionsIndex { + self.intern_no_init( + options, + |me| &mut me.options_map, + |me, options| me.convert_options(options), + ) + } + + fn convert_options(&mut self, options: OptionsId) -> OptionsIndex { + let options = &self.dfg.options[options]; let data_model = match options.data_model { CanonicalOptionsDataModel::Gc {} => info::CanonicalOptionsDataModel::Gc {}, CanonicalOptionsDataModel::LinearMemory { memory, realloc } => { @@ -743,7 +758,7 @@ impl LinearizeDfg<'_> { }; let callback = options.callback.map(|mem| self.runtime_callback(mem)); let post_return = options.post_return.map(|mem| self.runtime_post_return(mem)); - info::CanonicalOptions { + let options = info::CanonicalOptions { instance: options.instance, string_encoding: options.string_encoding, callback, @@ -751,7 +766,8 @@ impl LinearizeDfg<'_> { async_: options.async_, core_type: options.core_type, data_model, - } + }; + self.options.push(options) } fn runtime_memory(&mut self, mem: MemoryId) -> RuntimeMemoryIndex { @@ -818,7 +834,7 @@ impl LinearizeDfg<'_> { }); info::Trampoline::LowerImport { index, - options: self.options(options), + options: self.options(*options), lower_ty: *lower_ty, } } @@ -844,7 +860,7 @@ impl LinearizeDfg<'_> { }, Trampoline::TaskReturn { results, options } => info::Trampoline::TaskReturn { results: *results, - options: self.options(options), + options: self.options(*options), }, Trampoline::TaskCancel { instance } => info::Trampoline::TaskCancel { instance: *instance, @@ -852,23 +868,11 @@ impl LinearizeDfg<'_> { Trampoline::WaitableSetNew { instance } => info::Trampoline::WaitableSetNew { instance: *instance, }, - Trampoline::WaitableSetWait { - instance, - async_, - memory, - } => info::Trampoline::WaitableSetWait { - instance: *instance, - async_: *async_, - memory: self.runtime_memory(*memory), + Trampoline::WaitableSetWait { options } => info::Trampoline::WaitableSetWait { + options: self.options(*options), }, - Trampoline::WaitableSetPoll { - instance, - async_, - memory, - } => info::Trampoline::WaitableSetPoll { - instance: *instance, - async_: *async_, - memory: self.runtime_memory(*memory), + Trampoline::WaitableSetPoll { options } => info::Trampoline::WaitableSetPoll { + options: self.options(*options), }, Trampoline::WaitableSetDrop { instance } => info::Trampoline::WaitableSetDrop { instance: *instance, @@ -887,11 +891,11 @@ impl LinearizeDfg<'_> { Trampoline::StreamNew { ty } => info::Trampoline::StreamNew { ty: *ty }, Trampoline::StreamRead { ty, options } => info::Trampoline::StreamRead { ty: *ty, - options: self.options(options), + options: self.options(*options), }, Trampoline::StreamWrite { ty, options } => info::Trampoline::StreamWrite { ty: *ty, - options: self.options(options), + options: self.options(*options), }, Trampoline::StreamCancelRead { ty, async_ } => info::Trampoline::StreamCancelRead { ty: *ty, @@ -910,11 +914,11 @@ impl LinearizeDfg<'_> { Trampoline::FutureNew { ty } => info::Trampoline::FutureNew { ty: *ty }, Trampoline::FutureRead { ty, options } => info::Trampoline::FutureRead { ty: *ty, - options: self.options(options), + options: self.options(*options), }, Trampoline::FutureWrite { ty, options } => info::Trampoline::FutureWrite { ty: *ty, - options: self.options(options), + options: self.options(*options), }, Trampoline::FutureCancelRead { ty, async_ } => info::Trampoline::FutureCancelRead { ty: *ty, @@ -932,12 +936,12 @@ impl LinearizeDfg<'_> { } Trampoline::ErrorContextNew { ty, options } => info::Trampoline::ErrorContextNew { ty: *ty, - options: self.options(options), + options: self.options(*options), }, Trampoline::ErrorContextDebugMessage { ty, options } => { info::Trampoline::ErrorContextDebugMessage { ty: *ty, - options: self.options(options), + options: self.options(*options), } } Trampoline::ErrorContextDrop { ty } => info::Trampoline::ErrorContextDrop { ty: *ty }, @@ -1034,6 +1038,35 @@ impl LinearizeDfg<'_> { generate: impl FnOnce(&mut Self, K) -> T, init: impl FnOnce(V, T) -> GlobalInitializer, ) -> V + where + K: Hash + Eq + Copy, + V: EntityRef, + { + self.intern_(key, map, generate, |me, key, val| { + me.initializers.push(init(key, val)); + }) + } + + fn intern_no_init( + &mut self, + key: K, + map: impl Fn(&mut Self) -> &mut HashMap, + generate: impl FnOnce(&mut Self, K) -> T, + ) -> V + where + K: Hash + Eq + Copy, + V: EntityRef, + { + self.intern_(key, map, generate, |_me, _key, _val| {}) + } + + fn intern_( + &mut self, + key: K, + map: impl Fn(&mut Self) -> &mut HashMap, + generate: impl FnOnce(&mut Self, K) -> T, + init: impl FnOnce(&mut Self, V, T), + ) -> V where K: Hash + Eq + Copy, V: EntityRef, @@ -1043,7 +1076,7 @@ impl LinearizeDfg<'_> { } let tmp = generate(self, key); let index = V::new(map(self).len()); - self.initializers.push(init(index, tmp)); + init(self, index, tmp); let prev = map(self).insert(key, index); assert!(prev.is_none()); index diff --git a/crates/environ/src/component/info.rs b/crates/environ/src/component/info.rs index 91784b7045..b939b12b46 100644 --- a/crates/environ/src/component/info.rs +++ b/crates/environ/src/component/info.rs @@ -194,6 +194,10 @@ pub struct Component { /// This is used to determine which set of instance flags are inspected when /// testing reentrance. pub defined_resource_instances: PrimaryMap, + + /// All canonical options used by this component. Stored as a table here + /// from index-to-options so the options can be consulted at runtime. + pub options: PrimaryMap, } impl Component { @@ -451,7 +455,7 @@ pub enum Export { /// Which core WebAssembly export is being lifted. func: CoreDef, /// Any options, if present, associated with this lifting. - options: CanonicalOptions, + options: OptionsIndex, }, /// A module defined within this component is exported. ModuleStatic { @@ -673,7 +677,7 @@ pub enum Trampoline { /// The canonical ABI options used when lowering this function specified /// in the original component. - options: CanonicalOptions, + options: OptionsIndex, }, /// Information about a string transcoding function required by an adapter @@ -734,7 +738,7 @@ pub enum Trampoline { results: TypeTupleIndex, /// The canonical ABI options specified for this intrinsic. - options: CanonicalOptions, + options: OptionsIndex, }, /// A `task.cancel` intrinsic, which acknowledges a `CANCELLED` event @@ -755,24 +759,16 @@ pub enum Trampoline { /// outstanding async task/stream/future to make progress, returning the /// first such event. WaitableSetWait { - /// The specific component instance which is calling the intrinsic. - instance: RuntimeComponentInstanceIndex, - /// If `true`, indicates the caller instance maybe reentered. - async_: bool, - /// Memory to use when storing the event. - memory: RuntimeMemoryIndex, + /// Configuration options for this intrinsic call. + options: OptionsIndex, }, /// A `waitable-set.poll` intrinsic, which checks whether any outstanding /// async task/stream/future has made progress. Unlike `task.wait`, this /// does not block and may return nothing if no such event has occurred. WaitableSetPoll { - /// The specific component instance which is calling the intrinsic. - instance: RuntimeComponentInstanceIndex, - /// If `true`, indicates the caller instance maybe reentered. - async_: bool, - /// Memory to use when storing the event. - memory: RuntimeMemoryIndex, + /// Configuration options for this intrinsic call. + options: OptionsIndex, }, /// A `waitable-set.drop` intrinsic. @@ -823,7 +819,7 @@ pub enum Trampoline { /// Any options (e.g. string encoding) to use when storing values to /// memory. - options: CanonicalOptions, + options: OptionsIndex, }, /// A `stream.write` intrinsic to write to a `stream` of the specified type. @@ -833,7 +829,7 @@ pub enum Trampoline { /// Any options (e.g. string encoding) to use when storing values to /// memory. - options: CanonicalOptions, + options: OptionsIndex, }, /// A `stream.cancel-read` intrinsic to cancel an in-progress read from a @@ -884,7 +880,7 @@ pub enum Trampoline { /// Any options (e.g. string encoding) to use when storing values to /// memory. - options: CanonicalOptions, + options: OptionsIndex, }, /// A `future.write` intrinsic to write to a `future` of the specified type. @@ -894,7 +890,7 @@ pub enum Trampoline { /// Any options (e.g. string encoding) to use when storing values to /// memory. - options: CanonicalOptions, + options: OptionsIndex, }, /// A `future.cancel-read` intrinsic to cancel an in-progress read from a @@ -937,7 +933,7 @@ pub enum Trampoline { /// The table index for the `error-context` type in the caller instance. ty: TypeComponentLocalErrorContextTableIndex, /// String encoding, memory, etc. to use when loading debug message. - options: CanonicalOptions, + options: OptionsIndex, }, /// A `error-context.debug-message` intrinsic to get the debug message for a @@ -949,7 +945,7 @@ pub enum Trampoline { /// The table index for the `error-context` type in the caller instance. ty: TypeComponentLocalErrorContextTableIndex, /// String encoding, memory, etc. to use when storing debug message. - options: CanonicalOptions, + options: OptionsIndex, }, /// A `error-context.drop` intrinsic to drop a specified `error-context`. diff --git a/crates/environ/src/component/translate.rs b/crates/environ/src/component/translate.rs index 290d57a965..ecdb0e421f 100644 --- a/crates/environ/src/component/translate.rs +++ b/crates/environ/src/component/translate.rs @@ -204,14 +204,10 @@ enum LocalInitializer<'data> { func: ModuleInternedTypeIndex, }, WaitableSetWait { - func: ModuleInternedTypeIndex, - async_: bool, - memory: MemoryIndex, + options: LocalCanonicalOptions, }, WaitableSetPoll { - func: ModuleInternedTypeIndex, - async_: bool, - memory: MemoryIndex, + options: LocalCanonicalOptions, }, WaitableSetDrop { func: ModuleInternedTypeIndex, @@ -844,21 +840,37 @@ impl<'a, 'data> Translator<'a, 'data> { LocalInitializer::WaitableSetNew { func } } wasmparser::CanonicalFunction::WaitableSetWait { async_, memory } => { - let func = self.core_func_signature(core_func_index)?; + let core_type = self.core_func_signature(core_func_index)?; core_func_index += 1; LocalInitializer::WaitableSetWait { - func, - async_, - memory: MemoryIndex::from_u32(memory), + options: LocalCanonicalOptions { + core_type, + async_, + data_model: LocalDataModel::LinearMemory { + memory: Some(MemoryIndex::from_u32(memory)), + realloc: None, + }, + post_return: None, + callback: None, + string_encoding: StringEncoding::Utf8, + }, } } wasmparser::CanonicalFunction::WaitableSetPoll { async_, memory } => { - let func = self.core_func_signature(core_func_index)?; + let core_type = self.core_func_signature(core_func_index)?; core_func_index += 1; LocalInitializer::WaitableSetPoll { - func, - async_, - memory: MemoryIndex::from_u32(memory), + options: LocalCanonicalOptions { + core_type, + async_, + data_model: LocalDataModel::LinearMemory { + memory: Some(MemoryIndex::from_u32(memory)), + realloc: None, + }, + post_return: None, + callback: None, + string_encoding: StringEncoding::Utf8, + }, } } wasmparser::CanonicalFunction::WaitableSetDrop => { diff --git a/crates/environ/src/component/translate/inline.rs b/crates/environ/src/component/translate/inline.rs index 85d45caab6..95de5bcc30 100644 --- a/crates/environ/src/component/translate/inline.rs +++ b/crates/environ/src/component/translate/inline.rs @@ -714,39 +714,25 @@ impl<'a> Inliner<'a> { )); frame.funcs.push((*func, dfg::CoreDef::Trampoline(index))); } - WaitableSetWait { - func, - async_, - memory, - } => { - let (memory, _) = self.memory(frame, types, *memory); - let memory = self.result.memories.push(memory); - let index = self.result.trampolines.push(( - *func, - dfg::Trampoline::WaitableSetWait { - instance: frame.instance, - async_: *async_, - memory, - }, - )); - frame.funcs.push((*func, dfg::CoreDef::Trampoline(index))); + WaitableSetWait { options } => { + let func = options.core_type; + let options = self.adapter_options(frame, types, options); + let options = self.canonical_options(options); + let index = self + .result + .trampolines + .push((func, dfg::Trampoline::WaitableSetWait { options })); + frame.funcs.push((func, dfg::CoreDef::Trampoline(index))); } - WaitableSetPoll { - func, - async_, - memory, - } => { - let (memory, _) = self.memory(frame, types, *memory); - let memory = self.result.memories.push(memory); - let index = self.result.trampolines.push(( - *func, - dfg::Trampoline::WaitableSetPoll { - instance: frame.instance, - async_: *async_, - memory, - }, - )); - frame.funcs.push((*func, dfg::CoreDef::Trampoline(index))); + WaitableSetPoll { options } => { + let func = options.core_type; + let options = self.adapter_options(frame, types, options); + let options = self.canonical_options(options); + let index = self + .result + .trampolines + .push((func, dfg::Trampoline::WaitableSetPoll { options })); + frame.funcs.push((func, dfg::CoreDef::Trampoline(index))); } WaitableSetDrop { func } => { let index = self.result.trampolines.push(( @@ -1433,7 +1419,7 @@ impl<'a> Inliner<'a> { /// memories/functions are inserted into the global initializer list for /// use at runtime. This is only used for lowered host functions and lifted /// functions exported to the host. - fn canonical_options(&mut self, options: AdapterOptions) -> dfg::CanonicalOptions { + fn canonical_options(&mut self, options: AdapterOptions) -> dfg::OptionsId { let data_model = match options.data_model { DataModel::Gc {} => dfg::CanonicalOptionsDataModel::Gc {}, DataModel::LinearMemory { @@ -1449,7 +1435,7 @@ impl<'a> Inliner<'a> { let post_return = options .post_return .map(|def| self.result.post_returns.push(def)); - dfg::CanonicalOptions { + self.result.options.push(dfg::CanonicalOptions { instance: options.instance, string_encoding: options.string_encoding, callback, @@ -1457,7 +1443,7 @@ impl<'a> Inliner<'a> { async_: options.async_, core_type: options.core_type, data_model, - } + }) } fn record_export( diff --git a/crates/environ/src/component/types.rs b/crates/environ/src/component/types.rs index c6ea97ae4c..fee236413d 100644 --- a/crates/environ/src/component/types.rs +++ b/crates/environ/src/component/types.rs @@ -240,6 +240,9 @@ indices! { /// An index into `Component::export_items` at the end of compilation. pub struct ExportIndex(u32); + + /// An index into `Component::options` at the end of compilation. + pub struct OptionsIndex(u32); } // Reexport for convenience some core-wasm indices which are also used in the diff --git a/crates/environ/src/tunables.rs b/crates/environ/src/tunables.rs index 4ace171bd9..55b2bdad8f 100644 --- a/crates/environ/src/tunables.rs +++ b/crates/environ/src/tunables.rs @@ -1,6 +1,6 @@ use crate::{IndexType, Limits, Memory, TripleExt}; -use anyhow::{Result, anyhow, bail}; -use core::fmt; +use anyhow::{Error, Result, anyhow, bail}; +use core::{fmt, str::FromStr}; use serde_derive::{Deserialize, Serialize}; use target_lexicon::{PointerWidth, Triple}; @@ -121,6 +121,21 @@ define_tunables! { /// Whether CoW images might be used to initialize linear memories. pub memory_init_cow: bool, + + /// Whether to enable inlining in Wasmtime's compilation orchestration + /// or not. + pub inlining: bool, + + /// Whether to inline calls within the same core Wasm module or not. + pub inlining_intra_module: IntraModuleInlining, + + /// The size of "small callees" that can be inlined regardless of the + /// caller's size. + pub inlining_small_callee_size: u32, + + /// The general size threshold for the sum of the caller's and callee's + /// sizes, past which we will generally not inline calls anymore. + pub inlining_sum_size_threshold: u32, } pub struct ConfigTunables { @@ -191,6 +206,10 @@ impl Tunables { winch_callable: false, signals_based_traps: false, memory_init_cow: true, + inlining: false, + inlining_intra_module: IntraModuleInlining::WhenUsingGc, + inlining_small_callee_size: 50, + inlining_sum_size_threshold: 2000, } } @@ -267,3 +286,28 @@ impl fmt::Display for Collector { } } } + +/// Whether to inline function calls within the same module. +#[derive(Clone, Copy, Hash, Serialize, Deserialize, Debug, PartialEq, Eq)] +#[expect(missing_docs, reason = "self-describing variants")] +pub enum IntraModuleInlining { + Yes, + No, + WhenUsingGc, +} + +impl FromStr for IntraModuleInlining { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s { + "y" | "yes" | "true" => Ok(Self::Yes), + "n" | "no" | "false" => Ok(Self::No), + "gc" => Ok(Self::WhenUsingGc), + _ => bail!( + "invalid intra-module inlining option string: `{s}`, \ + only yes,no,gc accepted" + ), + } + } +} diff --git a/crates/fuzzing/Cargo.toml b/crates/fuzzing/Cargo.toml index 7fea9a4d4b..3ae30794a7 100644 --- a/crates/fuzzing/Cargo.toml +++ b/crates/fuzzing/Cargo.toml @@ -36,6 +36,7 @@ wasmi = { version = "0.43.1", default-features = false, features = ["std", "simd futures = { workspace = true } wasmtime-test-util = { workspace = true, features = ['wast', 'component-fuzz', 'component'] } serde_json = { workspace = true } +serde = { workspace = true } [dependencies.wasmtime-cli-flags] workspace = true diff --git a/crates/fuzzing/src/generators/config.rs b/crates/fuzzing/src/generators/config.rs index a3bd5428d0..9586ba88c2 100644 --- a/crates/fuzzing/src/generators/config.rs +++ b/crates/fuzzing/src/generators/config.rs @@ -266,6 +266,7 @@ impl Config { cfg.codegen.native_unwind_info = Some(cfg!(target_os = "windows") || self.wasmtime.native_unwind_info); cfg.codegen.parallel_compilation = Some(false); + cfg.debug.address_map = Some(self.wasmtime.generate_address_map); cfg.opts.opt_level = Some(self.wasmtime.opt_level.to_wasmtime()); cfg.opts.regalloc_algorithm = Some(self.wasmtime.regalloc_algorithm.to_wasmtime()); @@ -326,9 +327,32 @@ impl Config { && self.wasmtime.pcc && !self.module_config.config.memory64_enabled; + cfg.codegen.inlining = self.wasmtime.inlining; + // Only set cranelift specific flags when the Cranelift strategy is // chosen. if cranelift_strategy { + if let Some(option) = self.wasmtime.inlining_intra_module { + cfg.codegen.cranelift.push(( + "wasmtime_inlining_intra_module".to_string(), + Some(option.to_string()), + )); + } + if let Some(size) = self.wasmtime.inlining_small_callee_size { + cfg.codegen.cranelift.push(( + "wasmtime_inlining_small_callee_size".to_string(), + // Clamp to avoid extreme code size blow up. + Some(std::cmp::min(1000, size).to_string()), + )); + } + if let Some(size) = self.wasmtime.inlining_sum_size_threshold { + cfg.codegen.cranelift.push(( + "wasmtime_inlining_sum_size_threshold".to_string(), + // Clamp to avoid extreme code size blow up. + Some(std::cmp::min(1000, size).to_string()), + )); + } + // If the wasm-smith-generated module use nan canonicalization then we // don't need to enable it, but if it doesn't enable it already then we // enable this codegen option. @@ -554,6 +578,10 @@ pub struct WasmtimeConfig { force_jump_veneers: bool, memory_init_cow: bool, memory_guaranteed_dense_image_size: u64, + inlining: Option, + inlining_intra_module: Option, + inlining_small_callee_size: Option, + inlining_sum_size_threshold: Option, use_precompiled_cwasm: bool, async_stack_zeroing: bool, /// Configuration for the instance allocation strategy to use. @@ -824,6 +852,23 @@ impl RegallocAlgorithm { } } +#[derive(Arbitrary, Clone, Copy, Debug, PartialEq, Eq, Hash)] +enum IntraModuleInlining { + Yes, + No, + WhenUsingGc, +} + +impl std::fmt::Display for IntraModuleInlining { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + IntraModuleInlining::Yes => write!(f, "yes"), + IntraModuleInlining::No => write!(f, "no"), + IntraModuleInlining::WhenUsingGc => write!(f, "gc"), + } + } +} + #[derive(Clone, Debug, PartialEq, Eq, Hash)] /// Compiler to use. pub enum CompilerStrategy { diff --git a/crates/fuzzing/src/generators/table_ops.rs b/crates/fuzzing/src/generators/table_ops.rs index c6a1039b0a..8b7c91d8df 100644 --- a/crates/fuzzing/src/generators/table_ops.rs +++ b/crates/fuzzing/src/generators/table_ops.rs @@ -1,6 +1,7 @@ //! Generating series of `table.get` and `table.set` operations. use mutatis::mutators as m; use mutatis::{Candidates, Context, DefaultMutate, Generate, Mutate, Result as MutResult}; +use serde::{Deserialize, Serialize}; use smallvec::SmallVec; use std::ops::RangeInclusive; use wasm_encoder::{ @@ -11,12 +12,12 @@ use wasm_encoder::{ /// A description of a Wasm module that makes a series of `externref` table /// operations. -#[derive(Debug, Default)] +#[derive(Debug, Default, Serialize, Deserialize)] pub struct TableOps { pub(crate) num_params: u32, pub(crate) num_globals: u32, pub(crate) table_size: i32, - ops: Vec, + pub(crate) ops: Vec, } const NUM_PARAMS_RANGE: RangeInclusive = 0..=10; @@ -182,6 +183,13 @@ impl TableOps { self.ops = new_ops; } + + /// Attempts to remove the last opcode from the sequence. + /// + /// Returns `true` if an opcode was successfully removed, or `false` if the list was already empty. + pub fn pop(&mut self) -> bool { + self.ops.pop().is_some() + } } /// A mutator for the table ops @@ -283,7 +291,7 @@ macro_rules! define_table_ops { $op:ident $( ( $($limit:expr => $ty:ty),* ) )? : $params:expr => $results:expr , )* ) => { - #[derive(Copy, Clone, Debug)] + #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub(crate) enum TableOp { $( $op ( $( $($ty),* )? ), diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs index 74f0862aee..51f7387dc0 100644 --- a/crates/fuzzing/src/oracles.rs +++ b/crates/fuzzing/src/oracles.rs @@ -764,7 +764,7 @@ pub fn wast_test(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<()> { }) .unwrap(); wast_context - .run_buffer(test.path.to_str().unwrap(), test.contents.as_bytes()) + .run_wast(test.path.to_str().unwrap(), test.contents.as_bytes()) .unwrap(); Ok(()) } diff --git a/crates/jit-debug/Cargo.toml b/crates/jit-debug/Cargo.toml index 58565c85d2..059a2c4a2d 100644 --- a/crates/jit-debug/Cargo.toml +++ b/crates/jit-debug/Cargo.toml @@ -26,5 +26,6 @@ wasmtime-versioned-export-macros = { workspace = true } rustix = { workspace = true, features = ["mm", "time"], optional = true } [features] +std = [] gdb_jit_int = [] -perf_jitdump = ["rustix", "object"] +perf_jitdump = ["rustix", "object", "std"] diff --git a/crates/jit-debug/src/gdb_jit_int.rs b/crates/jit-debug/src/gdb_jit_int.rs index 074194a74f..880a6c9b7c 100644 --- a/crates/jit-debug/src/gdb_jit_int.rs +++ b/crates/jit-debug/src/gdb_jit_int.rs @@ -2,9 +2,10 @@ //! the __jit_debug_register_code() and __jit_debug_descriptor to register //! or unregister generated object images with debuggers. -use std::pin::Pin; -use std::ptr; -use std::sync::Mutex; +extern crate alloc; + +use alloc::{boxed::Box, vec::Vec}; +use core::{pin::Pin, ptr}; use wasmtime_versioned_export_macros::versioned_link; #[repr(C)] @@ -33,13 +34,66 @@ unsafe extern "C" { fn __jit_debug_register_code(); } -/// The process controls access to the __jit_debug_descriptor by itself -- -/// the GDB/LLDB accesses this structure and its data at the process startup -/// and when paused in __jit_debug_register_code. +#[cfg(feature = "std")] +mod gdb_registration { + use std::sync::{Mutex, MutexGuard}; + + /// The process controls access to the __jit_debug_descriptor by itself -- + /// the GDB/LLDB accesses this structure and its data at the process startup + /// and when paused in __jit_debug_register_code. + /// + /// The GDB_REGISTRATION lock is needed for GdbJitImageRegistration to protect + /// access to the __jit_debug_descriptor within this process. + pub static GDB_REGISTRATION: Mutex<()> = Mutex::new(()); + + /// The lock guard for the GDB registration lock. + #[expect( + dead_code, + reason = "field used to hold the lock until the end of the scope" + )] + pub struct LockGuard<'a>(MutexGuard<'a, ()>); + + pub fn lock() -> LockGuard<'static> { + LockGuard(GDB_REGISTRATION.lock().unwrap()) + } +} + +/// For no_std there's no access to synchronization primitives so a primitive +/// fallback for now is to panic-on-contention which is, in theory, rare to come +/// up as threads are rarer in no_std mode too. /// -/// The GDB_REGISTRATION lock is needed for GdbJitImageRegistration to protect -/// access to the __jit_debug_descriptor within this process. -static GDB_REGISTRATION: Mutex<()> = Mutex::new(()); +/// This provides the guarantee that the debugger will see consistent state at +/// least, but if this panic is hit in practice it'll require some sort of +/// no_std synchronization mechanism one way or another. +#[cfg(not(feature = "std"))] +mod gdb_registration { + use core::sync::atomic::{AtomicBool, Ordering}; + + /// Whether or not a lock is held or not. + pub static GDB_REGISTRATION: AtomicBool = AtomicBool::new(false); + + /// The lock guard for the GDB registration lock. + pub struct LockGuard; + + /// When the `LockGuard` is dropped, it releases the lock by setting + /// `GDB_REGISTRATION` to false. + impl Drop for LockGuard { + fn drop(&mut self) { + GDB_REGISTRATION.store(false, Ordering::Release); + } + } + + /// Locks the GDB registration lock. If the lock is already held, it panics + pub fn lock() -> LockGuard { + if GDB_REGISTRATION + .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_err() + { + panic!("GDB JIT registration lock contention detected in no_std mode"); + } + LockGuard + } +} /// Registration for JIT image pub struct GdbJitImageRegistration { @@ -87,7 +141,7 @@ unsafe impl Sync for GdbJitImageRegistration {} unsafe fn register_gdb_jit_image(entry: *mut JITCodeEntry) { unsafe { - let _lock = GDB_REGISTRATION.lock().unwrap(); + let _lock = gdb_registration::lock(); let desc = &mut *wasmtime_jit_debug_descriptor(); // Add it to the linked list in the JIT descriptor. @@ -109,7 +163,7 @@ unsafe fn register_gdb_jit_image(entry: *mut JITCodeEntry) { unsafe fn unregister_gdb_jit_image(entry: *mut JITCodeEntry) { unsafe { - let _lock = GDB_REGISTRATION.lock().unwrap(); + let _lock = gdb_registration::lock(); let desc = &mut *wasmtime_jit_debug_descriptor(); // Remove the code entry corresponding to the code from the linked list. diff --git a/crates/jit-debug/src/lib.rs b/crates/jit-debug/src/lib.rs index c747ed10a1..2ea409b501 100644 --- a/crates/jit-debug/src/lib.rs +++ b/crates/jit-debug/src/lib.rs @@ -4,6 +4,10 @@ //! > you're interested in using this feel free to file an issue on the //! > Wasmtime repository to start a discussion about doing so, but otherwise //! > be aware that your usage of this crate is not supported. +#![no_std] + +#[cfg(feature = "std")] +extern crate std; #[cfg(feature = "gdb_jit_int")] pub mod gdb_jit_int; diff --git a/crates/jit-debug/src/perf_jitdump.rs b/crates/jit-debug/src/perf_jitdump.rs index 50dcdcdc22..88aa071062 100644 --- a/crates/jit-debug/src/perf_jitdump.rs +++ b/crates/jit-debug/src/perf_jitdump.rs @@ -16,6 +16,8 @@ use std::io; use std::io::Write; use std::path::Path; use std::ptr; +use std::string::String; +use std::vec::Vec; use std::{mem, process}; /// Defines jitdump record types diff --git a/crates/misc/component-async-tests/src/borrowing_host.rs b/crates/misc/component-async-tests/src/borrowing_host.rs index 67fc4da72a..ddc92e5b21 100644 --- a/crates/misc/component-async-tests/src/borrowing_host.rs +++ b/crates/misc/component-async-tests/src/borrowing_host.rs @@ -8,12 +8,7 @@ pub mod bindings { wasmtime::component::bindgen!({ path: "wit", world: "borrowing-host", - trappable_imports: true, - concurrent_imports: true, - concurrent_exports: true, - async: { - only_imports: [] - }, + imports: { default: trappable }, with: { "local:local/borrowing-types/x": super::MyX, } diff --git a/crates/misc/component-async-tests/src/closed_streams.rs b/crates/misc/component-async-tests/src/closed_streams.rs index 5e402f49f0..bb3d2294f5 100644 --- a/crates/misc/component-async-tests/src/closed_streams.rs +++ b/crates/misc/component-async-tests/src/closed_streams.rs @@ -2,13 +2,5 @@ pub mod bindings { wasmtime::component::bindgen!({ path: "wit", world: "closed-streams", - concurrent_imports: true, - concurrent_exports: true, - async: { - only_imports: [ - "local:local/closed#read-stream", - "local:local/closed#read-future", - ] - }, }); } diff --git a/crates/misc/component-async-tests/src/resource_stream.rs b/crates/misc/component-async-tests/src/resource_stream.rs index 84633db691..3b8bf30dda 100644 --- a/crates/misc/component-async-tests/src/resource_stream.rs +++ b/crates/misc/component-async-tests/src/resource_stream.rs @@ -1,6 +1,6 @@ use anyhow::Result; use wasmtime::component::{ - Accessor, AccessorTask, Resource, StreamReader, StreamWriter, WithAccessor, + Accessor, AccessorTask, GuardedStreamWriter, Resource, StreamReader, StreamWriter, }; use wasmtime_wasi::p2::IoView; @@ -8,39 +8,33 @@ use super::Ctx; pub mod bindings { wasmtime::component::bindgen!({ - trappable_imports: true, path: "wit", world: "read-resource-stream", - concurrent_imports: true, - concurrent_exports: true, - async: true, with: { "local:local/resource-stream/x": super::ResourceStreamX, }, + imports: { + "local:local/resource-stream/foo": async | store | trappable, + default: trappable, + }, }); } pub struct ResourceStreamX; -impl bindings::local::local::resource_stream::HostXConcurrent for Ctx { - async fn foo(accessor: &Accessor, x: Resource) -> Result<()> { - accessor.with(|mut view| { - _ = view.get().table().get(&x)?; - Ok(()) - }) +impl bindings::local::local::resource_stream::HostX for Ctx { + fn foo(&mut self, x: Resource) -> Result<()> { + self.table().get(&x)?; + Ok(()) } - async fn drop(accessor: &Accessor, x: Resource) -> Result<()> { - accessor.with(move |mut view| { - view.get().table().delete(x)?; - Ok(()) - }) + fn drop(&mut self, x: Resource) -> Result<()> { + IoView::table(self).delete(x)?; + Ok(()) } } -impl bindings::local::local::resource_stream::HostX for Ctx {} - -impl bindings::local::local::resource_stream::HostConcurrent for Ctx { +impl bindings::local::local::resource_stream::HostWithStore for Ctx { async fn foo( accessor: &Accessor, count: u32, @@ -53,11 +47,11 @@ impl bindings::local::local::resource_stream::HostConcurrent for Ctx { impl AccessorTask> for Task { async fn run(self, accessor: &Accessor) -> Result<()> { - let mut tx = WithAccessor::new(accessor, self.tx); + let mut tx = GuardedStreamWriter::new(accessor, self.tx); for _ in 0..self.count { let item = accessor.with(|mut view| view.get().table().push(ResourceStreamX))?; - tx.write_all(accessor, Some(item)).await; + tx.write_all(Some(item)).await; } Ok(()) } diff --git a/crates/misc/component-async-tests/src/round_trip.rs b/crates/misc/component-async-tests/src/round_trip.rs index 4f6b237737..d8894c5473 100644 --- a/crates/misc/component-async-tests/src/round_trip.rs +++ b/crates/misc/component-async-tests/src/round_trip.rs @@ -4,29 +4,23 @@ use wasmtime::component::Accessor; pub mod bindings { wasmtime::component::bindgen!({ - trappable_imports: true, path: "wit", world: "round-trip", - concurrent_imports: true, - concurrent_exports: true, - async: true, }); } pub mod non_concurrent_export_bindings { wasmtime::component::bindgen!({ - trappable_imports: true, path: "wit", world: "round-trip", - concurrent_imports: true, - async: true, + exports: { default: ignore_wit | async }, }); } -impl bindings::local::local::baz::HostConcurrent for Ctx { - async fn foo(_: &Accessor, s: String) -> wasmtime::Result { +impl bindings::local::local::baz::HostWithStore for Ctx { + async fn foo(_: &Accessor, s: String) -> String { crate::util::sleep(Duration::from_millis(10)).await; - Ok(format!("{s} - entered host - exited host")) + format!("{s} - entered host - exited host") } } diff --git a/crates/misc/component-async-tests/src/round_trip_direct.rs b/crates/misc/component-async-tests/src/round_trip_direct.rs index 6252bc3c31..1cab5002f5 100644 --- a/crates/misc/component-async-tests/src/round_trip_direct.rs +++ b/crates/misc/component-async-tests/src/round_trip_direct.rs @@ -4,19 +4,15 @@ use wasmtime::component::Accessor; pub mod bindings { wasmtime::component::bindgen!({ - trappable_imports: true, path: "wit", world: "round-trip-direct", - concurrent_imports: true, - concurrent_exports: true, - async: true, }); } -impl bindings::RoundTripDirectImportsConcurrent for Ctx { - async fn foo(_: &Accessor, s: String) -> wasmtime::Result { +impl bindings::RoundTripDirectImportsWithStore for Ctx { + async fn foo(_: &Accessor, s: String) -> String { crate::util::sleep(Duration::from_millis(10)).await; - Ok(format!("{s} - entered host - exited host")) + format!("{s} - entered host - exited host") } } diff --git a/crates/misc/component-async-tests/src/round_trip_many.rs b/crates/misc/component-async-tests/src/round_trip_many.rs index 80ca508688..188c506a90 100644 --- a/crates/misc/component-async-tests/src/round_trip_many.rs +++ b/crates/misc/component-async-tests/src/round_trip_many.rs @@ -5,30 +5,24 @@ use wasmtime::component::Accessor; pub mod bindings { wasmtime::component::bindgen!({ - trappable_imports: true, path: "wit", world: "round-trip-many", - concurrent_imports: true, - concurrent_exports: true, - async: true, additional_derives: [ Eq, PartialEq ], }); } pub mod non_concurrent_export_bindings { wasmtime::component::bindgen!({ - trappable_imports: true, path: "wit", world: "round-trip-many", - concurrent_imports: true, - async: true, additional_derives: [ Eq, PartialEq ], + exports: { default: ignore_wit | async }, }); } use bindings::local::local::many::Stuff; -impl bindings::local::local::many::HostConcurrent for Ctx { +impl bindings::local::local::many::HostWithStore for Ctx { async fn foo( _: &Accessor, a: String, @@ -38,7 +32,7 @@ impl bindings::local::local::many::HostConcurrent for Ctx { e: Stuff, f: Option, g: Result, - ) -> wasmtime::Result<( + ) -> ( String, u32, Vec, @@ -46,9 +40,9 @@ impl bindings::local::local::many::HostConcurrent for Ctx { Stuff, Option, Result, - )> { + ) { crate::util::sleep(Duration::from_millis(10)).await; - Ok(( + ( format!("{a} - entered host - exited host"), b, c, @@ -56,7 +50,7 @@ impl bindings::local::local::many::HostConcurrent for Ctx { e, f, g, - )) + ) } } diff --git a/crates/misc/component-async-tests/src/sleep.rs b/crates/misc/component-async-tests/src/sleep.rs index 050ede20c5..13e13950c7 100644 --- a/crates/misc/component-async-tests/src/sleep.rs +++ b/crates/misc/component-async-tests/src/sleep.rs @@ -5,16 +5,9 @@ use wasmtime::component::Accessor; wasmtime::component::bindgen!({ path: "wit", world: "sleep-host", - concurrent_imports: true, - concurrent_exports: true, - async: { - only_imports: [ - "local:local/sleep#[async]sleep-millis", - ] - }, }); -impl local::local::sleep::HostConcurrent for Ctx { +impl local::local::sleep::HostWithStore for Ctx { async fn sleep_millis(_: &Accessor, time_in_millis: u64) { crate::util::sleep(Duration::from_millis(time_in_millis)).await; } diff --git a/crates/misc/component-async-tests/src/transmit.rs b/crates/misc/component-async-tests/src/transmit.rs index 6b7490492c..e4ff839b6b 100644 --- a/crates/misc/component-async-tests/src/transmit.rs +++ b/crates/misc/component-async-tests/src/transmit.rs @@ -2,7 +2,5 @@ pub mod bindings { wasmtime::component::bindgen!({ path: "wit", world: "transmit-callee", - concurrent_exports: true, - async: true }); } diff --git a/crates/misc/component-async-tests/src/yield_host.rs b/crates/misc/component-async-tests/src/yield_host.rs index cd8a6e62f4..ab1fb37346 100644 --- a/crates/misc/component-async-tests/src/yield_host.rs +++ b/crates/misc/component-async-tests/src/yield_host.rs @@ -8,13 +8,6 @@ pub mod bindings { wasmtime::component::bindgen!({ path: "wit", world: "yield-host", - concurrent_imports: true, - concurrent_exports: true, - async: { - only_imports: [ - "local:local/ready#[async]when-ready", - ] - }, }); } @@ -43,7 +36,7 @@ impl bindings::local::local::ready::Host for Ctx { } } -impl bindings::local::local::ready::HostConcurrent for Ctx { +impl bindings::local::local::ready::HostWithStore for Ctx { async fn when_ready(accessor: &Accessor) { let wakers = accessor.with(|mut view| view.get().wakers.clone()); future::poll_fn(move |cx| { diff --git a/crates/misc/component-async-tests/tests/scenario/round_trip.rs b/crates/misc/component-async-tests/tests/scenario/round_trip.rs index 985ad3a745..1db81d8ec6 100644 --- a/crates/misc/component-async-tests/tests/scenario/round_trip.rs +++ b/crates/misc/component-async-tests/tests/scenario/round_trip.rs @@ -341,7 +341,7 @@ pub async fn test_round_trip( *expected, &round_trip .local_local_baz() - .call_foo(&mut store, (*input).to_owned()) + .call_foo(&mut store, input) .await? ); } @@ -389,11 +389,17 @@ pub async fn test_round_trip( let mut futures = FuturesUnordered::new(); for (input, output) in inputs_and_outputs { let output = (*output).to_owned(); - futures.push( + futures.push(async move { + let mut results = vec![Val::Bool(false)]; foo_function - .call_concurrent(store, vec![Val::String((*input).to_owned())]) - .map(move |v| v.map(move |v| (v, output))), - ); + .call_concurrent( + store, + &[Val::String((*input).to_owned())], + &mut results, + ) + .await?; + anyhow::Ok((results, output)) + }); } while let Some((actual, expected)) = futures.try_next().await? { diff --git a/crates/misc/component-async-tests/tests/scenario/round_trip_direct.rs b/crates/misc/component-async-tests/tests/scenario/round_trip_direct.rs index b29ff93618..7b503aaaec 100644 --- a/crates/misc/component-async-tests/tests/scenario/round_trip_direct.rs +++ b/crates/misc/component-async-tests/tests/scenario/round_trip_direct.rs @@ -93,7 +93,7 @@ async fn test_round_trip_direct( wasmtime_wasi::p2::add_to_linker_async(&mut linker)?; linker .root() - .func_new_concurrent("foo", |_, params, results| { + .func_new_concurrent("[async]foo", |_, params, results| { Box::pin(async move { sleep(Duration::from_millis(10)).await; let Some(Val::String(s)) = params.into_iter().next() else { @@ -108,7 +108,7 @@ async fn test_round_trip_direct( let instance = linker.instantiate_async(&mut store, &component).await?; let foo_function = instance - .get_export_index(&mut store, None, "foo") + .get_export_index(&mut store, None, "[async]foo") .ok_or_else(|| anyhow!("can't find `foo` in instance"))?; let foo_function = instance .get_func(&mut store, foo_function) @@ -119,9 +119,13 @@ async fn test_round_trip_direct( .run_concurrent(&mut store, async |store| -> wasmtime::Result<_> { let mut futures = FuturesUnordered::new(); for _ in 0..3 { - futures.push( - foo_function.call_concurrent(store, vec![Val::String(input.to_owned())]), - ); + futures.push(async move { + let mut results = vec![Val::Bool(false)]; + foo_function + .call_concurrent(store, &[Val::String(input.to_owned())], &mut results) + .await?; + anyhow::Ok(results) + }); } while let Some(value) = futures.try_next().await? { diff --git a/crates/misc/component-async-tests/tests/scenario/round_trip_many.rs b/crates/misc/component-async-tests/tests/scenario/round_trip_many.rs index c696ce0e9c..4379749764 100644 --- a/crates/misc/component-async-tests/tests/scenario/round_trip_many.rs +++ b/crates/misc/component-async-tests/tests/scenario/round_trip_many.rs @@ -325,16 +325,7 @@ async fn test_round_trip_many( ), round_trip_many .local_local_many() - .call_foo( - &mut store, - (*input).to_owned(), - b, - c.clone(), - d, - e.clone(), - f.clone(), - g.clone() - ) + .call_foo(&mut store, input, b, &c, d, &e, f.as_ref(), Err(())) .await? ); } @@ -407,11 +398,13 @@ async fn test_round_trip_many( let mut futures = FuturesUnordered::new(); for (input, output) in inputs_and_outputs { let output = (*output).to_owned(); - futures.push( + futures.push(async move { + let mut result = vec![Val::Bool(false)]; foo_function - .call_concurrent(store, make(input)) - .map(move |v| v.map(move |v| (v, output))), - ); + .call_concurrent(store, &make(input), &mut result) + .await?; + anyhow::Ok((result, output)) + }); } while let Some((actual, expected)) = futures.try_next().await? { diff --git a/crates/misc/component-async-tests/tests/scenario/streams.rs b/crates/misc/component-async-tests/tests/scenario/streams.rs index f19c4de600..0798411d90 100644 --- a/crates/misc/component-async-tests/tests/scenario/streams.rs +++ b/crates/misc/component-async-tests/tests/scenario/streams.rs @@ -7,22 +7,23 @@ use { stream::{FuturesUnordered, StreamExt, TryStreamExt}, }, std::{ - future::Future, + future::{self, Future}, pin::pin, sync::{Arc, Mutex}, task::{Context, Waker}, }, wasmtime::{ - Engine, Store, - component::{Linker, ResourceTable, StreamReader, StreamWriter, VecBuffer, WithAccessor}, + Engine, Store, Trap, + component::{ + Accessor, GuardedFutureReader, GuardedStreamReader, GuardedStreamWriter, Linker, + ResourceTable, VecBuffer, + }, }, wasmtime_wasi::p2::WasiCtxBuilder, }; #[tokio::test] pub async fn async_watch_streams() -> Result<()> { - use wasmtime::component::{DropWithStore, DropWithStoreAndValue}; - let engine = Engine::new(&config())?; let mut store = Store::new( @@ -48,80 +49,79 @@ pub async fn async_watch_streams() -> Result<()> { let instance = linker.instantiate_async(&mut store, &component).await?; // Test watching and then dropping the read end of a stream. - let (mut tx, rx) = instance.stream::(&mut store)?; + let (mut tx, mut rx) = instance.stream::(&mut store)?; instance .run_concurrent(&mut store, async |store| { - futures::join!(tx.watch_reader(store), async { rx.drop_with(store) }).1 + futures::join!(tx.watch_reader(store), async { rx.close_with(store) }).1 }) - .await??; + .await?; // Test dropping and then watching the read end of a stream. - let (mut tx, rx) = instance.stream::(&mut store)?; + let (mut tx, mut rx) = instance.stream::(&mut store)?; instance .run_concurrent(&mut store, async |store| { - rx.drop_with(store)?; + rx.close_with(store); tx.watch_reader(store).await; - anyhow::Ok(()) }) - .await??; + .await?; // Test watching and then dropping the write end of a stream. - let (tx, mut rx) = instance.stream::(&mut store)?; + let (mut tx, mut rx) = instance.stream::(&mut store)?; instance .run_concurrent(&mut store, async |store| { - futures::join!(rx.watch_writer(store), async { tx.drop_with(store) }).1 + futures::join!(rx.watch_writer(store), async { tx.close_with(store) }).1 }) - .await??; + .await?; // Test dropping and then watching the write end of a stream. - let (tx, mut rx) = instance.stream::(&mut store)?; + let (mut tx, mut rx) = instance.stream::(&mut store)?; instance .run_concurrent(&mut store, async |store| { - tx.drop_with(store)?; + tx.close_with(store); rx.watch_writer(store).await; - anyhow::Ok(()) }) - .await??; + .await?; // Test watching and then dropping the read end of a future. - let (mut tx, rx) = instance.future::(&mut store)?; + let (mut tx, mut rx) = instance.future::(&mut store, || 42)?; instance .run_concurrent(&mut store, async |store| { - futures::join!(tx.watch_reader(store), async { rx.drop_with(store) }).1 + futures::join!(tx.watch_reader(store), async { rx.close_with(store) }).1 }) - .await??; + .await?; // Test dropping and then watching the read end of a future. - let (mut tx, rx) = instance.future::(&mut store)?; + let (mut tx, mut rx) = instance.future::(&mut store, || 42)?; instance .run_concurrent(&mut store, async |store| { - rx.drop_with(store)?; + rx.close_with(store); tx.watch_reader(store).await; - anyhow::Ok(()) }) - .await??; + .await?; // Test watching and then dropping the write end of a future. - let (tx, mut rx) = instance.future::(&mut store)?; + let (mut tx, mut rx) = instance.future::(&mut store, || 42)?; instance .run_concurrent(&mut store, async |store| { - futures::join!(rx.watch_writer(store), async { tx.drop_with(store, 42) }).1 + futures::join!(rx.watch_writer(store), async { tx.close_with(store) }).1 }) - .await??; + .await?; // Test dropping and then watching the write end of a future. - let (tx, mut rx) = instance.future::(&mut store)?; + let (mut tx, mut rx) = instance.future::(&mut store, || 42)?; instance .run_concurrent(&mut store, async |store| { - tx.drop_with(store, 42)?; + tx.close_with(store); rx.watch_writer(store).await; - anyhow::Ok(()) }) - .await??; + .await?; enum Event<'a> { - Write(Option, Ctx>>), - Read(Option, Ctx>>, Option), + Write(Option>>), + Read( + Option>>, + Option, + ), } // Test watching, then writing to, then dropping, then writing again to the @@ -129,17 +129,17 @@ pub async fn async_watch_streams() -> Result<()> { let (tx, rx) = instance.stream(&mut store)?; instance .run_concurrent(&mut store, async move |store| -> wasmtime::Result<_> { - let mut tx = WithAccessor::new(store, tx); - let mut rx = WithAccessor::new(store, rx); + let mut tx = GuardedStreamWriter::new(store, tx); + let mut rx = GuardedStreamReader::new(store, rx); let mut futures = FuturesUnordered::new(); assert!( - pin!(tx.watch_reader(store)) + pin!(tx.watch_reader()) .poll(&mut Context::from_waker(&Waker::noop())) .is_pending() ); futures.push( async move { - tx.write_all(store, Some(42)).await; + tx.write_all(Some(42)).await; let w = if tx.is_closed() { None } else { Some(tx) }; anyhow::Ok(Event::Write(w)) } @@ -147,7 +147,7 @@ pub async fn async_watch_streams() -> Result<()> { ); futures.push( async move { - let b = rx.read(store, None).await; + let b = rx.read(None).await; let r = if rx.is_closed() { None } else { Some(rx) }; Ok(Event::Read(r, b)) } @@ -169,8 +169,8 @@ pub async fn async_watch_streams() -> Result<()> { drop(rx); let mut tx = tx.take().unwrap(); - tx.watch_reader(store).await; - tx.write_all(store, Some(42)).await; + tx.watch_reader().await; + tx.write_all(Some(42)).await; assert!(tx.is_closed()); Ok(()) }) @@ -215,9 +215,9 @@ pub async fn test_closed_streams(watch: bool) -> Result<()> { let instance = linker.instantiate_async(&mut store, &component).await?; enum StreamEvent<'a> { - FirstWrite(Option, Ctx>>), - FirstRead(Option, Ctx>>, Vec), - SecondWrite(Option, Ctx>>), + FirstWrite(Option>>), + FirstRead(Option>>, Vec), + SecondWrite(Option>>), GuestCompleted, } @@ -239,14 +239,14 @@ pub async fn test_closed_streams(watch: bool) -> Result<()> { instance .run_concurrent(&mut store, async move |store| -> wasmtime::Result<_> { - let mut tx = WithAccessor::new(store, tx); - let mut rx = WithAccessor::new(store, rx); + let mut tx = GuardedStreamWriter::new(store, tx); + let mut rx = GuardedStreamReader::new(store, rx); let mut futures = FuturesUnordered::new(); futures.push({ let values = values.clone(); async move { - tx.write_all(store, VecBuffer::from(values)).await; + tx.write_all(VecBuffer::from(values)).await; anyhow::Ok(StreamEvent::FirstWrite(if tx.is_closed() { None } else { @@ -257,7 +257,7 @@ pub async fn test_closed_streams(watch: bool) -> Result<()> { }); futures.push( async move { - let b = rx.read(store, Vec::with_capacity(3)).await; + let b = rx.read(Vec::with_capacity(3)).await; let r = if rx.is_closed() { None } else { Some(rx) }; Ok(StreamEvent::FirstRead(r, b)) } @@ -272,7 +272,7 @@ pub async fn test_closed_streams(watch: bool) -> Result<()> { if watch { futures.push( async move { - tx.watch_reader(store).await; + tx.watch_reader().await; Ok(StreamEvent::SecondWrite(None)) } .boxed(), @@ -281,7 +281,7 @@ pub async fn test_closed_streams(watch: bool) -> Result<()> { futures.push({ let values = values.clone(); async move { - tx.write_all(store, VecBuffer::from(values)).await; + tx.write_all(VecBuffer::from(values)).await; Ok(StreamEvent::SecondWrite(if tx.is_closed() { None } else { @@ -315,12 +315,12 @@ pub async fn test_closed_streams(watch: bool) -> Result<()> { // Next, test futures host->host { - let (tx, rx) = instance.future(&mut store)?; - let (mut tx_ignored, rx_ignored) = instance.future(&mut store)?; + let (tx, rx) = instance.future(&mut store, || unreachable!())?; + let (mut tx_ignored, rx_ignored) = instance.future(&mut store, || unreachable!())?; instance .run_concurrent(&mut store, async move |store| { - let rx_ignored = WithAccessor::new(store, rx_ignored); + let rx_ignored = GuardedFutureReader::new(store, rx_ignored); let mut futures = FuturesUnordered::new(); futures.push(tx.write(store, value).map(FutureEvent::Write).boxed()); @@ -376,7 +376,7 @@ pub async fn test_closed_streams(watch: bool) -> Result<()> { instance .run_concurrent(&mut store, async move |accessor| { - let mut tx = WithAccessor::new(accessor, tx); + let mut tx = GuardedStreamWriter::new(accessor, tx); let mut futures = FuturesUnordered::new(); futures.push( @@ -389,7 +389,7 @@ pub async fn test_closed_streams(watch: bool) -> Result<()> { futures.push({ let values = values.clone(); async move { - tx.write_all(accessor, VecBuffer::from(values)).await; + tx.write_all(VecBuffer::from(values)).await; let w = if tx.is_closed() { None } else { Some(tx) }; Ok(StreamEvent::FirstWrite(w)) } @@ -404,7 +404,7 @@ pub async fn test_closed_streams(watch: bool) -> Result<()> { if watch { futures.push( async move { - tx.watch_reader(accessor).await; + tx.watch_reader().await; Ok(StreamEvent::SecondWrite(None)) } .boxed(), @@ -413,7 +413,7 @@ pub async fn test_closed_streams(watch: bool) -> Result<()> { futures.push({ let values = values.clone(); async move { - tx.write_all(accessor, VecBuffer::from(values)).await; + tx.write_all(VecBuffer::from(values)).await; let w = if tx.is_closed() { None } else { Some(tx) }; Ok(StreamEvent::SecondWrite(w)) } @@ -442,8 +442,8 @@ pub async fn test_closed_streams(watch: bool) -> Result<()> { // Next, test futures host->guest { - let (tx, rx) = instance.future(&mut store)?; - let (mut tx_ignored, rx_ignored) = instance.future(&mut store)?; + let (tx, rx) = instance.future(&mut store, || unreachable!())?; + let (mut tx_ignored, rx_ignored) = instance.future(&mut store, || unreachable!())?; let closed_streams = closed_streams::bindings::ClosedStreams::new(&mut store, &instance)?; @@ -502,5 +502,38 @@ pub async fn test_closed_streams(watch: bool) -> Result<()> { .await??; } + // Next, test futures host->guest again, but this time using the default value when closing the writers. + { + let (mut tx, rx) = instance.future(&mut store, || 42)?; + let (mut tx_ignored, rx_ignored) = instance.future(&mut store, || 42)?; + + let closed_streams = closed_streams::bindings::ClosedStreams::new(&mut store, &instance)?; + + let result = instance + .run_concurrent(&mut store, async move |accessor| { + closed_streams + .local_local_closed() + .call_read_future_post_return(accessor, rx, 42, rx_ignored) + .await?; + + tx.close_with(accessor); + tx_ignored.close_with(accessor); + + future::pending::<()>().await; + + anyhow::Ok(()) + }) + .await; + + // As of this writing, passing a future which never resolves to + // `Instance::run_concurrent` and expecting a `Trap::AsyncDeadlock` is + // the only way to join all tasks for the `Instance`, so that's what we + // do: + assert!(matches!( + result.unwrap_err().downcast::(), + Ok(Trap::AsyncDeadlock) + )); + } + Ok(()) } diff --git a/crates/misc/component-async-tests/tests/scenario/transmit.rs b/crates/misc/component-async-tests/tests/scenario/transmit.rs index 18cdcc02db..35bb44cdd4 100644 --- a/crates/misc/component-async-tests/tests/scenario/transmit.rs +++ b/crates/misc/component-async-tests/tests/scenario/transmit.rs @@ -8,12 +8,12 @@ use cancel::exports::local::local::cancel::Mode; use component_async_tests::transmit::bindings::exports::local::local::transmit::Control; use component_async_tests::{Ctx, sleep, transmit}; use futures::{ - future::{self, FutureExt}, + future::FutureExt, stream::{FuturesUnordered, TryStreamExt}, }; use wasmtime::component::{ - Accessor, Component, FutureReader, HasSelf, Instance, Linker, ResourceTable, StreamReader, - StreamWriter, Val, WithAccessor, + Accessor, Component, FutureReader, GuardedFutureReader, GuardedStreamReader, + GuardedStreamWriter, HasSelf, Instance, Linker, ResourceTable, StreamReader, Val, }; use wasmtime::{AsContextMut, Engine, Store}; use wasmtime_wasi::p2::WasiCtxBuilder; @@ -32,11 +32,7 @@ pub mod cancel { wasmtime::component::bindgen!({ path: "wit", world: "cancel-host", - concurrent_imports: true, - concurrent_exports: true, - async: { - only_imports: [], - } + exports: { default: async | store }, }); } @@ -52,6 +48,11 @@ pub async fn async_cancel_caller() -> Result<()> { test_cancel(Mode::Normal).await } +#[tokio::test] +pub async fn async_cancel_caller_leak_task_after_cancel() -> Result<()> { + test_cancel(Mode::LeakTaskAfterCancel).await +} + #[tokio::test] pub async fn async_trap_cancel_guest_after_start_cancelled() -> Result<()> { test_cancel_trap(Mode::TrapCancelGuestAfterStartCancelled).await @@ -270,11 +271,11 @@ impl TransmitTest for DynamicTransmitTest { Ok((instance, instance)) } - fn call<'a>( + async fn call<'a>( accessor: &'a Accessor>, instance: &'a Self::Instance, params: Self::Params, - ) -> impl Future> + Send + 'a { + ) -> Result { let exchange_function = accessor.with(|mut store| { let transmit_instance = instance .get_export_index(store.as_context_mut(), None, "local:local/transmit") @@ -289,15 +290,13 @@ impl TransmitTest for DynamicTransmitTest { instance .get_func(store.as_context_mut(), exchange_function) .ok_or_else(|| anyhow!("can't find `exchange` in instance")) - }); + })?; - match exchange_function { - Ok(exchange_function) => exchange_function - .call_concurrent(accessor, params) - .map(|v| v.map(|v| v.into_iter().next().unwrap())) - .boxed(), - Err(e) => future::ready(Err(e)).boxed(), - } + let mut results = vec![Val::Bool(false)]; + exchange_function + .call_concurrent(accessor, ¶ms, &mut results) + .await?; + Ok(results.pop().unwrap()) } fn into_params( @@ -365,30 +364,30 @@ async fn test_transmit_with(component: &str) -> Re enum Event<'a, Test: TransmitTest> { Result(Test::Result), - ControlWriteA(Option, Ctx>>), - ControlWriteB(Option, Ctx>>), - ControlWriteC(Option, Ctx>>), + ControlWriteA(Option>>), + ControlWriteB(Option>>), + ControlWriteC(Option>>), ControlWriteD, WriteA, WriteB(bool), ReadC( - Option, Ctx>>, + Option>>, Option, ), ReadD(Option), - ReadNone(Option, Ctx>>), + ReadNone(Option>>), } let (control_tx, control_rx) = instance.stream(&mut store)?; let (caller_stream_tx, caller_stream_rx) = instance.stream(&mut store)?; - let (caller_future1_tx, caller_future1_rx) = instance.future(&mut store)?; - let (_caller_future2_tx, caller_future2_rx) = instance.future(&mut store)?; + let (caller_future1_tx, caller_future1_rx) = instance.future(&mut store, || unreachable!())?; + let (_caller_future2_tx, caller_future2_rx) = instance.future(&mut store, || unreachable!())?; instance .run_concurrent(&mut store, async move |accessor| { - let mut control_tx = WithAccessor::new(accessor, control_tx); - let control_rx = WithAccessor::new(accessor, control_rx); - let mut caller_stream_tx = WithAccessor::new(accessor, caller_stream_tx); + let mut control_tx = GuardedStreamWriter::new(accessor, control_tx); + let control_rx = GuardedStreamReader::new(accessor, control_rx); + let mut caller_stream_tx = GuardedStreamWriter::new(accessor, caller_stream_tx); let mut futures = FuturesUnordered::< Pin>> + Send>>, @@ -401,7 +400,7 @@ async fn test_transmit_with(component: &str) -> Re futures.push( async move { control_tx - .write_all(accessor, Some(Control::ReadStream("a".into()))) + .write_all(Some(Control::ReadStream("a".into()))) .await; let w = if control_tx.is_closed() { None @@ -415,9 +414,7 @@ async fn test_transmit_with(component: &str) -> Re futures.push( async move { - caller_stream_tx - .write_all(accessor, Some(String::from("a"))) - .await; + caller_stream_tx.write_all(Some(String::from("a"))).await; Ok(Event::WriteA) } .boxed(), @@ -428,7 +425,7 @@ async fn test_transmit_with(component: &str) -> Re accessor, &test, Test::into_params( - control_rx.into_inner(), + control_rx.into(), caller_stream_rx, caller_future1_rx, caller_future2_rx, @@ -443,15 +440,14 @@ async fn test_transmit_with(component: &str) -> Re Event::Result(result) => { let (stream_rx, future_rx, _) = accessor .with(|mut store| Test::from_result(&mut store, instance, result))?; - callee_stream_rx = Some(WithAccessor::new(accessor, stream_rx)); - callee_future1_rx = Some(WithAccessor::new(accessor, future_rx)); + callee_stream_rx = Some(GuardedStreamReader::new(accessor, stream_rx)); + callee_future1_rx = Some(GuardedFutureReader::new(accessor, future_rx)); } Event::ControlWriteA(tx) => { futures.push( async move { let mut tx = tx.unwrap(); - tx.write_all(accessor, Some(Control::ReadFuture("b".into()))) - .await; + tx.write_all(Some(Control::ReadFuture("b".into()))).await; let w = if tx.is_closed() { None } else { Some(tx) }; Ok(Event::ControlWriteB(w)) } @@ -473,8 +469,7 @@ async fn test_transmit_with(component: &str) -> Re futures.push( async move { let mut tx = tx.unwrap(); - tx.write_all(accessor, Some(Control::WriteStream("c".into()))) - .await; + tx.write_all(Some(Control::WriteStream("c".into()))).await; let w = if tx.is_closed() { None } else { Some(tx) }; Ok(Event::ControlWriteC(w)) } @@ -486,7 +481,7 @@ async fn test_transmit_with(component: &str) -> Re let mut rx = callee_stream_rx.take().unwrap(); futures.push( async move { - let b = rx.read(accessor, None).await; + let b = rx.read(None).await; let r = if rx.is_closed() { None } else { Some(rx) }; Ok(Event::ReadC(r, b)) } @@ -497,8 +492,7 @@ async fn test_transmit_with(component: &str) -> Re futures.push( async move { let mut tx = tx.unwrap(); - tx.write_all(accessor, Some(Control::WriteFuture("d".into()))) - .await; + tx.write_all(Some(Control::WriteFuture("d".into()))).await; Ok(Event::ControlWriteD) } .boxed(), @@ -511,8 +505,7 @@ async fn test_transmit_with(component: &str) -> Re callee_future1_rx .take() .unwrap() - .into_inner() - .read(accessor) + .read() .map(Event::ReadD) .map(Ok) .boxed(), @@ -526,7 +519,7 @@ async fn test_transmit_with(component: &str) -> Re let mut rx = callee_stream_rx.take().unwrap(); futures.push( async move { - rx.read(accessor, None).await; + rx.read(None).await; let r = if rx.is_closed() { None } else { Some(rx) }; Ok(Event::ReadNone(r)) } diff --git a/crates/misc/component-async-tests/wit/test.wit b/crates/misc/component-async-tests/wit/test.wit index 78a7099898..f074e9bc31 100644 --- a/crates/misc/component-async-tests/wit/test.wit +++ b/crates/misc/component-async-tests/wit/test.wit @@ -38,8 +38,8 @@ world round-trip-many { } world round-trip-direct { - import foo: func(s: string) -> string; - export foo: func(s: string) -> string; + import foo: async func(s: string) -> string; + export foo: async func(s: string) -> string; } interface ready { @@ -124,8 +124,9 @@ interface resource-stream { } interface closed { - read-stream: func(rx: stream, expected: list); - read-future: func(rx: future, expected: u8, rx-ignore: future); + read-stream: async func(rx: stream, expected: list); + read-future: async func(rx: future, expected: u8, rx-ignore: future); + read-future-post-return: async func(rx: future, expected: u8, rx-ignore: future); } interface sleep { @@ -151,6 +152,7 @@ interface cancel { trap-cancel-guest-after-return, trap-cancel-host-after-return-cancelled, trap-cancel-host-after-return, + leak-task-after-cancel, } run: func(mode: mode, cancel-delay-millis: u64); diff --git a/crates/test-programs/src/bin/async_cancel_callee.rs b/crates/test-programs/src/bin/async_cancel_callee.rs index 1379676862..c7ace208f2 100644 --- a/crates/test-programs/src/bin/async_cancel_callee.rs +++ b/crates/test-programs/src/bin/async_cancel_callee.rs @@ -57,6 +57,7 @@ const _MODE_TRAP_CANCEL_GUEST_AFTER_RETURN_CANCELLED: u8 = 2; const _MODE_TRAP_CANCEL_GUEST_AFTER_RETURN: u8 = 3; const MODE_TRAP_CANCEL_HOST_AFTER_RETURN_CANCELLED: u8 = 4; const MODE_TRAP_CANCEL_HOST_AFTER_RETURN: u8 = 5; +const MODE_LEAK_TASK_AFTER_CANCEL: u8 = 6; #[derive(Clone, Copy)] struct SleepParams { @@ -166,7 +167,10 @@ unsafe extern "C" fn callback_sleep_with_options_sleep_millis( } waitable_join(*waitable, 0); - subtask_drop(*waitable); + + if params.mode != MODE_LEAK_TASK_AFTER_CANCEL { + subtask_drop(*waitable); + } if params.on_cancel_delay_millis == 0 { match params.on_cancel { diff --git a/crates/test-programs/src/bin/async_closed_streams.rs b/crates/test-programs/src/bin/async_closed_streams.rs index cf90cf4a96..459b542fb2 100644 --- a/crates/test-programs/src/bin/async_closed_streams.rs +++ b/crates/test-programs/src/bin/async_closed_streams.rs @@ -11,7 +11,7 @@ mod bindings { use { bindings::exports::local::local::closed::Guest, - wit_bindgen_rt::async_support::{FutureReader, StreamReader, StreamResult}, + wit_bindgen_rt::async_support::{self, FutureReader, StreamReader, StreamResult}, }; struct Component; @@ -26,6 +26,16 @@ impl Guest for Component { async fn read_future(rx: FutureReader, expected: u8, _rx_ignored: FutureReader) { assert_eq!(rx.await, expected); } + + async fn read_future_post_return( + rx: FutureReader, + expected: u8, + _rx_ignored: FutureReader, + ) { + async_support::spawn(async move { + assert_eq!(rx.await, expected); + }); + } } // Unused function; required since this file is built as a `bin`: diff --git a/crates/test-programs/src/bin/async_round_trip_direct_stackless.rs b/crates/test-programs/src/bin/async_round_trip_direct_stackless.rs index b817e33ff2..bbe2e229f3 100644 --- a/crates/test-programs/src/bin/async_round_trip_direct_stackless.rs +++ b/crates/test-programs/src/bin/async_round_trip_direct_stackless.rs @@ -2,7 +2,6 @@ mod bindings { wit_bindgen::generate!({ path: "../misc/component-async-tests/wit", world: "round-trip-direct", - async: true, }); use super::Component; diff --git a/crates/test-programs/src/bin/p3_cli.rs b/crates/test-programs/src/bin/p3_cli.rs new file mode 100644 index 0000000000..b0a05c0ca9 --- /dev/null +++ b/crates/test-programs/src/bin/p3_cli.rs @@ -0,0 +1,40 @@ +use test_programs::p3::wasi::cli::{ + environment, stderr, stdin, stdout, terminal_stderr, terminal_stdin, terminal_stdout, +}; +use test_programs::p3::wit_stream; +use wit_bindgen::StreamResult; + +struct Component; + +test_programs::p3::export!(Component); + +impl test_programs::p3::exports::wasi::cli::run::Guest for Component { + async fn run() -> Result<(), ()> { + assert_eq!(environment::get_arguments(), ["p3_cli.component", "."]); + assert_ne!(environment::get_environment(), []); + assert_eq!(environment::initial_cwd(), None); + + assert!(terminal_stdin::get_terminal_stdin().is_none()); + assert!(terminal_stdout::get_terminal_stdout().is_none()); + assert!(terminal_stderr::get_terminal_stderr().is_none()); + + let mut stdin = stdin::get_stdin(); + assert!(stdin.next().await.is_none()); + + let (mut stdout_tx, stdout_rx) = wit_stream::new(); + stdout::set_stdout(stdout_rx); + let (res, buf) = stdout_tx.write(b"hello stdout\n".into()).await; + assert_eq!(res, StreamResult::Complete(13)); + assert_eq!(buf.into_vec(), []); + + let (mut stderr_tx, stderr_rx) = wit_stream::new(); + stderr::set_stderr(stderr_rx); + let (res, buf) = stderr_tx.write(b"hello stderr\n".into()).await; + assert_eq!(res, StreamResult::Complete(13)); + assert_eq!(buf.into_vec(), []); + + Ok(()) + } +} + +fn main() {} diff --git a/crates/test-programs/src/bin/p3_sockets_ip_name_lookup.rs b/crates/test-programs/src/bin/p3_sockets_ip_name_lookup.rs index 3b1b2a9d64..94548c2191 100644 --- a/crates/test-programs/src/bin/p3_sockets_ip_name_lookup.rs +++ b/crates/test-programs/src/bin/p3_sockets_ip_name_lookup.rs @@ -1,4 +1,4 @@ -use futures::try_join; +use futures::join; use test_programs::p3::wasi::sockets::ip_name_lookup::{ErrorCode, resolve_addresses}; use test_programs::p3::wasi::sockets::types::IpAddress; @@ -17,11 +17,13 @@ async fn resolve_one(name: &str) -> Result { impl test_programs::p3::exports::wasi::cli::run::Guest for Component { async fn run() -> Result<(), ()> { // Valid domains - try_join!( + let (res0, res1) = join!( resolve_addresses("localhost".into()), resolve_addresses("example.com".into()) - ) - .unwrap(); + ); + if res0.is_err() && res1.is_err() { + panic!("should have been able to resolve at least one domain"); + } // NB: this is an actual real resolution, so it might time out, might cause // issues, etc. This result is ignored to prevent flaky failures in CI. diff --git a/crates/test-programs/src/bin/p3_sockets_tcp_bind.rs b/crates/test-programs/src/bin/p3_sockets_tcp_bind.rs index 23dd69a093..6045fc1c8d 100644 --- a/crates/test-programs/src/bin/p3_sockets_tcp_bind.rs +++ b/crates/test-programs/src/bin/p3_sockets_tcp_bind.rs @@ -4,6 +4,7 @@ use test_programs::p3::wasi::sockets::types::{ ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, TcpSocket, }; use test_programs::p3::wit_stream; +use wit_bindgen::yield_blocking; struct Component; @@ -84,14 +85,27 @@ async fn test_tcp_bind_reuseaddr(ip: IpAddress) { bind_addr }; - { + // If SO_REUSEADDR was configured correctly, the following lines + // shouldn't be affected by the TIME_WAIT state of the just closed + // `listener1` socket. + // + // Note though that the way things are modeled in Wasmtime right now is that + // the TCP socket is kept alive by a spawned task created in `listen` + // meaning that to fully close the socket it requires the spawned task to + // shut down. That may require yielding to the host or similar so try a few + // times to let the host get around to closing the task while testing each + // time to see if we can reuse the address. This loop is bounded because it + // should complete "quickly". + for _ in 0..10 { let listener2 = TcpSocket::new(ip.family()); - - // If SO_REUSEADDR was configured correctly, the following lines shouldn't be - // affected by the TIME_WAIT state of the just closed `listener1` socket: - listener2.bind(bind_addr).unwrap(); - listener2.listen().unwrap(); + if listener2.bind(bind_addr).is_ok() { + listener2.listen().unwrap(); + return; + } + yield_blocking(); } + + panic!("looks like REUSEADDR isn't in use?"); } // Try binding to an address that is not configured on the system. @@ -163,11 +177,8 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { test_tcp_bind_specific_port(IpAddress::IPV4_UNSPECIFIED); test_tcp_bind_specific_port(IpAddress::IPV6_UNSPECIFIED); - // FIXME: these tests are broken and should be investigated. - if false { - test_tcp_bind_reuseaddr(IpAddress::IPV4_LOOPBACK).await; - test_tcp_bind_reuseaddr(IpAddress::IPV6_LOOPBACK).await; - } + test_tcp_bind_reuseaddr(IpAddress::IPV4_LOOPBACK).await; + test_tcp_bind_reuseaddr(IpAddress::IPV6_LOOPBACK).await; test_tcp_bind_addrinuse(IpAddress::IPV4_LOOPBACK); test_tcp_bind_addrinuse(IpAddress::IPV6_LOOPBACK); diff --git a/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs b/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs index 6541f4a91d..0aaf63b065 100644 --- a/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs +++ b/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs @@ -104,7 +104,7 @@ async fn test_tcp_shutdown_should_not_lose_data(family: IpAddressFamily) { outgoing_data, incoming_data, "Received data should match the sent data" ); - server_fut.await.unwrap() + server_fut.await.unwrap(); }, ); }) diff --git a/crates/test-programs/src/bin/preview1_renumber.rs b/crates/test-programs/src/bin/preview1_renumber.rs index bfeb250b85..394e5776f4 100644 --- a/crates/test-programs/src/bin/preview1_renumber.rs +++ b/crates/test-programs/src/bin/preview1_renumber.rs @@ -74,6 +74,46 @@ unsafe fn test_renumber(dir_fd: wasip1::Fd) { ); wasip1::fd_close(fd_to).expect("closing a file"); + + wasip1::fd_renumber(0, 0).expect("renumbering 0 to 0"); + let fd_file3 = wasip1::path_open( + dir_fd, + 0, + "file3", + wasip1::OFLAGS_CREAT, + wasip1::RIGHTS_FD_READ | wasip1::RIGHTS_FD_WRITE, + 0, + 0, + ) + .expect("opening a file"); + assert!( + fd_file3 > libc::STDERR_FILENO as wasip1::Fd, + "file descriptor range check", + ); + + wasip1::fd_renumber(fd_file3, 127).expect("renumbering FD to 127"); + match wasip1::fd_renumber(127, u32::MAX) { + Err(wasip1::ERRNO_NOMEM) => { + // The preview1 adapter cannot handle more than 128 descriptors + eprintln!("fd_renumber({fd_file3}, {}) returned NOMEM", u32::MAX) + } + res => res.expect("renumbering FD to `u32::MAX`"), + } + + let fd_file4 = wasip1::path_open( + dir_fd, + 0, + "file4", + wasip1::OFLAGS_CREAT, + wasip1::RIGHTS_FD_READ | wasip1::RIGHTS_FD_WRITE, + 0, + 0, + ) + .expect("opening a file"); + assert!( + fd_file4 > libc::STDERR_FILENO as wasip1::Fd, + "file descriptor range check", + ); } fn main() { diff --git a/crates/test-programs/src/bin/tls_sample_application.rs b/crates/test-programs/src/bin/tls_sample_application.rs index 8cc813d848..b12d96a4cc 100644 --- a/crates/test-programs/src/bin/tls_sample_application.rs +++ b/crates/test-programs/src/bin/tls_sample_application.rs @@ -34,10 +34,11 @@ fn test_tls_sample_application(domain: &str, ip: IpAddress) -> Result<()> { .context("closing tls connection failed")?; socket.shutdown(ShutdownType::Both)?; - if String::from_utf8(response)?.contains("HTTP/1.1 200 OK") { + let response = String::from_utf8(response)?; + if response.contains("HTTP/1.1 200 OK") { Ok(()) } else { - Err(anyhow!("server did not respond with 200 OK")) + Err(anyhow!("server did not respond with 200 OK: {response}")) } } diff --git a/crates/test-util/src/wast.rs b/crates/test-util/src/wast.rs index 8c80400c79..0b2058bfd9 100644 --- a/crates/test-util/src/wast.rs +++ b/crates/test-util/src/wast.rs @@ -75,11 +75,6 @@ fn spec_test_config(test: &Path) -> TestConfig { let mut ret = TestConfig::default(); ret.spec_test = Some(true); match spec_proposal_from_path(test) { - Some("multi-memory") => { - ret.multi_memory = Some(true); - ret.reference_types = Some(true); - ret.simd = Some(true); - } Some("wide-arithmetic") => { ret.wide_arithmetic = Some(true); } @@ -87,25 +82,9 @@ fn spec_test_config(test: &Path) -> TestConfig { ret.threads = Some(true); ret.reference_types = Some(false); } - Some("tail-call") => { - ret.tail_call = Some(true); - ret.reference_types = Some(true); - } Some("relaxed-simd") => { ret.relaxed_simd = Some(true); } - Some("memory64") => { - ret.memory64 = Some(true); - ret.tail_call = Some(true); - ret.gc = Some(true); - ret.extended_const = Some(true); - ret.multi_memory = Some(true); - ret.relaxed_simd = Some(true); - } - Some("extended-const") => { - ret.extended_const = Some(true); - ret.reference_types = Some(true); - } Some("custom-page-sizes") => { ret.custom_page_sizes = Some(true); ret.multi_memory = Some(true); @@ -117,21 +96,6 @@ fn spec_test_config(test: &Path) -> TestConfig { ret.hogs_memory = Some(true); } } - Some("exception-handling") => { - ret.reference_types = Some(true); - ret.exceptions = Some(true); - if test.parent().unwrap().ends_with("legacy") { - ret.legacy_exceptions = Some(true); - } - } - Some("gc") => { - ret.gc = Some(true); - ret.tail_call = Some(true); - } - Some("function-references") => { - ret.function_references = Some(true); - ret.tail_call = Some(true); - } Some("annotations") => { ret.simd = Some(true); } diff --git a/crates/unwinder/src/exception_table.rs b/crates/unwinder/src/exception_table.rs index ab36ce079d..025df705bd 100644 --- a/crates/unwinder/src/exception_table.rs +++ b/crates/unwinder/src/exception_table.rs @@ -15,24 +15,36 @@ use object::{Bytes, LittleEndian, U32Bytes}; #[cfg(feature = "cranelift")] use alloc::{vec, vec::Vec}; #[cfg(feature = "cranelift")] -use cranelift_codegen::{FinalizedMachCallSite, binemit::CodeOffset}; +use cranelift_codegen::{ + ExceptionContextLoc, FinalizedMachCallSite, FinalizedMachExceptionHandler, binemit::CodeOffset, +}; /// Collector struct for exception handlers per call site. /// /// # Format /// -/// We keep four different arrays (`Vec`s) that we build as we visit +/// We keep five different arrays (`Vec`s) that we build as we visit /// callsites, in ascending offset (address relative to beginning of -/// code segment) order: tags, destination offsets, callsite offsets, -/// and tag/destination ranges. +/// code segment) order: callsite offsets, tag/destination ranges, +/// tags, tag context SP offset, destination offsets. /// /// The callsite offsets and tag/destination ranges logically form a /// sorted lookup array, allowing us to find information for any -/// single callsite. The range denotes a range of indices in the tag -/// and destination offset arrays, and those are sorted by tag per -/// callsite. Ranges are stored with the (exclusive) *end* index only; -/// the start index is implicit as the previous end, or zero if first -/// element. +/// single callsite. The range denotes a range of indices in the +/// tag/context and destination offset arrays. Ranges are stored with +/// the (exclusive) *end* index only; the start index is implicit as +/// the previous end, or zero if first element. +/// +/// The slices of tag, context, and handlers arrays named by `ranges` +/// for each callsite specify a series of handler items for that +/// callsite. The tag and context together allow a +/// dynamic-tag-instance match in the unwinder: the context specifies +/// an offset from SP at the callsite that contains a machine word +/// (e.g. with vmctx) that, together with the static tag index, can be +/// used to perform a dynamic match. A context of `-1` indicates no +/// dynamic context, and a tag of `-1` indicates a catch-all +/// handler. If a handler item matches, control should be transferred +/// to the code offset given in the last array, `handlers`. /// /// # Example /// @@ -42,6 +54,7 @@ use cranelift_codegen::{FinalizedMachCallSite, binemit::CodeOffset}; /// callsites: [0x10, 0x50, 0xf0] // callsites (return addrs) at offsets 0x10, 0x50, 0xf0 /// ranges: [2, 4, 5] // corresponding ranges for each callsite /// tags: [1, 5, 1, -1, -1] // tags for each handler at each callsite +/// contexts: [-1, -1, 0x10, 0x20, 0x30] // SP-offset for context for each tag /// handlers: [0x40, 0x42, 0x6f, 0x71, 0xf5] // handler destinations at each callsite /// ``` /// @@ -64,6 +77,16 @@ use cranelift_codegen::{FinalizedMachCallSite, binemit::CodeOffset}; /// # tags for callsite 0xf0: /// -1, # "catch-all" /// ] +/// contexts: [ +/// # SP-offsets for context for each tag at callsite 0x10: +/// -1, +/// -1, +/// # for callsite 0x50: +/// 0x10, +/// 0x20, +/// # for callsite 0xf0: +/// 0x30, +/// ] /// handlers: [ /// # handlers for callsite 0x10: /// 0x40, # relative PC to handle tag 1 (above) @@ -81,6 +104,7 @@ pub struct ExceptionTableBuilder { pub callsites: Vec>, pub ranges: Vec>, pub tags: Vec>, + pub contexts: Vec>, pub handlers: Vec>, last_start_offset: CodeOffset, } @@ -107,22 +131,38 @@ impl ExceptionTableBuilder { for call_site in call_sites { let ret_addr = call_site.ret_addr.checked_add(start_offset).unwrap(); handlers.extend(call_site.exception_handlers.iter().cloned()); - handlers.sort_by_key(|(tag, _dest)| *tag); - - if handlers.windows(2).any(|parts| parts[0].0 == parts[1].0) { - anyhow::bail!("Duplicate handler tag"); - } let start_idx = u32::try_from(self.tags.len()).unwrap(); - for (tag, dest) in handlers.drain(..) { - self.tags.push(U32Bytes::new( - LittleEndian, - tag.expand().map(|t| t.as_u32()).unwrap_or(u32::MAX), - )); - self.handlers.push(U32Bytes::new( - LittleEndian, - dest.checked_add(start_offset).unwrap(), - )); + let mut context = u32::MAX; + for handler in call_site.exception_handlers { + match handler { + FinalizedMachExceptionHandler::Tag(tag, offset) => { + self.tags.push(U32Bytes::new(LittleEndian, tag.as_u32())); + self.contexts.push(U32Bytes::new(LittleEndian, context)); + self.handlers.push(U32Bytes::new( + LittleEndian, + offset.checked_add(start_offset).unwrap(), + )); + } + FinalizedMachExceptionHandler::Default(offset) => { + self.tags.push(U32Bytes::new(LittleEndian, u32::MAX)); + self.contexts.push(U32Bytes::new(LittleEndian, context)); + self.handlers.push(U32Bytes::new( + LittleEndian, + offset.checked_add(start_offset).unwrap(), + )); + } + FinalizedMachExceptionHandler::Context(ExceptionContextLoc::SPOffset( + offset, + )) => { + context = *offset; + } + FinalizedMachExceptionHandler::Context(ExceptionContextLoc::GPR(_)) => { + panic!( + "Wasmtime exception unwind info only supports dynamic contexts on the stack" + ); + } + } } let end_idx = u32::try_from(self.tags.len()).unwrap(); @@ -151,6 +191,7 @@ impl ExceptionTableBuilder { f(object::bytes_of_slice(&self.callsites)); f(object::bytes_of_slice(&self.ranges)); f(object::bytes_of_slice(&self.tags)); + f(object::bytes_of_slice(&self.contexts)); f(object::bytes_of_slice(&self.handlers)); } @@ -173,6 +214,11 @@ pub struct ExceptionTable<'a> { callsites: &'a [U32Bytes], ranges: &'a [U32Bytes], tags: &'a [U32Bytes], + #[expect( + dead_code, + reason = "Will be used in subsequent PR for Wasm exception handling" + )] + contexts: &'a [U32Bytes], handlers: &'a [U32Bytes], } @@ -197,6 +243,9 @@ impl<'a> ExceptionTable<'a> { .map_err(|_| anyhow::anyhow!("Unable to read ranges slice"))?; let (tags, data) = object::slice_from_bytes::>(data, handler_count) .map_err(|_| anyhow::anyhow!("Unable to read tags slice"))?; + let (contexts, data) = + object::slice_from_bytes::>(data, handler_count) + .map_err(|_| anyhow::anyhow!("Unable to read contexts slice"))?; let (handlers, data) = object::slice_from_bytes::>(data, handler_count) .map_err(|_| anyhow::anyhow!("Unable to read handlers slice"))?; @@ -209,6 +258,7 @@ impl<'a> ExceptionTable<'a> { callsites, ranges, tags, + contexts, handlers, }) } @@ -269,9 +319,9 @@ mod test { FinalizedMachCallSite { ret_addr: 0x10, exception_handlers: &[ - (Some(ExceptionTag::new(1)).into(), 0x20), - (Some(ExceptionTag::new(2)).into(), 0x30), - (None.into(), 0x40), + FinalizedMachExceptionHandler::Tag(ExceptionTag::new(1), 0x20), + FinalizedMachExceptionHandler::Tag(ExceptionTag::new(2), 0x30), + FinalizedMachExceptionHandler::Default(0x40), ], }, FinalizedMachCallSite { @@ -280,7 +330,7 @@ mod test { }, FinalizedMachCallSite { ret_addr: 0x50, - exception_handlers: &[(None.into(), 0x60)], + exception_handlers: &[FinalizedMachExceptionHandler::Default(0x60)], }, ]; diff --git a/crates/wasi-common/src/snapshots/preview_1/error.rs b/crates/wasi-common/src/snapshots/preview_1/error.rs index 71a950679d..b2164390cd 100644 --- a/crates/wasi-common/src/snapshots/preview_1/error.rs +++ b/crates/wasi-common/src/snapshots/preview_1/error.rs @@ -249,11 +249,9 @@ impl From for Error { PtrOverflow { .. } | PtrOutOfBounds { .. } | PtrNotAligned { .. } => { Error::trap(err.into()) } - PtrBorrowed { .. } => Errno::Fault.into(), InvalidUtf8 { .. } => Errno::Ilseq.into(), TryFromIntError { .. } => Errno::Overflow.into(), SliceLengthsDiffer { .. } => Errno::Fault.into(), - BorrowCheckerOutOfHandles { .. } => Errno::Fault.into(), InFunc { err, .. } => Error::from(*err), } } diff --git a/crates/wasi-config/src/lib.rs b/crates/wasi-config/src/lib.rs index a7161d8501..63fde906f1 100644 --- a/crates/wasi-config/src/lib.rs +++ b/crates/wasi-config/src/lib.rs @@ -75,7 +75,7 @@ mod gen_ { wasmtime::component::bindgen!({ path: "wit", world: "wasi:config/imports", - trappable_imports: true, + imports: { default: trappable }, }); } use self::gen_::wasi::config::store as generated; diff --git a/crates/wasi-http/Cargo.toml b/crates/wasi-http/Cargo.toml index c23362e99f..62cd2c7f7f 100644 --- a/crates/wasi-http/Cargo.toml +++ b/crates/wasi-http/Cargo.toml @@ -11,6 +11,14 @@ description = "Experimental HTTP library for WebAssembly in Wasmtime" [lints] workspace = true +[features] +default = ["default-send-request", "p3"] +default-send-request = ["dep:tokio-rustls", "dep:rustls", "dep:webpki-roots"] +p3 = [ + "wasmtime-wasi/p3", + "wasmtime/component-model-async", +] + [dependencies] anyhow = { workspace = true } async-trait = { workspace = true } @@ -29,9 +37,9 @@ tracing = { workspace = true } wasmtime-wasi = { workspace = true } wasmtime-wasi-io = { workspace = true } wasmtime = { workspace = true, features = ['component-model'] } -tokio-rustls = { workspace = true } -rustls = { workspace = true } -webpki-roots = { workspace = true } +tokio-rustls = { workspace = true, optional = true } +rustls = { workspace = true, optional = true } +webpki-roots = { workspace = true, optional = true } pin-project-lite = "0.2.14" [dev-dependencies] @@ -47,10 +55,3 @@ base64 = { workspace = true } flate2 = { workspace = true } wasm-compose = { workspace = true } tempfile = { workspace = true } - -[features] -default = ["p3"] -p3 = [ - "wasmtime-wasi/p3", - "wasmtime/component-model-async", -] diff --git a/crates/wasi-http/src/bindings.rs b/crates/wasi-http/src/bindings.rs index cc3d3433e0..bb9bb1483f 100644 --- a/crates/wasi-http/src/bindings.rs +++ b/crates/wasi-http/src/bindings.rs @@ -8,14 +8,8 @@ mod generated { wasmtime::component::bindgen!({ path: "wit", world: "wasi:http/proxy", - tracing: true, - // Flag this as "possibly async" which will cause the exports to be - // generated as async, but none of the imports here are async since - // all the blocking-ness happens in wasi:io - async: { - only_imports: ["nonexistent"], - }, - trappable_imports: true, + imports: { default: tracing | trappable }, + exports: { default: async }, require_store_data_send: true, with: { // Upstream package dependencies @@ -55,8 +49,7 @@ pub mod sync { mod generated { wasmtime::component::bindgen!({ world: "wasi:http/proxy", - tracing: true, - async: false, + imports: { default: tracing }, with: { // http is in this crate "wasi:http": crate::bindings::http, diff --git a/crates/wasi-http/src/error.rs b/crates/wasi-http/src/error.rs index c2f440975d..8a2c2cf0fc 100644 --- a/crates/wasi-http/src/error.rs +++ b/crates/wasi-http/src/error.rs @@ -58,6 +58,7 @@ impl fmt::Display for HttpError { impl Error for HttpError {} +#[cfg(feature = "default-send-request")] pub(crate) fn dns_error(rcode: String, info_code: u16) -> ErrorCode { ErrorCode::DnsError(crate::bindings::http::types::DnsErrorPayload { rcode: Some(rcode), diff --git a/crates/wasi-http/src/p3/bindings.rs b/crates/wasi-http/src/p3/bindings.rs index 90b3f93986..f6d12c3dc5 100644 --- a/crates/wasi-http/src/p3/bindings.rs +++ b/crates/wasi-http/src/p3/bindings.rs @@ -5,21 +5,17 @@ mod generated { wasmtime::component::bindgen!({ path: "src/p3/wit", world: "wasi:http/proxy", - //tracing: true, // TODO: Reenable once fixed - trappable_imports: true, - concurrent_exports: true, - concurrent_imports: true, - async: { - only_imports: [ - "wasi:http/handler@0.3.0-draft#[async]handle", - "wasi:http/types@0.3.0-draft#[method]request.body", - "wasi:http/types@0.3.0-draft#[method]response.body", - "wasi:http/types@0.3.0-draft#[static]request.new", - "wasi:http/types@0.3.0-draft#[drop]request", - "wasi:http/types@0.3.0-draft#[static]response.new", - "wasi:http/types@0.3.0-draft#[drop]response", - ], + imports: { + "wasi:http/handler/[async]handle": async | store | trappable | tracing, + "wasi:http/types/[method]request.body": async | store | trappable | tracing, + "wasi:http/types/[method]response.body": async | store | trappable | tracing, + "wasi:http/types/[static]request.new": async | store | trappable | tracing, + "wasi:http/types/[drop]request": async | store | trappable | tracing, + "wasi:http/types/[static]response.new": async | store | trappable | tracing, + "wasi:http/types/[drop]response": async | store | trappable | tracing, + default: trappable | tracing, }, + exports: { default: async | store }, with: { "wasi:http/types/fields": with::Fields, "wasi:http/types/request": crate::p3::Request, diff --git a/crates/wasi-http/src/p3/body.rs b/crates/wasi-http/src/p3/body.rs index 4eef210268..508c226704 100644 --- a/crates/wasi-http/src/p3/body.rs +++ b/crates/wasi-http/src/p3/body.rs @@ -14,8 +14,7 @@ use tokio::sync::mpsc; use tokio::sync::oneshot; use wasmtime::AsContextMut; use wasmtime::component::{ - Accessor, DropWithStore, DropWithStoreAndValue, FutureReader, FutureWriter, HasData, Resource, - StreamReader, + Accessor, AsAccessor, FutureReader, FutureWriter, HasData, Resource, StreamReader, }; use wasmtime_wasi::p3::WithChildren; @@ -115,10 +114,8 @@ impl Body { buffer: None, } } -} -impl DropWithStore for Body { - fn drop(self, mut store: impl AsContextMut) -> wasmtime::Result<()> { + pub(crate) fn drop_with_store(&mut self, mut store: impl AsContextMut) { if let Body::Guest { contents, trailers, @@ -128,14 +125,44 @@ impl DropWithStore for Body { { let mut store = store.as_context_mut(); if let MaybeTombstone::Some(contents) = contents { - contents.drop(&mut store)?; + contents.close(&mut store); } if let MaybeTombstone::Some(trailers) = trailers { - trailers.drop(&mut store)?; + trailers.close(&mut store); } - tx.drop(store, Ok(()))?; + tx.close(store); + } + } + + pub(crate) fn with_store(self, store: A) -> BodyWithStore + where + A: AsAccessor, + { + BodyWithStore { + store, + body: Some(self), + } + } +} + +pub(crate) struct BodyWithStore { + store: A, + body: Option, +} + +impl BodyWithStore { + pub(crate) fn into_inner(mut self) -> Body { + self.body.take().unwrap() + } +} + +impl Drop for BodyWithStore { + fn drop(&mut self) { + if let Some(body) = &mut self.body { + self.store + .as_accessor() + .with(|store| body.drop_with_store(store)) } - anyhow::Ok(()) } } diff --git a/crates/wasi-http/src/p3/host/handler.rs b/crates/wasi-http/src/p3/host/handler.rs index 0e80a337c1..a032511894 100644 --- a/crates/wasi-http/src/p3/host/handler.rs +++ b/crates/wasi-http/src/p3/host/handler.rs @@ -17,10 +17,12 @@ use std::sync::Arc; use tokio::sync::mpsc; use tokio::sync::oneshot; use tracing::debug; -use wasmtime::component::{Accessor, Resource, WithAccessor, WithAccessorAndValue}; +use wasmtime::component::{ + Accessor, FutureWriter, GuardedFutureWriter, GuardedStreamReader, Resource, StreamReader, +}; use wasmtime_wasi::p3::{AbortOnDropHandle, ResourceView as _, SpawnExt}; -impl handler::HostConcurrent for WasiHttp +impl handler::HostWithStore for WasiHttp where T: WasiHttpView + 'static, { @@ -48,7 +50,7 @@ where bail!("lock poisoned"); }; - let body = WithAccessor::new(store, body); + let body = body.with_store(store); let mut client = store.with(|mut view| view.get().http().client.clone()); @@ -229,8 +231,8 @@ where tx, content_length, } => { - let contents = WithAccessor::new(store, contents); - let tx = WithAccessorAndValue::new(store, tx, Ok(())); + let contents = GuardedStreamReader::new(store, contents); + let tx = GuardedFutureWriter::new(store, tx); let (trailers_tx, trailers_rx) = oneshot::channel(); let (body_tx, body_rx) = mpsc::channel(1); let task = AbortOnDropHandle(store.spawn_fn_box(|store| { @@ -255,18 +257,18 @@ where return Ok(Err(err)); } }; - let contents = contents.into_inner(); - let tx = tx.into_inner(); + let contents = StreamReader::from(contents); + let tx = FutureWriter::from(tx); store.spawn_fn_box(move |store| Box::pin(async move { - let mut contents = WithAccessor::new(store, contents); - let tx = WithAccessorAndValue::new(store, tx, Ok(())); + let mut contents = GuardedStreamReader::new(store, contents); + let tx = GuardedFutureWriter::new(store, tx); let (io_res, body_res) = futures::join! { io, async { body_tx.send(Ok(buffer)).await?; let mut rx_buffer = BytesMut::with_capacity(DEFAULT_BUFFER_CAPACITY); while !contents.is_closed() { - rx_buffer = contents.read(store,rx_buffer).await; + rx_buffer = contents.read(rx_buffer).await; let buffer = rx_buffer.split(); body_tx.send(Ok(buffer.freeze())).await?; rx_buffer.reserve(DEFAULT_BUFFER_CAPACITY); @@ -279,7 +281,7 @@ where // itself goes away due to cancellation elsewhere, so // swallow this error. let _ = body_res; - tx.into_inner().write(store,io_res.map_err(Into::into)).await; + tx.write(io_res.map_err(Into::into)).await; Ok(()) })); match response.await { diff --git a/crates/wasi-http/src/p3/host/types.rs b/crates/wasi-http/src/p3/host/types.rs index e012a7b394..160ef51a21 100644 --- a/crates/wasi-http/src/p3/host/types.rs +++ b/crates/wasi-http/src/p3/host/types.rs @@ -1,6 +1,6 @@ use crate::p3::bindings::http::types::{ - ErrorCode, FieldName, FieldValue, HeaderError, Host, HostConcurrent, HostFields, HostRequest, - HostRequestConcurrent, HostRequestOptions, HostResponse, HostResponseConcurrent, Method, + ErrorCode, FieldName, FieldValue, HeaderError, Host, HostFields, HostRequest, + HostRequestOptions, HostRequestWithStore, HostResponse, HostResponseWithStore, Method, RequestOptionsError, Scheme, StatusCode, Trailers, }; use crate::p3::host::{ @@ -27,8 +27,8 @@ use http_body::Body as _; use std::io::Cursor; use std::sync::Arc; use wasmtime::component::{ - Accessor, AccessorTask, DropWithStore, FutureReader, FutureWriter, Resource, StreamReader, - StreamWriter, WithAccessor, WithAccessorAndValue, + Accessor, AccessorTask, FutureReader, FutureWriter, GuardedFutureReader, GuardedFutureWriter, + GuardedStreamReader, GuardedStreamWriter, Resource, StreamReader, StreamWriter, }; use wasmtime_wasi::ResourceTable; use wasmtime_wasi::p3::bindings::clocks::monotonic_clock::Duration; @@ -128,8 +128,8 @@ where }; mem::replace(&mut *body, Body::Consumed) }; - let mut contents_tx = WithAccessor::new(store, self.contents_tx); - let mut trailers_tx = WithAccessorAndValue::new(store, self.trailers_tx, Ok(None)); + let mut contents_tx = GuardedStreamWriter::new(store, self.contents_tx); + let mut trailers_tx = GuardedFutureWriter::new(store, self.trailers_tx); match body { Body::Guest { contents: MaybeTombstone::None, @@ -145,8 +145,7 @@ where }, async { trailers_tx - .into_inner() - .write(store, Err(self.cx.as_body_size_error(sent))) + .write(Err(self.cx.as_body_size_error(sent))) .await; } ); @@ -161,7 +160,7 @@ where } => { drop(contents_tx); let res = { - let mut watch_reader = pin!(trailers_tx.watch_reader(store)); + let mut watch_reader = pin!(trailers_tx.watch_reader()); let mut trailers_rx = pin!(trailers_rx.read(store)); poll_fn(|cx| match watch_reader.as_mut().poll(cx) { Poll::Ready(()) => return Poll::Ready(None), @@ -184,11 +183,7 @@ where }; return Ok(()); }; - if !trailers_tx - .into_inner() - .write(store, clone_trailer_result(&res)) - .await - { + if !trailers_tx.write(clone_trailer_result(&res)).await { let Ok(mut body) = self.body.lock() else { bail!("lock poisoned"); }; @@ -212,11 +207,7 @@ where content_length: None, } => { drop(contents_tx); - if !trailers_tx - .into_inner() - .write(store, clone_trailer_result(&res)) - .await - { + if !trailers_tx.write(clone_trailer_result(&res)).await { let Ok(mut body) = self.body.lock() else { bail!("lock poisoned"); }; @@ -239,11 +230,11 @@ where tx, mut content_length, } => { - let mut contents_rx = WithAccessor::new(store, contents_rx); - let trailers_rx = WithAccessor::new(store, trailers_rx); + let mut contents_rx = GuardedStreamReader::new(store, contents_rx); + let trailers_rx = GuardedFutureReader::new(store, trailers_rx); match buffer { Some(BodyFrame::Data(buffer)) => { - let buffer = contents_tx.write_all(store, Cursor::new(buffer)).await; + let buffer = contents_tx.write_all(Cursor::new(buffer)).await; if contents_tx.is_closed() { let Ok(mut body) = self.body.lock() else { bail!("lock poisoned"); @@ -251,8 +242,8 @@ where let pos = buffer.position().try_into()?; let buffer = buffer.into_inner().split_off(pos); *body = Body::Guest { - contents: MaybeTombstone::Some(contents_rx.into_inner()), - trailers: MaybeTombstone::Some(trailers_rx.into_inner()), + contents: MaybeTombstone::Some(contents_rx.into()), + trailers: MaybeTombstone::Some(trailers_rx.into()), buffer: Some(BodyFrame::Data(buffer)), tx, content_length, @@ -266,8 +257,8 @@ where let mut rx_buffer = BytesMut::with_capacity(DEFAULT_BUFFER_CAPACITY); loop { let res = { - let mut contents_tx_drop = pin!(contents_tx.watch_reader(store)); - let mut next_chunk = pin!(contents_rx.read(store, rx_buffer)); + let mut contents_tx_drop = pin!(contents_tx.watch_reader()); + let mut next_chunk = pin!(contents_rx.read(rx_buffer)); poll_fn(|cx| match contents_tx_drop.as_mut().poll(cx) { Poll::Ready(()) => return Poll::Ready(None), Poll::Pending => next_chunk.as_mut().poll(cx).map(Some), @@ -284,7 +275,7 @@ where // reads in Wasmtime to fully support this to avoid // needing `Taken` at all. contents: MaybeTombstone::Tombstone, - trailers: MaybeTombstone::Some(trailers_rx.into_inner()), + trailers: MaybeTombstone::Some(trailers_rx.into()), buffer: None, tx, content_length, @@ -305,8 +296,7 @@ where }, async { trailers_tx - .into_inner() - .write(store, Err(self.cx.as_body_size_error(sent))) + .write(Err(self.cx.as_body_size_error(sent))) .await; } ); @@ -335,10 +325,7 @@ where tx.write(store, Err(self.cx.as_body_size_error(n))).await; }, async { - trailers_tx - .into_inner() - .write(store, Err(self.cx.as_body_size_error(n))) - .await; + trailers_tx.write(Err(self.cx.as_body_size_error(n))).await; } ); return Ok(()); @@ -346,7 +333,7 @@ where } let buffer = rx_buffer.split().freeze(); rx_buffer.reserve(DEFAULT_BUFFER_CAPACITY); - let buffer = tx_tail.write_all(store, Cursor::new(buffer)).await; + let buffer = tx_tail.write_all(Cursor::new(buffer)).await; if tx_tail.is_closed() { let Ok(mut body) = self.body.lock() else { bail!("lock poisoned"); @@ -354,8 +341,8 @@ where let pos = buffer.position().try_into()?; let buffer = buffer.into_inner().split_off(pos); *body = Body::Guest { - contents: MaybeTombstone::Some(contents_rx.into_inner()), - trailers: MaybeTombstone::Some(trailers_rx.into_inner()), + contents: MaybeTombstone::Some(contents_rx.into()), + trailers: MaybeTombstone::Some(trailers_rx.into()), buffer: Some(BodyFrame::Data(buffer)), tx, content_length, @@ -366,8 +353,8 @@ where } let res = { - let mut watch_reader = pin!(trailers_tx.watch_reader(store)); - let mut trailers_rx = pin!(trailers_rx.into_inner().read(store)); + let mut watch_reader = pin!(trailers_tx.watch_reader()); + let mut trailers_rx = pin!(trailers_rx.read()); poll_fn(|cx| match watch_reader.as_mut().poll(cx) { Poll::Ready(()) => return Poll::Ready(None), Poll::Pending => trailers_rx.as_mut().poll(cx).map(Some), @@ -389,11 +376,7 @@ where }; return Ok(()); }; - if !trailers_tx - .into_inner() - .write(store, clone_trailer_result(&res)) - .await - { + if !trailers_tx.write(clone_trailer_result(&res)).await { let Ok(mut body) = self.body.lock() else { bail!("lock poisoned"); }; @@ -416,7 +399,7 @@ where } => { match buffer { Some(BodyFrame::Data(buffer)) => { - let buffer = contents_tx.write_all(store, Cursor::new(buffer)).await; + let buffer = contents_tx.write_all(Cursor::new(buffer)).await; if contents_tx.is_closed() { let Ok(mut body) = self.body.lock() else { bail!("lock poisoned"); @@ -435,7 +418,7 @@ where } loop { let res = { - let mut watch_reader = pin!(contents_tx.watch_reader(store)); + let mut watch_reader = pin!(contents_tx.watch_reader()); poll_fn(|cx| match watch_reader.as_mut().poll(cx) { Poll::Ready(()) => return Poll::Ready(None), Poll::Pending => Pin::new(&mut stream).poll_frame(cx).map(Some), @@ -456,7 +439,7 @@ where } Some(None) => { drop(contents_tx); - if !trailers_tx.into_inner().write(store, Ok(None)).await { + if !trailers_tx.write(Ok(None)).await { let Ok(mut body) = self.body.lock() else { bail!("lock poisoned"); }; @@ -470,8 +453,7 @@ where Some(Some(Ok(frame))) => { match frame.into_data().map_err(http_body::Frame::into_trailers) { Ok(buffer) => { - let buffer = - contents_tx.write_all(store, Cursor::new(buffer)).await; + let buffer = contents_tx.write_all(Cursor::new(buffer)).await; if contents_tx.is_closed() { let Ok(mut body) = self.body.lock() else { bail!("lock poisoned"); @@ -491,8 +473,7 @@ where push_fields(view.get().table(), WithChildren::new(trailers)) })?; if !trailers_tx - .into_inner() - .write(store, Ok(Some(Resource::new_own(trailers.rep())))) + .write(Ok(Some(Resource::new_own(trailers.rep())))) .await { let Ok(mut body) = self.body.lock() else { @@ -507,11 +488,7 @@ where } Err(Err(..)) => { drop(contents_tx); - if !trailers_tx - .into_inner() - .write(store, Err(ErrorCode::HttpProtocolError)) - .await - { + if !trailers_tx.write(Err(ErrorCode::HttpProtocolError)).await { let Ok(mut body) = self.body.lock() else { bail!("lock poisoned"); }; @@ -528,11 +505,7 @@ where } Some(Some(Err(err))) => { drop(contents_tx); - if !trailers_tx - .into_inner() - .write(store, Err(err.clone())) - .await - { + if !trailers_tx.write(Err(err.clone())).await { let Ok(mut body) = self.body.lock() else { bail!("lock poisoned"); }; @@ -551,11 +524,7 @@ where buffer: Some(BodyFrame::Trailers(res)), } => { drop(contents_tx); - if !trailers_tx - .into_inner() - .write(store, clone_trailer_result(&res)) - .await - { + if !trailers_tx.write(clone_trailer_result(&res)).await { let Ok(mut body) = self.body.lock() else { bail!("lock poisoned"); }; @@ -574,8 +543,6 @@ where impl Host for WasiHttpImpl where T: WasiHttpView {} -impl HostConcurrent for WasiHttp where T: WasiHttpView + 'static {} - fn parse_header_value( name: &http::HeaderName, value: impl AsRef<[u8]>, @@ -765,7 +732,7 @@ where } } -impl HostRequestConcurrent for WasiHttp +impl HostRequestWithStore for WasiHttp where T: WasiHttpView + 'static, { @@ -779,7 +746,7 @@ where store.with(|mut view| { let instance = view.instance(); let (res_tx, res_rx) = instance - .future(&mut view) + .future(&mut view, || Ok(())) .context("failed to create future")?; let contents = match contents { Some(contents) => MaybeTombstone::Some(contents), @@ -821,7 +788,7 @@ where .stream(&mut view) .context("failed to create stream")?; let (trailers_tx, trailers_rx) = instance - .future(&mut view) + .future(&mut view, || Ok(None)) .context("failed to create future")?; let mut binding = view.get(); let Request { body, .. } = get_request_mut(binding.table(), &req)?; @@ -860,7 +827,9 @@ where .table() .delete(req) .context("failed to delete request from table")?; - mem::replace(request.body.lock().unwrap().deref_mut(), Body::Consumed).drop(store) + mem::replace(request.body.lock().unwrap().deref_mut(), Body::Consumed) + .drop_with_store(store); + Ok(()) }) } } @@ -1097,7 +1066,7 @@ where } } -impl HostResponseConcurrent for WasiHttp +impl HostResponseWithStore for WasiHttp where T: WasiHttpView + 'static, { @@ -1110,7 +1079,7 @@ where store.with(|mut view| { let instance = view.instance(); let (res_tx, res_rx) = instance - .future(&mut view) + .future(&mut view, || Ok(())) .context("failed to create future")?; let contents = match contents { Some(contents) => MaybeTombstone::Some(contents), @@ -1143,7 +1112,7 @@ where .stream(&mut view) .context("failed to create stream")?; let (trailers_tx, trailers_rx) = instance - .future(&mut view) + .future(&mut view, || Ok(None)) .context("failed to create future")?; let mut binding = view.get(); let Response { body, .. } = get_response_mut(binding.table(), &res)?; @@ -1182,7 +1151,9 @@ where .table() .delete(req) .context("failed to delete request from table")?; - mem::replace(request.body.lock().unwrap().deref_mut(), Body::Consumed).drop(store) + mem::replace(request.body.lock().unwrap().deref_mut(), Body::Consumed) + .drop_with_store(store); + Ok(()) }) } } diff --git a/crates/wasi-http/src/p3/mod.rs b/crates/wasi-http/src/p3/mod.rs index 8b62c336e9..9c9fd75aff 100644 --- a/crates/wasi-http/src/p3/mod.rs +++ b/crates/wasi-http/src/p3/mod.rs @@ -283,7 +283,7 @@ where + 'static, { wasmtime_wasi::p3::clocks::add_to_linker(l)?; - wasmtime_wasi::p3::cli::add_stdio_to_linker(l)?; + wasmtime_wasi::p3::cli::add_to_linker(l)?; wasmtime_wasi::p3::random::add_to_linker(l)?; add_only_http_to_linker(l) diff --git a/crates/wasi-http/src/p3/response.rs b/crates/wasi-http/src/p3/response.rs index b306ba7e87..ae4a3a7004 100644 --- a/crates/wasi-http/src/p3/response.rs +++ b/crates/wasi-http/src/p3/response.rs @@ -11,7 +11,7 @@ use http_body_util::combinators::BoxBody; use http_body_util::{BodyExt, BodyStream, StreamBody}; use tokio::sync::{mpsc, oneshot}; use wasmtime::component::{ - Accessor, FutureReader, FutureWriter, Resource, StreamReader, WithAccessor, + Accessor, FutureReader, FutureWriter, GuardedStreamReader, Resource, StreamReader, }; use wasmtime::{AsContextMut, StoreContextMut}; use wasmtime_wasi::p3::{AbortOnDropHandle, ResourceView, WithChildren}; @@ -277,10 +277,10 @@ impl ResponseIo { T: ResourceView + 'static, { if let Some((contents, contents_tx)) = self.body.take() { - let mut contents = WithAccessor::new(store, contents); + let mut contents = GuardedStreamReader::new(store, contents); let mut rx_buffer = BytesMut::with_capacity(DEFAULT_BUFFER_CAPACITY); while !contents.is_closed() { - rx_buffer = contents.read(store, rx_buffer).await; + rx_buffer = contents.read(rx_buffer).await; let buffer = rx_buffer.split(); if !buffer.is_empty() { if let Err(..) = contents_tx.send(buffer.freeze()).await { diff --git a/crates/wasi-http/src/types.rs b/crates/wasi-http/src/types.rs index a0536d8835..d865e36ed7 100644 --- a/crates/wasi-http/src/types.rs +++ b/crates/wasi-http/src/types.rs @@ -1,12 +1,9 @@ //! Implements the base structure (i.e. [WasiHttpCtx]) that will provide the //! implementation of the wasi-http API. -use crate::io::TokioIo; use crate::{ bindings::http::types::{self, Method, Scheme}, body::{HostIncomingBody, HyperIncomingBody, HyperOutgoingBody}, - error::dns_error, - hyper_request_error, }; use anyhow::bail; use bytes::Bytes; @@ -15,12 +12,18 @@ use hyper::body::Body; use hyper::header::HeaderName; use std::any::Any; use std::time::Duration; -use tokio::net::TcpStream; -use tokio::time::timeout; use wasmtime::component::{Resource, ResourceTable}; use wasmtime_wasi::p2::{IoImpl, IoView, Pollable}; use wasmtime_wasi::runtime::AbortOnDropJoinHandle; +#[cfg(feature = "default-send-request")] +use { + crate::io::TokioIo, + crate::{error::dns_error, hyper_request_error}, + tokio::net::TcpStream, + tokio::time::timeout, +}; + /// Capture the state necessary for use in the wasi-http API implementation. #[derive(Debug)] pub struct WasiHttpCtx { @@ -112,6 +115,7 @@ pub trait WasiHttpView: IoView { } /// Send an outgoing request. + #[cfg(feature = "default-send-request")] fn send_request( &mut self, request: hyper::Request, @@ -120,9 +124,17 @@ pub trait WasiHttpView: IoView { Ok(default_send_request(request, config)) } + /// Send an outgoing request. + #[cfg(not(feature = "default-send-request"))] + fn send_request( + &mut self, + request: hyper::Request, + config: OutgoingRequestConfig, + ) -> crate::HttpResult; + /// Whether a given header should be considered forbidden and not allowed. - fn is_forbidden_header(&mut self, _name: &HeaderName) -> bool { - false + fn is_forbidden_header(&mut self, name: &HeaderName) -> bool { + DEFAULT_FORBIDDEN_HEADERS.contains(name) } /// Number of distinct write calls to the outgoing body's output-stream @@ -269,22 +281,19 @@ impl WasiHttpView for WasiHttpImpl { } } -/// Returns `true` when the header is forbidden according to this [`WasiHttpView`] implementation. -pub(crate) fn is_forbidden_header(view: &mut dyn WasiHttpView, name: &HeaderName) -> bool { - static FORBIDDEN_HEADERS: [HeaderName; 9] = [ - hyper::header::CONNECTION, - HeaderName::from_static("keep-alive"), - hyper::header::PROXY_AUTHENTICATE, - hyper::header::PROXY_AUTHORIZATION, - HeaderName::from_static("proxy-connection"), - hyper::header::TRANSFER_ENCODING, - hyper::header::UPGRADE, - hyper::header::HOST, - HeaderName::from_static("http2-settings"), - ]; - - FORBIDDEN_HEADERS.contains(name) || view.is_forbidden_header(name) -} +/// Set of [http::header::HeaderName], that are forbidden by default +/// for requests and responses originating in the guest. +pub const DEFAULT_FORBIDDEN_HEADERS: [http::header::HeaderName; 9] = [ + hyper::header::CONNECTION, + HeaderName::from_static("keep-alive"), + hyper::header::PROXY_AUTHENTICATE, + hyper::header::PROXY_AUTHORIZATION, + HeaderName::from_static("proxy-connection"), + hyper::header::TRANSFER_ENCODING, + hyper::header::UPGRADE, + hyper::header::HOST, + HeaderName::from_static("http2-settings"), +]; /// Removes forbidden headers from a [`hyper::HeaderMap`]. pub(crate) fn remove_forbidden_headers( @@ -292,7 +301,7 @@ pub(crate) fn remove_forbidden_headers( headers: &mut hyper::HeaderMap, ) { let forbidden_keys = Vec::from_iter(headers.keys().filter_map(|name| { - if is_forbidden_header(view, name) { + if view.is_forbidden_header(name) { Some(name.clone()) } else { None @@ -320,6 +329,7 @@ pub struct OutgoingRequestConfig { /// /// This implementation is used by the `wasi:http/outgoing-handler` interface /// default implementation. +#[cfg(feature = "default-send-request")] pub fn default_send_request( request: hyper::Request, config: OutgoingRequestConfig, @@ -334,6 +344,7 @@ pub fn default_send_request( /// in a task. /// /// This is called from [default_send_request] to actually send the request. +#[cfg(feature = "default-send-request")] pub async fn default_send_request_handler( mut request: hyper::Request, OutgoingRequestConfig { diff --git a/crates/wasi-http/src/types_impl.rs b/crates/wasi-http/src/types_impl.rs index 02e6a5bb6a..9713a298aa 100644 --- a/crates/wasi-http/src/types_impl.rs +++ b/crates/wasi-http/src/types_impl.rs @@ -7,7 +7,7 @@ use crate::{ types::{ FieldMap, HostFields, HostFutureIncomingResponse, HostIncomingRequest, HostIncomingResponse, HostOutgoingRequest, HostOutgoingResponse, HostResponseOutparam, - is_forbidden_header, remove_forbidden_headers, + remove_forbidden_headers, }, }; use anyhow::{Context, anyhow}; @@ -125,7 +125,7 @@ where Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)), }; - if is_forbidden_header(self, &header) { + if self.is_forbidden_header(&header) { return Ok(Err(types::HeaderError::Forbidden)); } @@ -196,7 +196,7 @@ where Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)), }; - if is_forbidden_header(self, &header) { + if self.is_forbidden_header(&header) { return Ok(Err(types::HeaderError::Forbidden)); } @@ -228,7 +228,7 @@ where Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)), }; - if is_forbidden_header(self, &header) { + if self.is_forbidden_header(&header) { return Ok(Err(types::HeaderError::Forbidden)); } @@ -248,7 +248,7 @@ where Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)), }; - if is_forbidden_header(self, &header) { + if self.is_forbidden_header(&header) { return Ok(Err(types::HeaderError::Forbidden)); } diff --git a/crates/wasi-http/tests/all/p2/mod.rs b/crates/wasi-http/tests/all/p2/mod.rs index d3a2693e6b..9421714de1 100644 --- a/crates/wasi-http/tests/all/p2/mod.rs +++ b/crates/wasi-http/tests/all/p2/mod.rs @@ -72,7 +72,8 @@ impl WasiHttpView for Ctx { } fn is_forbidden_header(&mut self, name: &hyper::header::HeaderName) -> bool { - name.as_str() == "custom-forbidden-header" + types::DEFAULT_FORBIDDEN_HEADERS.contains(name) + || name.as_str() == "custom-forbidden-header" } } diff --git a/crates/wasi-http/tests/all/p3/incoming.rs b/crates/wasi-http/tests/all/p3/incoming.rs index a880bf9c6e..789bda1c74 100644 --- a/crates/wasi-http/tests/all/p3/incoming.rs +++ b/crates/wasi-http/tests/all/p3/incoming.rs @@ -4,7 +4,6 @@ use http_body::Body; use http_body_util::{BodyExt as _, Collected, Empty}; use wasmtime::Store; use wasmtime::component::{Component, Linker}; -use wasmtime_wasi::p3::cli::WasiCliCtx; use wasmtime_wasi_http::p3::bindings::Proxy; use wasmtime_wasi_http::p3::bindings::http::types::ErrorCode; use wasmtime_wasi_http::p3::{Response, WasiHttpCtx}; @@ -26,9 +25,6 @@ pub async fn run_wasi_http + 'static>( let mut store = Store::new( &engine, Ctx { - cli: WasiCliCtx { - ..WasiCliCtx::default() - }, http: WasiHttpCtx { client }, ..Ctx::default() }, diff --git a/crates/wasi-http/tests/all/p3/mod.rs b/crates/wasi-http/tests/all/p3/mod.rs index f3c26b6e21..77216be6ae 100644 --- a/crates/wasi-http/tests/all/p3/mod.rs +++ b/crates/wasi-http/tests/all/p3/mod.rs @@ -3,13 +3,9 @@ use core::future::Future; use bytes::Bytes; use wasmtime::Store; use wasmtime::component::{Component, Linker, ResourceTable}; -use wasmtime_wasi::clocks::{WasiClocksCtx, WasiClocksView}; use wasmtime_wasi::p2::{IoView, WasiCtx, WasiCtxBuilder, WasiView}; use wasmtime_wasi::p3::ResourceView; -use wasmtime_wasi::p3::cli::{WasiCliCtx, WasiCliView}; use wasmtime_wasi::p3::filesystem::{WasiFilesystemCtx, WasiFilesystemView}; -use wasmtime_wasi::p3::sockets::{WasiSocketsCtx, WasiSocketsView}; -use wasmtime_wasi::random::{WasiRandomCtx, WasiRandomView}; use wasmtime_wasi_http::p3::bindings::http::types::ErrorCode; use wasmtime_wasi_http::p3::{ Client, DEFAULT_FORBIDDEN_HEADERS, RequestOptions, WasiHttpCtx, WasiHttpView, @@ -21,9 +17,7 @@ mod outgoing; mod proxy; struct Ctx { - cli: WasiCliCtx, filesystem: WasiFilesystemCtx, - sockets: WasiSocketsCtx, table: ResourceTable, wasip2: WasiCtx, wasip3: wasmtime_wasi::p3::WasiCtx, @@ -36,9 +30,7 @@ where { fn default() -> Self { Self { - cli: WasiCliCtx::default(), filesystem: WasiFilesystemCtx::default(), - sockets: WasiSocketsCtx::default(), table: ResourceTable::default(), wasip2: WasiCtxBuilder::new().inherit_stdio().build(), wasip3: wasmtime_wasi::p3::WasiCtxBuilder::new() @@ -62,8 +54,11 @@ impl IoView for Ctx { } impl wasmtime_wasi::p3::WasiView for Ctx { - fn ctx(&mut self) -> &mut wasmtime_wasi::p3::WasiCtx { - &mut self.wasip3 + fn ctx(&mut self) -> wasmtime_wasi::p3::WasiCtxView<'_> { + wasmtime_wasi::p3::WasiCtxView { + ctx: &mut self.wasip3, + table: &mut self.table, + } } } @@ -73,36 +68,12 @@ impl ResourceView for Ctx { } } -impl WasiCliView for Ctx { - fn cli(&mut self) -> &WasiCliCtx { - &self.cli - } -} - -impl WasiClocksView for Ctx { - fn clocks(&mut self) -> &WasiClocksCtx { - &self.wasip3.clocks - } -} - impl WasiFilesystemView for Ctx { fn filesystem(&self) -> &WasiFilesystemCtx { &self.filesystem } } -impl WasiRandomView for Ctx { - fn random(&mut self) -> &mut WasiRandomCtx { - &mut self.wasip3.random - } -} - -impl WasiSocketsView for Ctx { - fn sockets(&self) -> &WasiSocketsCtx { - &self.sockets - } -} - impl WasiHttpView for Ctx { type Client = C; diff --git a/crates/wasi-http/tests/all/p3/outgoing.rs b/crates/wasi-http/tests/all/p3/outgoing.rs index 7d7b95123f..ded44b157e 100644 --- a/crates/wasi-http/tests/all/p3/outgoing.rs +++ b/crates/wasi-http/tests/all/p3/outgoing.rs @@ -20,10 +20,9 @@ async fn run(path: &str, server: &Server) -> anyhow::Result<()> { let mut store = Store::new( &engine, Ctx { - cli: WasiCliCtx { - environment: vec![("HTTP_SERVER".into(), server.addr())], - ..WasiCliCtx::default() - }, + wasip3: wasmtime_wasi::p3::WasiCtxBuilder::new() + .env("HTTP_SERVER", server.addr()) + .build(), ..Ctx::::default() }, ); diff --git a/crates/wasi-io/src/bindings.rs b/crates/wasi-io/src/bindings.rs index b90c5a75f0..74ab41ab92 100644 --- a/crates/wasi-io/src/bindings.rs +++ b/crates/wasi-io/src/bindings.rs @@ -1,27 +1,24 @@ wasmtime::component::bindgen!({ path: "wit", - trappable_imports: true, with: { "wasi:io/poll/pollable": crate::poll::DynPollable, "wasi:io/streams/input-stream": crate::streams::DynInputStream, "wasi:io/streams/output-stream": crate::streams::DynOutputStream, "wasi:io/error/error": crate::streams::Error, }, - async: { - only_imports: [ - "poll", - "[method]pollable.block", - "[method]pollable.ready", - "[method]input-stream.blocking-read", - "[method]input-stream.blocking-skip", - "[drop]input-stream", - "[method]output-stream.blocking-splice", - "[method]output-stream.blocking-flush", - "[method]output-stream.blocking-write", - "[method]output-stream.blocking-write-and-flush", - "[method]output-stream.blocking-write-zeroes-and-flush", - "[drop]output-stream", - ] + imports: { + "wasi:io/poll/poll": async | trappable, + "wasi:io/poll/[method]pollable.block": async | trappable, + "wasi:io/poll/[method]pollable.ready": async | trappable, + "wasi:io/streams/[method]input-stream.blocking-read": async | trappable, + "wasi:io/streams/[method]input-stream.blocking-skip": async | trappable, + "wasi:io/streams/[drop]input-stream": async | trappable, + "wasi:io/streams/[method]output-stream.blocking-splice": async | trappable, + "wasi:io/streams/[method]output-stream.blocking-flush": async | trappable, + "wasi:io/streams/[method]output-stream.blocking-write-and-flush": async | trappable, + "wasi:io/streams/[method]output-stream.blocking-write-zeroes-and-flush": async | trappable, + "wasi:io/streams/[drop]output-stream": async | trappable, + default: trappable, }, trappable_error_type: { "wasi:io/streams/stream-error" => crate::streams::StreamError, diff --git a/crates/wasi-keyvalue/src/lib.rs b/crates/wasi-keyvalue/src/lib.rs index 9dab4b7a34..303ab783ec 100644 --- a/crates/wasi-keyvalue/src/lib.rs +++ b/crates/wasi-keyvalue/src/lib.rs @@ -71,7 +71,7 @@ mod generated { wasmtime::component::bindgen!({ path: "wit", world: "wasi:keyvalue/imports", - trappable_imports: true, + imports: { default: trappable }, with: { "wasi:keyvalue/store/bucket": crate::Bucket, }, diff --git a/crates/wasi-nn/src/wit.rs b/crates/wasi-nn/src/wit.rs index 67fb98c786..a5cd9812bf 100644 --- a/crates/wasi-nn/src/wit.rs +++ b/crates/wasi-nn/src/wit.rs @@ -118,7 +118,6 @@ pub(crate) mod generated_ { wasmtime::component::bindgen!({ world: "ml", path: "wit/wasi-nn.wit", - trappable_imports: true, with: { // Configure all WIT http resources to be defined types in this // crate to use the `ResourceTable` helper methods. @@ -127,6 +126,7 @@ pub(crate) mod generated_ { "wasi:nn/inference/graph-execution-context": crate::ExecutionContext, "wasi:nn/errors/error": super::Error, }, + imports: { default: trappable }, trappable_error_type: { "wasi:nn/errors/error" => super::Error, }, diff --git a/crates/wasi-preview1-component-adapter/src/descriptors.rs b/crates/wasi-preview1-component-adapter/src/descriptors.rs index 47d3439560..da3dc6e705 100644 --- a/crates/wasi-preview1-component-adapter/src/descriptors.rs +++ b/crates/wasi-preview1-component-adapter/src/descriptors.rs @@ -330,8 +330,8 @@ impl Descriptors { .ok_or(wasi::ERRNO_BADF) } - // Internal: close a fd, returning the descriptor. - fn close_(&mut self, fd: Fd) -> Result { + // Close an fd. + pub fn close(&mut self, fd: Fd) -> Result<(), Errno> { // Throw an error if closing an fd which is already closed match self.get(fd)? { Descriptor::Closed(_) => Err(wasi::ERRNO_BADF)?, @@ -341,12 +341,7 @@ impl Descriptors { let last_closed = self.closed; let prev = std::mem::replace(self.get_mut(fd)?, Descriptor::Closed(last_closed)); self.closed = Some(fd); - Ok(prev) - } - - // Close an fd. - pub fn close(&mut self, fd: Fd) -> Result<(), Errno> { - drop(self.close_(fd)?); + drop(prev); Ok(()) } @@ -366,11 +361,20 @@ impl Descriptors { while self.table_len.get() as u32 <= to_fd { self.push_closed()?; } - // Then, close from_fd and put its contents into to_fd: - let desc = self.close_(from_fd)?; - // TODO FIXME if this overwrites a preopen, do we need to clear it from the preopen table? - *self.get_mut(to_fd)? = desc; - + // Throw an error if renumbering a closed fd + match self.get(from_fd)? { + Descriptor::Closed(_) => Err(wasi::ERRNO_BADF)?, + _ => {} + } + // Close from_fd and put its contents into to_fd + if from_fd != to_fd { + // Mutate the descriptor to be closed, and push the closed fd onto the head of the linked list: + let last_closed = self.closed; + let desc = std::mem::replace(self.get_mut(from_fd)?, Descriptor::Closed(last_closed)); + self.closed = Some(from_fd); + // TODO FIXME if this overwrites a preopen, do we need to clear it from the preopen table? + *self.get_mut(to_fd)? = desc; + } Ok(()) } diff --git a/crates/wasi-tls/src/bindings.rs b/crates/wasi-tls/src/bindings.rs index 355034ee51..03f8e72caf 100644 --- a/crates/wasi-tls/src/bindings.rs +++ b/crates/wasi-tls/src/bindings.rs @@ -11,10 +11,8 @@ mod generated { "wasi:tls/types/client-handshake": crate::HostClientHandshake, "wasi:tls/types/future-client-streams": crate::HostFutureClientStreams, }, - trappable_imports: true, - async: { - only_imports: [], - } + imports: { default: trappable }, + require_store_data_send: true, }); } diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml index 86c8da0570..50cf35cc31 100644 --- a/crates/wasi/Cargo.toml +++ b/crates/wasi/Cargo.toml @@ -38,7 +38,6 @@ futures = { workspace = true } url = { workspace = true } [dev-dependencies] -env_logger = { workspace = true } tokio = { workspace = true, features = ["time", "sync", "io-std", "io-util", "rt", "rt-multi-thread", "net", "macros", "fs"] } test-log = { workspace = true } tracing-subscriber = { workspace = true } @@ -46,6 +45,7 @@ test-programs-artifacts = { workspace = true } tempfile = { workspace = true } wasmtime = { workspace = true, features = ['cranelift', 'incremental-cache'] } wasmtime-test-util = { workspace = true } +env_logger = { workspace = true } [target.'cfg(unix)'.dependencies] rustix = { workspace = true, features = ["event", "fs", "net"] } diff --git a/crates/wasi/src/cli.rs b/crates/wasi/src/cli.rs new file mode 100644 index 0000000000..3769a11d08 --- /dev/null +++ b/crates/wasi/src/cli.rs @@ -0,0 +1,95 @@ +use std::rc::Rc; +use std::sync::Arc; + +#[derive(Default)] +pub struct WasiCliCtx { + pub environment: Vec<(String, String)>, + pub arguments: Vec, + pub initial_cwd: Option, + pub stdin: I, + pub stdout: O, + pub stderr: O, +} + +pub trait IsTerminal { + /// Returns whether this stream is backed by a TTY. + fn is_terminal(&self) -> bool; +} + +impl IsTerminal for &T { + fn is_terminal(&self) -> bool { + T::is_terminal(self) + } +} + +impl IsTerminal for &mut T { + fn is_terminal(&self) -> bool { + T::is_terminal(self) + } +} + +impl IsTerminal for Box { + fn is_terminal(&self) -> bool { + T::is_terminal(self) + } +} + +impl IsTerminal for Rc { + fn is_terminal(&self) -> bool { + T::is_terminal(self) + } +} + +impl IsTerminal for Arc { + fn is_terminal(&self) -> bool { + T::is_terminal(self) + } +} + +impl IsTerminal for tokio::io::Empty { + fn is_terminal(&self) -> bool { + false + } +} + +impl IsTerminal for std::io::Empty { + fn is_terminal(&self) -> bool { + false + } +} + +impl IsTerminal for tokio::io::Stdin { + fn is_terminal(&self) -> bool { + std::io::stdin().is_terminal() + } +} + +impl IsTerminal for std::io::Stdin { + fn is_terminal(&self) -> bool { + std::io::IsTerminal::is_terminal(self) + } +} + +impl IsTerminal for tokio::io::Stdout { + fn is_terminal(&self) -> bool { + std::io::stdout().is_terminal() + } +} + +impl IsTerminal for std::io::Stdout { + fn is_terminal(&self) -> bool { + std::io::IsTerminal::is_terminal(self) + } +} + +impl IsTerminal for tokio::io::Stderr { + fn is_terminal(&self) -> bool { + std::io::stderr().is_terminal() + } +} + +impl IsTerminal for std::io::Stderr { + fn is_terminal(&self) -> bool { + std::io::IsTerminal::is_terminal(self) + } +} diff --git a/crates/wasi/src/clocks.rs b/crates/wasi/src/clocks.rs index 2618655424..61fd630dea 100644 --- a/crates/wasi/src/clocks.rs +++ b/crates/wasi/src/clocks.rs @@ -2,31 +2,6 @@ use cap_std::time::{Duration, Instant, SystemClock}; use cap_std::{AmbientAuthority, ambient_authority}; use cap_time_ext::{MonotonicClockExt as _, SystemClockExt as _}; -#[repr(transparent)] -pub struct WasiClocksImpl(pub T); - -impl WasiClocksView for &mut T { - fn clocks(&mut self) -> &WasiClocksCtx { - (**self).clocks() - } -} - -impl WasiClocksView for WasiClocksImpl { - fn clocks(&mut self) -> &WasiClocksCtx { - self.0.clocks() - } -} - -impl WasiClocksView for WasiClocksCtx { - fn clocks(&mut self) -> &WasiClocksCtx { - self - } -} - -pub trait WasiClocksView: Send { - fn clocks(&mut self) -> &WasiClocksCtx; -} - pub struct WasiClocksCtx { pub wall_clock: Box, pub monotonic_clock: Box, @@ -41,6 +16,16 @@ impl Default for WasiClocksCtx { } } +pub trait WasiClocksView: Send { + fn clocks(&mut self) -> &mut WasiClocksCtx; +} + +impl WasiClocksView for WasiClocksCtx { + fn clocks(&mut self) -> &mut WasiClocksCtx { + self + } +} + pub trait HostWallClock: Send { fn resolution(&self) -> Duration; fn now(&self) -> Duration; diff --git a/crates/wasi/src/ctx.rs b/crates/wasi/src/ctx.rs index 4e057996bf..a13adf7d6a 100644 --- a/crates/wasi/src/ctx.rs +++ b/crates/wasi/src/ctx.rs @@ -1,6 +1,7 @@ +use crate::cli::WasiCliCtx; use crate::clocks::{HostMonotonicClock, HostWallClock, WasiClocksCtx}; -use crate::net::{SocketAddrCheck, SocketAddrUse}; use crate::random::WasiRandomCtx; +use crate::sockets::{SocketAddrCheck, SocketAddrUse, WasiSocketsCtx}; use cap_rand::RngCore; use std::future::Future; use std::net::SocketAddr; @@ -16,17 +17,16 @@ use std::sync::Arc; /// [p2::WasiCtxBuilder](crate::p2::WasiCtxBuilder) /// /// [`Store`]: wasmtime::Store -pub(crate) struct WasiCtxBuilder { - pub(crate) env: Vec<(String, String)>, - pub(crate) args: Vec, - pub(crate) random: WasiRandomCtx, +#[derive(Default)] +pub(crate) struct WasiCtxBuilder { + pub(crate) cli: WasiCliCtx, pub(crate) clocks: WasiClocksCtx, - pub(crate) socket_addr_check: SocketAddrCheck, - pub(crate) allowed_network_uses: AllowedNetworkUses, + pub(crate) random: WasiRandomCtx, + pub(crate) sockets: WasiSocketsCtx, pub(crate) allow_blocking_current_thread: bool, } -impl WasiCtxBuilder { +impl WasiCtxBuilder { /// Creates a builder for a new context with default parameters set. /// /// The current defaults are: @@ -44,20 +44,45 @@ impl WasiCtxBuilder { /// /// These defaults can all be updated via the various builder configuration /// methods below. - pub(crate) fn new() -> Self { - let random = WasiRandomCtx::default(); + pub(crate) fn new(stdin: I, stdout: O, stderr: O) -> Self { + let cli = WasiCliCtx { + environment: Vec::default(), + arguments: Vec::default(), + initial_cwd: None, + stdin, + stdout, + stderr, + }; let clocks = WasiClocksCtx::default(); + let random = WasiRandomCtx::default(); + let sockets = WasiSocketsCtx::default(); Self { - env: Vec::new(), - args: Vec::new(), - random, + cli, clocks, - socket_addr_check: SocketAddrCheck::default(), - allowed_network_uses: AllowedNetworkUses::default(), + random, + sockets, allow_blocking_current_thread: false, } } + /// Provides a custom implementation of stdin to use. + pub fn stdin(&mut self, stdin: I) -> &mut Self { + self.cli.stdin = stdin; + self + } + + /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stdout. + pub fn stdout(&mut self, stdout: O) -> &mut Self { + self.cli.stdout = stdout; + self + } + + /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stderr. + pub fn stderr(&mut self, stderr: O) -> &mut Self { + self.cli.stderr = stderr; + self + } + /// Configures whether or not blocking operations made through this /// `WasiCtx` are allowed to block the current thread. /// @@ -97,7 +122,7 @@ impl WasiCtxBuilder { /// At this time environment variables are not deduplicated and if the same /// key is set twice then the guest will see two entries for the same key. pub fn envs(&mut self, env: &[(impl AsRef, impl AsRef)]) -> &mut Self { - self.env.extend( + self.cli.environment.extend( env.iter() .map(|(k, v)| (k.as_ref().to_owned(), v.as_ref().to_owned())), ); @@ -109,7 +134,8 @@ impl WasiCtxBuilder { /// At this time environment variables are not deduplicated and if the same /// key is set twice then the guest will see two entries for the same key. pub fn env(&mut self, k: impl AsRef, v: impl AsRef) -> &mut Self { - self.env + self.cli + .environment .push((k.as_ref().to_owned(), v.as_ref().to_owned())); self } @@ -125,13 +151,15 @@ impl WasiCtxBuilder { /// Appends a list of arguments to the argument array to pass to wasm. pub fn args(&mut self, args: &[impl AsRef]) -> &mut Self { - self.args.extend(args.iter().map(|a| a.as_ref().to_owned())); + self.cli + .arguments + .extend(args.iter().map(|a| a.as_ref().to_owned())); self } /// Appends a single argument to get passed to wasm. pub fn arg(&mut self, arg: impl AsRef) -> &mut Self { - self.args.push(arg.as_ref().to_owned()); + self.cli.arguments.push(arg.as_ref().to_owned()); self } @@ -212,7 +240,7 @@ impl WasiCtxBuilder { + Sync + 'static, { - self.socket_addr_check = SocketAddrCheck(Arc::new(check)); + self.sockets.socket_addr_check = SocketAddrCheck(Arc::new(check)); self } @@ -220,7 +248,7 @@ impl WasiCtxBuilder { /// /// By default this is disabled. pub fn allow_ip_name_lookup(&mut self, enable: bool) -> &mut Self { - self.allowed_network_uses.ip_name_lookup = enable; + self.sockets.allowed_network_uses.ip_name_lookup = enable; self } @@ -229,7 +257,7 @@ impl WasiCtxBuilder { /// This is enabled by default, but can be disabled if UDP should be blanket /// disabled. pub fn allow_udp(&mut self, enable: bool) -> &mut Self { - self.allowed_network_uses.udp = enable; + self.sockets.allowed_network_uses.udp = enable; self } @@ -238,47 +266,7 @@ impl WasiCtxBuilder { /// This is enabled by default, but can be disabled if TCP should be blanket /// disabled. pub fn allow_tcp(&mut self, enable: bool) -> &mut Self { - self.allowed_network_uses.tcp = enable; + self.sockets.allowed_network_uses.tcp = enable; self } } - -pub struct AllowedNetworkUses { - pub ip_name_lookup: bool, - pub udp: bool, - pub tcp: bool, -} - -impl Default for AllowedNetworkUses { - fn default() -> Self { - Self { - ip_name_lookup: false, - udp: true, - tcp: true, - } - } -} - -impl AllowedNetworkUses { - pub(crate) fn check_allowed_udp(&self) -> std::io::Result<()> { - if !self.udp { - return Err(std::io::Error::new( - std::io::ErrorKind::PermissionDenied, - "UDP is not allowed", - )); - } - - Ok(()) - } - - pub(crate) fn check_allowed_tcp(&self) -> std::io::Result<()> { - if !self.tcp { - return Err(std::io::Error::new( - std::io::ErrorKind::PermissionDenied, - "TCP is not allowed", - )); - } - - Ok(()) - } -} diff --git a/crates/wasi/src/fs.rs b/crates/wasi/src/filesystem.rs similarity index 100% rename from crates/wasi/src/fs.rs rename to crates/wasi/src/filesystem.rs diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index 0fc8e6580d..53d098b388 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -12,11 +12,11 @@ //! //! For WASIp3, see [`p3`]. WASIp3 support is experimental, unstable and incomplete. +pub mod cli; pub mod clocks; mod ctx; mod error; -mod fs; -mod net; +mod filesystem; pub mod p2; #[cfg(feature = "p3")] pub mod p3; @@ -26,13 +26,14 @@ pub mod preview0; pub mod preview1; pub mod random; pub mod runtime; +pub mod sockets; pub use self::clocks::{HostMonotonicClock, HostWallClock}; pub(crate) use self::ctx::WasiCtxBuilder; pub use self::error::{I32Exit, TrappableError}; -pub use self::fs::{DirPerms, FilePerms, OpenMode}; -pub use self::net::{Network, SocketAddrUse}; +pub use self::filesystem::{DirPerms, FilePerms, OpenMode}; pub use self::random::{Deterministic, thread_rng}; +pub use self::sockets::{AllowedNetworkUses, SocketAddrUse}; #[doc(no_inline)] pub use async_trait::async_trait; #[doc(no_inline)] diff --git a/crates/wasi/src/net.rs b/crates/wasi/src/net.rs deleted file mode 100644 index d0d4aa374c..0000000000 --- a/crates/wasi/src/net.rs +++ /dev/null @@ -1,72 +0,0 @@ -use std::future::Future; -use std::net::SocketAddr; -use std::pin::Pin; -use std::sync::Arc; - -/// Value taken from rust std library. -pub const DEFAULT_TCP_BACKLOG: u32 = 128; - -pub struct Network { - pub socket_addr_check: SocketAddrCheck, - pub allow_ip_name_lookup: bool, -} - -impl Network { - pub async fn check_socket_addr( - &self, - addr: SocketAddr, - reason: SocketAddrUse, - ) -> std::io::Result<()> { - self.socket_addr_check.check(addr, reason).await - } -} - -/// A check that will be called for each socket address that is used of whether the address is permitted. -#[derive(Clone)] -pub struct SocketAddrCheck( - pub(crate) Arc< - dyn Fn(SocketAddr, SocketAddrUse) -> Pin + Send + Sync>> - + Send - + Sync, - >, -); - -impl SocketAddrCheck { - pub async fn check(&self, addr: SocketAddr, reason: SocketAddrUse) -> std::io::Result<()> { - if (self.0)(addr, reason).await { - Ok(()) - } else { - Err(std::io::Error::new( - std::io::ErrorKind::PermissionDenied, - "An address was not permitted by the socket address check.", - )) - } - } -} - -impl Default for SocketAddrCheck { - fn default() -> Self { - Self(Arc::new(|_, _| Box::pin(async { false }))) - } -} - -/// The reason what a socket address is being used for. -#[derive(Clone, Copy, Debug)] -pub enum SocketAddrUse { - /// Binding TCP socket - TcpBind, - /// Connecting TCP socket - TcpConnect, - /// Binding UDP socket - UdpBind, - /// Connecting UDP socket - UdpConnect, - /// Sending datagram on non-connected UDP socket - UdpOutgoingDatagram, -} - -#[derive(Copy, Clone)] -pub enum SocketAddressFamily { - Ipv4, - Ipv6, -} diff --git a/crates/wasi/src/p2/bindings.rs b/crates/wasi/src/p2/bindings.rs index 0f9dc33112..7cfdd1956e 100644 --- a/crates/wasi/src/p2/bindings.rs +++ b/crates/wasi/src/p2/bindings.rs @@ -39,7 +39,7 @@ //! with: { //! "wasi": wasmtime_wasi::p2::bindings, //! }, -//! async: true, +//! imports: { default: async }, //! }); //! //! struct MyState { @@ -152,13 +152,12 @@ pub mod sync { wasmtime::component::bindgen!({ path: "src/p2/wit", world: "wasi:cli/command", - tracing: true, trappable_error_type: { "wasi:io/streams/stream-error" => StreamError, "wasi:filesystem/types/error-code" => FsError, "wasi:sockets/network/error-code" => SocketError, }, - trappable_imports: true, + imports: { default: tracing | trappable }, with: { // These interfaces contain only synchronous methods, so they // can be aliased directly @@ -341,9 +340,7 @@ mod async_io { wasmtime::component::bindgen!({ path: "src/p2/wit", world: "wasi:cli/command", - tracing: true, - trappable_imports: true, - async: { + imports: { // Only these functions are `async` and everything else is sync // meaning that it basically doesn't need to block. These functions // are the only ones that need to block. @@ -351,59 +348,39 @@ mod async_io { // Note that at this time `only_imports` works on function names // which in theory can be shared across interfaces, so this may // need fancier syntax in the future. - only_imports: [ - "[method]descriptor.access-at", - "[method]descriptor.advise", - "[method]descriptor.change-directory-permissions-at", - "[method]descriptor.change-file-permissions-at", - "[method]descriptor.create-directory-at", - "[method]descriptor.get-flags", - "[method]descriptor.get-type", - "[method]descriptor.is-same-object", - "[method]descriptor.link-at", - "[method]descriptor.lock-exclusive", - "[method]descriptor.lock-shared", - "[method]descriptor.metadata-hash", - "[method]descriptor.metadata-hash-at", - "[method]descriptor.open-at", - "[method]descriptor.read", - "[method]descriptor.read-directory", - "[method]descriptor.readlink-at", - "[method]descriptor.remove-directory-at", - "[method]descriptor.rename-at", - "[method]descriptor.set-size", - "[method]descriptor.set-times", - "[method]descriptor.set-times-at", - "[method]descriptor.stat", - "[method]descriptor.stat-at", - "[method]descriptor.symlink-at", - "[method]descriptor.sync", - "[method]descriptor.sync-data", - "[method]descriptor.try-lock-exclusive", - "[method]descriptor.try-lock-shared", - "[method]descriptor.unlink-file-at", - "[method]descriptor.unlock", - "[method]descriptor.write", - "[method]input-stream.blocking-read", - "[method]input-stream.blocking-skip", - "[drop]input-stream", - "[method]output-stream.blocking-splice", - "[method]output-stream.blocking-flush", - "[method]output-stream.blocking-write", - "[method]output-stream.blocking-write-and-flush", - "[method]output-stream.blocking-write-zeroes-and-flush", - "[drop]output-stream", - "[method]directory-entry-stream.read-directory-entry", - "poll", - "[method]pollable.block", - "[method]pollable.ready", - "[method]tcp-socket.start-bind", - "[method]tcp-socket.start-connect", - "[method]udp-socket.start-bind", - "[method]udp-socket.stream", - "[method]outgoing-datagram-stream.send", - ], + "wasi:filesystem/types/[method]descriptor.advise": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.create-directory-at": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.get-flags": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.get-type": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.is-same-object": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.link-at": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.metadata-hash": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.metadata-hash-at": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.open-at": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.read": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.read-directory": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.readlink-at": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.remove-directory-at": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.rename-at": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.set-size": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.set-times": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.set-times-at": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.stat": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.stat-at": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.symlink-at": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.sync": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.sync-data": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.unlink-file-at": async | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.write": async | tracing | trappable, + "wasi:filesystem/types/[method]directory-entry-stream.read-directory-entry": async | tracing | trappable, + "wasi:sockets/tcp/[method]tcp-socket.start-bind": async | tracing | trappable, + "wasi:sockets/tcp/[method]tcp-socket.start-connect": async | tracing | trappable, + "wasi:sockets/udp/[method]udp-socket.start-bind": async | tracing | trappable, + "wasi:sockets/udp/[method]udp-socket.stream": async | tracing | trappable, + "wasi:sockets/udp/[method]outgoing-datagram-stream.send": async | tracing | trappable, + default: tracing | trappable, }, + exports: { default: async }, trappable_error_type: { "wasi:io/streams/stream-error" => wasmtime_wasi_io::streams::StreamError, "wasi:filesystem/types/error-code" => crate::p2::FsError, @@ -419,7 +396,7 @@ mod async_io { // Configure all other resources to be concrete types defined in // this crate - "wasi:sockets/network/network": crate::net::Network, + "wasi:sockets/network/network": crate::p2::network::Network, "wasi:sockets/tcp/tcp-socket": crate::p2::tcp::TcpSocket, "wasi:sockets/udp/udp-socket": crate::p2::udp::UdpSocket, "wasi:sockets/udp/incoming-datagram-stream": crate::p2::udp::IncomingDatagramStream, diff --git a/crates/wasi/src/p2/ctx.rs b/crates/wasi/src/p2/ctx.rs index 11a12f5770..2ef7fe41a6 100644 --- a/crates/wasi/src/p2/ctx.rs +++ b/crates/wasi/src/p2/ctx.rs @@ -1,10 +1,10 @@ +use crate::cli::WasiCliCtx; use crate::clocks::{HostMonotonicClock, HostWallClock, WasiClocksCtx}; -use crate::ctx::AllowedNetworkUses; -use crate::net::{SocketAddrCheck, SocketAddrUse}; use crate::p2::filesystem::Dir; use crate::p2::pipe; use crate::p2::stdio::{self, StdinStream, StdoutStream}; use crate::random::WasiRandomCtx; +use crate::sockets::{AllowedNetworkUses, SocketAddrCheck, SocketAddrUse, WasiSocketsCtx}; use crate::{DirPerms, FilePerms, OpenMode}; use anyhow::Result; use cap_rand::RngCore; @@ -36,10 +36,7 @@ use std::pin::Pin; /// /// [`Store`]: wasmtime::Store pub struct WasiCtxBuilder { - common: crate::WasiCtxBuilder, - stdin: Box, - stdout: Box, - stderr: Box, + common: crate::WasiCtxBuilder, Box>, preopens: Vec<(Dir, String)>, built: bool, } @@ -64,10 +61,11 @@ impl WasiCtxBuilder { /// methods below. pub fn new() -> Self { Self { - common: crate::WasiCtxBuilder::new(), - stdin: Box::new(pipe::ClosedInputStream), - stdout: Box::new(pipe::SinkOutputStream), - stderr: Box::new(pipe::SinkOutputStream), + common: crate::WasiCtxBuilder::new( + Box::new(pipe::ClosedInputStream), + Box::new(pipe::SinkOutputStream), + Box::new(pipe::SinkOutputStream), + ), preopens: Vec::new(), built: false, } @@ -88,19 +86,19 @@ impl WasiCtxBuilder { /// Note that inheriting the process's stdin can also be done through /// [`inherit_stdin`](WasiCtxBuilder::inherit_stdin). pub fn stdin(&mut self, stdin: impl StdinStream + 'static) -> &mut Self { - self.stdin = Box::new(stdin); + self.common.stdin(Box::new(stdin)); self } /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stdout. pub fn stdout(&mut self, stdout: impl StdoutStream + 'static) -> &mut Self { - self.stdout = Box::new(stdout); + self.common.stdout(Box::new(stdout)); self } /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stderr. pub fn stderr(&mut self, stderr: impl StdoutStream + 'static) -> &mut Self { - self.stderr = Box::new(stderr); + self.common.stderr(Box::new(stderr)); self } @@ -436,26 +434,28 @@ impl WasiCtxBuilder { let Self { common: crate::WasiCtxBuilder { - env, - args, - random: - WasiRandomCtx { - random, - insecure_random, - insecure_random_seed, + cli: + WasiCliCtx { + environment: env, + arguments: args, + initial_cwd: _, + stdin, + stdout, + stderr, }, clocks: WasiClocksCtx { wall_clock, monotonic_clock, }, - socket_addr_check, - allowed_network_uses, + random, + sockets: + WasiSocketsCtx { + socket_addr_check, + allowed_network_uses, + }, allow_blocking_current_thread, }, - stdin, - stdout, - stderr, preopens, built: _, } = mem::replace(self, Self::new()); @@ -470,8 +470,6 @@ impl WasiCtxBuilder { preopens, socket_addr_check, random, - insecure_random, - insecure_random_seed, wall_clock, monotonic_clock, allowed_network_uses, @@ -547,9 +545,7 @@ impl WasiCtxBuilder { /// } /// ``` pub struct WasiCtx { - pub(crate) random: Box, - pub(crate) insecure_random: Box, - pub(crate) insecure_random_seed: u128, + pub(crate) random: WasiRandomCtx, pub(crate) wall_clock: Box, pub(crate) monotonic_clock: Box, pub(crate) env: Vec<(String, String)>, diff --git a/crates/wasi/src/p2/host/instance_network.rs b/crates/wasi/src/p2/host/instance_network.rs index 40af0d3454..85c5a78018 100644 --- a/crates/wasi/src/p2/host/instance_network.rs +++ b/crates/wasi/src/p2/host/instance_network.rs @@ -1,5 +1,5 @@ -use crate::net::Network; use crate::p2::bindings::sockets::instance_network; +use crate::p2::network::Network; use crate::p2::{IoView, WasiImpl, WasiView}; use wasmtime::component::Resource; diff --git a/crates/wasi/src/p2/host/network.rs b/crates/wasi/src/p2/host/network.rs index 2b316daa48..30059aed83 100644 --- a/crates/wasi/src/p2/host/network.rs +++ b/crates/wasi/src/p2/host/network.rs @@ -2,8 +2,8 @@ use crate::p2::bindings::sockets::network::{ self, ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Ipv4SocketAddress, Ipv6SocketAddress, }; -use crate::p2::network::{from_ipv4_addr, from_ipv6_addr, to_ipv4_addr, to_ipv6_addr}; use crate::p2::{IoView, SocketError, WasiImpl, WasiView}; +use crate::sockets::util::{from_ipv4_addr, from_ipv6_addr, to_ipv4_addr, to_ipv6_addr}; use anyhow::Error; use rustix::io::Errno; use std::io; @@ -242,331 +242,3 @@ impl From for IpAddressFamily { } } } - -pub(crate) mod util { - use std::io; - use std::net::{IpAddr, Ipv6Addr, SocketAddr}; - use std::time::Duration; - - use crate::net::SocketAddressFamily; - use cap_net_ext::{AddressFamily, Blocking, UdpSocketExt}; - use rustix::fd::{AsFd, OwnedFd}; - use rustix::io::Errno; - use rustix::net::sockopt; - - pub fn validate_unicast(addr: &SocketAddr) -> io::Result<()> { - match to_canonical(&addr.ip()) { - IpAddr::V4(ipv4) => { - if ipv4.is_multicast() || ipv4.is_broadcast() { - Err(io::Error::new( - io::ErrorKind::InvalidInput, - "Both IPv4 broadcast and multicast addresses are not supported", - )) - } else { - Ok(()) - } - } - IpAddr::V6(ipv6) => { - if ipv6.is_multicast() { - Err(io::Error::new( - io::ErrorKind::InvalidInput, - "IPv6 multicast addresses are not supported", - )) - } else { - Ok(()) - } - } - } - } - - pub fn validate_remote_address(addr: &SocketAddr) -> io::Result<()> { - if to_canonical(&addr.ip()).is_unspecified() { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "Remote address may not be `0.0.0.0` or `::`", - )); - } - - if addr.port() == 0 { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "Remote port may not be 0", - )); - } - - Ok(()) - } - - pub fn validate_address_family( - addr: &SocketAddr, - socket_family: &SocketAddressFamily, - ) -> io::Result<()> { - match (socket_family, addr.ip()) { - (SocketAddressFamily::Ipv4, IpAddr::V4(_)) => Ok(()), - (SocketAddressFamily::Ipv6, IpAddr::V6(ipv6)) => { - if is_deprecated_ipv4_compatible(&ipv6) { - // Reject IPv4-*compatible* IPv6 addresses. They have been deprecated - // since 2006, OS handling of them is inconsistent and our own - // validations don't take them into account either. - // Note that these are not the same as IPv4-*mapped* IPv6 addresses. - Err(io::Error::new( - io::ErrorKind::InvalidInput, - "IPv4-compatible IPv6 addresses are not supported", - )) - } else if ipv6.to_ipv4_mapped().is_some() { - Err(io::Error::new( - io::ErrorKind::InvalidInput, - "IPv4-mapped IPv6 address passed to an IPv6-only socket", - )) - } else { - Ok(()) - } - } - _ => Err(io::Error::new( - io::ErrorKind::InvalidInput, - "Address family mismatch", - )), - } - } - - // Can be removed once `IpAddr::to_canonical` becomes stable. - pub fn to_canonical(addr: &IpAddr) -> IpAddr { - match addr { - IpAddr::V4(ipv4) => IpAddr::V4(*ipv4), - IpAddr::V6(ipv6) => { - if let Some(ipv4) = ipv6.to_ipv4_mapped() { - IpAddr::V4(ipv4) - } else { - IpAddr::V6(*ipv6) - } - } - } - } - - fn is_deprecated_ipv4_compatible(addr: &Ipv6Addr) -> bool { - matches!(addr.segments(), [0, 0, 0, 0, 0, 0, _, _]) - && *addr != Ipv6Addr::UNSPECIFIED - && *addr != Ipv6Addr::LOCALHOST - } - - /* - * Syscalls wrappers with (opinionated) portability fixes. - */ - - pub fn udp_socket(family: AddressFamily, blocking: Blocking) -> io::Result { - // Delegate socket creation to cap_net_ext. They handle a couple of things for us: - // - On Windows: call WSAStartup if not done before. - // - Set the NONBLOCK and CLOEXEC flags. Either immediately during socket creation, - // or afterwards using ioctl or fcntl. Exact method depends on the platform. - - let socket = cap_std::net::UdpSocket::new(family, blocking)?; - Ok(OwnedFd::from(socket)) - } - - pub fn udp_bind(sockfd: Fd, addr: &SocketAddr) -> rustix::io::Result<()> { - rustix::net::bind(sockfd, addr).map_err(|error| match error { - // See: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-bind#:~:text=WSAENOBUFS - // Windows returns WSAENOBUFS when the ephemeral ports have been exhausted. - #[cfg(windows)] - Errno::NOBUFS => Errno::ADDRINUSE, - _ => error, - }) - } - - pub fn udp_disconnect(sockfd: Fd) -> rustix::io::Result<()> { - match rustix::net::connect_unspec(sockfd) { - // BSD platforms return an error even if the UDP socket was disconnected successfully. - // - // MacOS was kind enough to document this: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/connect.2.html - // > Datagram sockets may dissolve the association by connecting to an - // > invalid address, such as a null address or an address with the address - // > family set to AF_UNSPEC (the error EAFNOSUPPORT will be harmlessly - // > returned). - // - // ... except that this appears to be incomplete, because experiments - // have shown that MacOS actually returns EINVAL, depending on the - // address family of the socket. - #[cfg(target_os = "macos")] - Err(Errno::INVAL | Errno::AFNOSUPPORT) => Ok(()), - r => r, - } - } - - // Even though SO_REUSEADDR is a SOL_* level option, this function contain a - // compatibility fix specific to TCP. That's why it contains the `_tcp_` infix instead of `_socket_`. - pub fn set_tcp_reuseaddr(sockfd: Fd, value: bool) -> rustix::io::Result<()> { - // When a TCP socket is closed, the system may - // temporarily reserve that specific address+port pair in a so called - // TIME_WAIT state. During that period, any attempt to rebind to that pair - // will fail. Setting SO_REUSEADDR to true bypasses that behaviour. Unlike - // the name "SO_REUSEADDR" might suggest, it does not allow multiple - // active sockets to share the same local address. - - // On Windows that behavior is the default, so there is no need to manually - // configure such an option. But (!), Windows _does_ have an identically - // named socket option which allows users to "hijack" active sockets. - // This is definitely not what we want to do here. - - // Microsoft's own documentation[1] states that we should set SO_EXCLUSIVEADDRUSE - // instead (to the inverse value), however the github issue below[2] seems - // to indicate that that may no longer be correct. - // [1]: https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse - // [2]: https://github.com/python-trio/trio/issues/928 - - #[cfg(not(windows))] - sockopt::set_socket_reuseaddr(sockfd, value)?; - #[cfg(windows)] - let _ = (sockfd, value); - - Ok(()) - } - - pub fn set_tcp_keepidle(sockfd: Fd, value: Duration) -> rustix::io::Result<()> { - if value <= Duration::ZERO { - // WIT: "If the provided value is 0, an `invalid-argument` error is returned." - return Err(Errno::INVAL); - } - - // Ensure that the value passed to the actual syscall never gets rounded down to 0. - const MIN_SECS: u64 = 1; - - // Cap it at Linux' maximum, which appears to have the lowest limit across our supported platforms. - const MAX_SECS: u64 = i16::MAX as u64; - - sockopt::set_tcp_keepidle( - sockfd, - value.clamp(Duration::from_secs(MIN_SECS), Duration::from_secs(MAX_SECS)), - ) - } - - pub fn set_tcp_keepintvl(sockfd: Fd, value: Duration) -> rustix::io::Result<()> { - if value <= Duration::ZERO { - // WIT: "If the provided value is 0, an `invalid-argument` error is returned." - return Err(Errno::INVAL); - } - - // Ensure that any fractional value passed to the actual syscall never gets rounded down to 0. - const MIN_SECS: u64 = 1; - - // Cap it at Linux' maximum, which appears to have the lowest limit across our supported platforms. - const MAX_SECS: u64 = i16::MAX as u64; - - sockopt::set_tcp_keepintvl( - sockfd, - value.clamp(Duration::from_secs(MIN_SECS), Duration::from_secs(MAX_SECS)), - ) - } - - pub fn set_tcp_keepcnt(sockfd: Fd, value: u32) -> rustix::io::Result<()> { - if value == 0 { - // WIT: "If the provided value is 0, an `invalid-argument` error is returned." - return Err(Errno::INVAL); - } - - const MIN_CNT: u32 = 1; - // Cap it at Linux' maximum, which appears to have the lowest limit across our supported platforms. - const MAX_CNT: u32 = i8::MAX as u32; - - sockopt::set_tcp_keepcnt(sockfd, value.clamp(MIN_CNT, MAX_CNT)) - } - - pub fn get_ip_ttl(sockfd: Fd) -> rustix::io::Result { - sockopt::ip_ttl(sockfd)? - .try_into() - .map_err(|_| Errno::OPNOTSUPP) - } - - pub fn get_ipv6_unicast_hops(sockfd: Fd) -> rustix::io::Result { - sockopt::ipv6_unicast_hops(sockfd) - } - - pub fn set_ip_ttl(sockfd: Fd, value: u8) -> rustix::io::Result<()> { - match value { - // WIT: "If the provided value is 0, an `invalid-argument` error is returned." - // - // A well-behaved IP application should never send out new packets with TTL 0. - // We validate the value ourselves because OS'es are not consistent in this. - // On Linux the validation is even inconsistent between their IPv4 and IPv6 implementation. - 0 => Err(Errno::INVAL), - _ => sockopt::set_ip_ttl(sockfd, value.into()), - } - } - - pub fn set_ipv6_unicast_hops(sockfd: Fd, value: u8) -> rustix::io::Result<()> { - match value { - 0 => Err(Errno::INVAL), // See `set_ip_ttl` - _ => sockopt::set_ipv6_unicast_hops(sockfd, Some(value)), - } - } - - fn normalize_get_buffer_size(value: usize) -> usize { - if cfg!(target_os = "linux") { - // Linux doubles the value passed to setsockopt to allow space for bookkeeping overhead. - // getsockopt returns this internally doubled value. - // We'll half the value to at least get it back into the same ballpark that the application requested it in. - // - // This normalized behavior is tested for in: test-programs/src/bin/preview2_tcp_sockopts.rs - value / 2 - } else { - value - } - } - - fn normalize_set_buffer_size(value: usize) -> usize { - value.clamp(1, i32::MAX as usize) - } - - pub fn get_socket_recv_buffer_size(sockfd: Fd) -> rustix::io::Result { - let value = sockopt::socket_recv_buffer_size(sockfd)?; - Ok(normalize_get_buffer_size(value)) - } - - pub fn get_socket_send_buffer_size(sockfd: Fd) -> rustix::io::Result { - let value = sockopt::socket_send_buffer_size(sockfd)?; - Ok(normalize_get_buffer_size(value)) - } - - pub fn set_socket_recv_buffer_size( - sockfd: Fd, - value: usize, - ) -> rustix::io::Result<()> { - if value == 0 { - // WIT: "If the provided value is 0, an `invalid-argument` error is returned." - return Err(Errno::INVAL); - } - - let value = normalize_set_buffer_size(value); - - match sockopt::set_socket_recv_buffer_size(sockfd, value) { - // Most platforms (Linux, Windows, Fuchsia, Solaris, Illumos, Haiku, ESP-IDF, ..and more?) treat the value - // passed to SO_SNDBUF/SO_RCVBUF as a performance tuning hint and silently clamp the input if it exceeds - // their capability. - // As far as I can see, only the *BSD family views this option as a hard requirement and fails when the - // value is out of range. We normalize this behavior in favor of the more commonly understood - // "performance hint" semantics. In other words; even ENOBUFS is "Ok". - // A future improvement could be to query the corresponding sysctl on *BSD platforms and clamp the input - // `size` ourselves, to completely close the gap with other platforms. - // - // This normalized behavior is tested for in: test-programs/src/bin/preview2_tcp_sockopts.rs - Err(Errno::NOBUFS) => Ok(()), - r => r, - } - } - - pub fn set_socket_send_buffer_size( - sockfd: Fd, - value: usize, - ) -> rustix::io::Result<()> { - if value == 0 { - // WIT: "If the provided value is 0, an `invalid-argument` error is returned." - return Err(Errno::INVAL); - } - - let value = normalize_set_buffer_size(value); - - match sockopt::set_socket_send_buffer_size(sockfd, value) { - Err(Errno::NOBUFS) => Ok(()), // See set_socket_recv_buffer_size - r => r, - } - } -} diff --git a/crates/wasi/src/p2/host/random.rs b/crates/wasi/src/p2/host/random.rs index a7c55dc7b2..aac8457a9f 100644 --- a/crates/wasi/src/p2/host/random.rs +++ b/crates/wasi/src/p2/host/random.rs @@ -1,45 +1,36 @@ use crate::p2::bindings::random::{insecure, insecure_seed, random}; -use crate::p2::{WasiImpl, WasiView}; +use crate::random::WasiRandomCtx; use cap_rand::{Rng, distributions::Standard}; -impl random::Host for WasiImpl -where - T: WasiView, -{ +impl random::Host for WasiRandomCtx { fn get_random_bytes(&mut self, len: u64) -> anyhow::Result> { - Ok((&mut self.ctx().random) + Ok((&mut self.random) .sample_iter(Standard) .take(len as usize) .collect()) } fn get_random_u64(&mut self) -> anyhow::Result { - Ok(self.ctx().random.sample(Standard)) + Ok(self.random.sample(Standard)) } } -impl insecure::Host for WasiImpl -where - T: WasiView, -{ +impl insecure::Host for WasiRandomCtx { fn get_insecure_random_bytes(&mut self, len: u64) -> anyhow::Result> { - Ok((&mut self.ctx().insecure_random) + Ok((&mut self.insecure_random) .sample_iter(Standard) .take(len as usize) .collect()) } fn get_insecure_random_u64(&mut self) -> anyhow::Result { - Ok(self.ctx().insecure_random.sample(Standard)) + Ok(self.insecure_random.sample(Standard)) } } -impl insecure_seed::Host for WasiImpl -where - T: WasiView, -{ +impl insecure_seed::Host for WasiRandomCtx { fn insecure_seed(&mut self) -> anyhow::Result<(u64, u64)> { - let seed: u128 = self.ctx().insecure_random_seed; + let seed: u128 = self.insecure_random_seed; Ok((seed as u64, (seed >> 64) as u64)) } } diff --git a/crates/wasi/src/p2/host/tcp.rs b/crates/wasi/src/p2/host/tcp.rs index 1f04fef247..f5c723227c 100644 --- a/crates/wasi/src/p2/host/tcp.rs +++ b/crates/wasi/src/p2/host/tcp.rs @@ -1,9 +1,9 @@ -use crate::net::{SocketAddrUse, SocketAddressFamily}; use crate::p2::bindings::{ sockets::network::{IpAddressFamily, IpSocketAddress, Network}, sockets::tcp::{self, ShutdownType}, }; use crate::p2::{SocketResult, WasiImpl, WasiView}; +use crate::sockets::{SocketAddrUse, SocketAddressFamily}; use std::net::SocketAddr; use std::time::Duration; use wasmtime::component::Resource; @@ -197,8 +197,7 @@ where ) -> SocketResult<()> { let table = self.table(); let socket = table.get_mut(&this)?; - let duration = Duration::from_nanos(value); - socket.set_keep_alive_idle_time(duration) + socket.set_keep_alive_idle_time(value) } fn keep_alive_interval(&mut self, this: Resource) -> SocketResult { @@ -249,7 +248,7 @@ where let table = self.table(); let socket = table.get(&this)?; - Ok(socket.receive_buffer_size()? as u64) + Ok(socket.receive_buffer_size()?) } fn set_receive_buffer_size( @@ -259,7 +258,6 @@ where ) -> SocketResult<()> { let table = self.table(); let socket = table.get_mut(&this)?; - let value = value.try_into().unwrap_or(usize::MAX); socket.set_receive_buffer_size(value) } @@ -267,7 +265,7 @@ where let table = self.table(); let socket = table.get(&this)?; - Ok(socket.send_buffer_size()? as u64) + Ok(socket.send_buffer_size()?) } fn set_send_buffer_size( @@ -277,7 +275,6 @@ where ) -> SocketResult<()> { let table = self.table(); let socket = table.get_mut(&this)?; - let value = value.try_into().unwrap_or(usize::MAX); socket.set_send_buffer_size(value) } diff --git a/crates/wasi/src/p2/host/udp.rs b/crates/wasi/src/p2/host/udp.rs index 84491ff7dd..553c422e18 100644 --- a/crates/wasi/src/p2/host/udp.rs +++ b/crates/wasi/src/p2/host/udp.rs @@ -1,9 +1,13 @@ -use crate::net::{SocketAddrUse, SocketAddressFamily}; use crate::p2::bindings::sockets::network::{ErrorCode, IpAddressFamily, IpSocketAddress, Network}; use crate::p2::bindings::sockets::udp; -use crate::p2::host::network::util; use crate::p2::udp::{IncomingDatagramStream, OutgoingDatagramStream, SendState, UdpState}; use crate::p2::{IoView, Pollable, SocketError, SocketResult, WasiImpl, WasiView}; +use crate::sockets::util::{ + get_ip_ttl, get_ipv6_unicast_hops, is_valid_address_family, is_valid_remote_address, + receive_buffer_size, send_buffer_size, set_receive_buffer_size, set_send_buffer_size, + set_unicast_hop_limit, udp_bind, udp_disconnect, +}; +use crate::sockets::{MAX_UDP_DATAGRAM_SIZE, SocketAddrUse, SocketAddressFamily}; use anyhow::anyhow; use async_trait::async_trait; use io_lifetimes::AsSocketlike; @@ -13,11 +17,6 @@ use tokio::io::Interest; use wasmtime::component::Resource; use wasmtime_wasi_io::poll::DynPollable; -/// Theoretical maximum byte size of a UDP datagram, the real limit is lower, -/// but we do not account for e.g. the transport layer here for simplicity. -/// In practice, datagrams are typically less than 1500 bytes. -const MAX_UDP_DATAGRAM_SIZE: usize = u16::MAX as usize; - impl udp::Host for WasiImpl where T: WasiView {} impl udp::HostUdpSocket for WasiImpl @@ -49,23 +48,15 @@ where let socket = table.get(&this)?; let local_address: SocketAddr = local_address.into(); - util::validate_address_family(&local_address, &socket.family)?; + if !is_valid_address_family(local_address.ip(), socket.family) { + return Err(ErrorCode::InvalidArgument.into()); + } { check.check(local_address, SocketAddrUse::UdpBind).await?; // Perform the OS bind call. - util::udp_bind(socket.udp_socket(), &local_address).map_err(|error| match error { - // From https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html: - // > [EAFNOSUPPORT] The specified address is not a valid address for the address family of the specified socket - // - // The most common reasons for this error should have already - // been handled by our own validation slightly higher up in this - // function. This error mapping is here just in case there is - // an edge case we didn't catch. - Errno::AFNOSUPPORT => ErrorCode::InvalidArgument, - _ => ErrorCode::from(error), - })?; + udp_bind(socket.udp_socket(), local_address)?; } let socket = table.get_mut(&this)?; @@ -121,7 +112,7 @@ where // Step #1: Disconnect if let UdpState::Connected = socket.udp_state { - util::udp_disconnect(socket.udp_socket())?; + udp_disconnect(socket.udp_socket())?; socket.udp_state = UdpState::Bound; } @@ -130,8 +121,11 @@ where let Some(check) = socket.socket_addr_check.as_ref() else { return Err(ErrorCode::InvalidState.into()); }; - util::validate_remote_address(&connect_addr)?; - util::validate_address_family(&connect_addr, &socket.family)?; + if !is_valid_remote_address(connect_addr) + || !is_valid_address_family(connect_addr.ip(), socket.family) + { + return Err(ErrorCode::InvalidArgument.into()); + } check.check(connect_addr, SocketAddrUse::UdpConnect).await?; rustix::net::connect(socket.udp_socket(), &connect_addr).map_err( @@ -218,8 +212,8 @@ where let socket = table.get(&this)?; let ttl = match socket.family { - SocketAddressFamily::Ipv4 => util::get_ip_ttl(socket.udp_socket())?, - SocketAddressFamily::Ipv6 => util::get_ipv6_unicast_hops(socket.udp_socket())?, + SocketAddressFamily::Ipv4 => get_ip_ttl(socket.udp_socket())?, + SocketAddressFamily::Ipv6 => get_ipv6_unicast_hops(socket.udp_socket())?, }; Ok(ttl) @@ -233,10 +227,7 @@ where let table = self.table(); let socket = table.get(&this)?; - match socket.family { - SocketAddressFamily::Ipv4 => util::set_ip_ttl(socket.udp_socket(), value)?, - SocketAddressFamily::Ipv6 => util::set_ipv6_unicast_hops(socket.udp_socket(), value)?, - } + set_unicast_hop_limit(socket.udp_socket(), socket.family, value)?; Ok(()) } @@ -245,8 +236,8 @@ where let table = self.table(); let socket = table.get(&this)?; - let value = util::get_socket_recv_buffer_size(socket.udp_socket())?; - Ok(value as u64) + let value = receive_buffer_size(socket.udp_socket())?; + Ok(value) } fn set_receive_buffer_size( @@ -256,9 +247,8 @@ where ) -> SocketResult<()> { let table = self.table(); let socket = table.get(&this)?; - let value = value.try_into().unwrap_or(usize::MAX); - util::set_socket_recv_buffer_size(socket.udp_socket(), value)?; + set_receive_buffer_size(socket.udp_socket(), value)?; Ok(()) } @@ -266,8 +256,8 @@ where let table = self.table(); let socket = table.get(&this)?; - let value = util::get_socket_send_buffer_size(socket.udp_socket())?; - Ok(value as u64) + let value = send_buffer_size(socket.udp_socket())?; + Ok(value) } fn set_send_buffer_size( @@ -277,9 +267,8 @@ where ) -> SocketResult<()> { let table = self.table(); let socket = table.get(&this)?; - let value = value.try_into().unwrap_or(usize::MAX); - util::set_socket_send_buffer_size(socket.udp_socket(), value)?; + set_send_buffer_size(socket.udp_socket(), value)?; Ok(()) } @@ -448,8 +437,10 @@ where _ => return Err(ErrorCode::InvalidArgument.into()), }; - util::validate_remote_address(&addr)?; - util::validate_address_family(&addr, &stream.family)?; + if !is_valid_remote_address(addr) || !is_valid_address_family(addr.ip(), stream.family) + { + return Err(ErrorCode::InvalidArgument.into()); + } if stream.remote_address == Some(addr) { stream.inner.try_send(&datagram.data)?; diff --git a/crates/wasi/src/p2/ip_name_lookup.rs b/crates/wasi/src/p2/ip_name_lookup.rs index bc3d8a09a0..b0252fa7c2 100644 --- a/crates/wasi/src/p2/ip_name_lookup.rs +++ b/crates/wasi/src/p2/ip_name_lookup.rs @@ -1,18 +1,16 @@ use crate::p2::bindings::sockets::ip_name_lookup::{Host, HostResolveAddressStream}; use crate::p2::bindings::sockets::network::{ErrorCode, IpAddress, Network}; -use crate::p2::host::network::util; use crate::p2::{IoView, SocketError, WasiImpl, WasiView}; use crate::runtime::{AbortOnDropJoinHandle, spawn_blocking}; use anyhow::Result; use std::mem; -use std::net::{Ipv6Addr, ToSocketAddrs}; +use std::net::ToSocketAddrs; use std::pin::Pin; -use std::str::FromStr; use std::vec; use wasmtime::component::Resource; use wasmtime_wasi_io::poll::{DynPollable, Pollable, subscribe}; -use super::network::{from_ipv4_addr, from_ipv6_addr}; +use crate::sockets::util::{from_ipv4_addr, from_ipv6_addr, parse_host}; pub enum ResolveAddressStream { Waiting(AbortOnDropJoinHandle, SocketError>>), @@ -30,7 +28,7 @@ where ) -> Result, SocketError> { let network = self.table().get(&network)?; - let host = parse(&name)?; + let host = parse_host(&name)?; if !network.allow_ip_name_lookup { return Err(ErrorCode::PermanentResolverFailure.into()); @@ -92,24 +90,6 @@ impl Pollable for ResolveAddressStream { } } -fn parse(name: &str) -> Result { - // `url::Host::parse` serves us two functions: - // 1. validate the input is a valid domain name or IP, - // 2. convert unicode domains to punycode. - match url::Host::parse(&name) { - Ok(host) => Ok(host), - - // `url::Host::parse` doesn't understand bare IPv6 addresses without [brackets] - Err(_) => { - if let Ok(addr) = Ipv6Addr::from_str(name) { - Ok(url::Host::Ipv6(addr)) - } else { - Err(ErrorCode::InvalidArgument.into()) - } - } - } -} - fn blocking_resolve(host: &url::Host) -> Result, SocketError> { match host { url::Host::Ipv4(v4addr) => Ok(vec![IpAddress::Ipv4(from_ipv4_addr(*v4addr))]), @@ -121,7 +101,7 @@ fn blocking_resolve(host: &url::Host) -> Result, SocketError> { let addresses = (domain.as_str(), 0) .to_socket_addrs() .map_err(|_| ErrorCode::NameUnresolvable)? // If/when we use `getaddrinfo` directly, map the error properly. - .map(|addr| util::to_canonical(&addr.ip()).into()) + .map(|addr| addr.ip().to_canonical().into()) .collect(); Ok(addresses) diff --git a/crates/wasi/src/p2/mod.rs b/crates/wasi/src/p2/mod.rs index b31fb18566..cfd602fa49 100644 --- a/crates/wasi/src/p2/mod.rs +++ b/crates/wasi/src/p2/mod.rs @@ -244,12 +244,13 @@ mod write_stream; pub use self::ctx::{WasiCtx, WasiCtxBuilder}; pub use self::filesystem::{FsError, FsResult}; -pub use self::network::{SocketError, SocketResult}; +pub use self::network::{Network, SocketError, SocketResult}; pub use self::stdio::{ AsyncStdinStream, AsyncStdoutStream, InputFile, IsATTY, OutputFile, Stderr, Stdin, StdinStream, Stdout, StdoutStream, stderr, stdin, stdout, }; pub use self::view::{WasiImpl, WasiView}; +use crate::random::WasiRandom; // These contents of wasmtime-wasi-io are re-exported by this module for compatibility: // they were originally defined in this module before being factored out, and many // users of this module depend on them at these names. @@ -355,9 +356,9 @@ where clocks::wall_clock::add_to_linker::>(l, f)?; clocks::monotonic_clock::add_to_linker::>(l, f)?; filesystem::preopens::add_to_linker::>(l, f)?; - random::random::add_to_linker::>(l, f)?; - random::insecure::add_to_linker::>(l, f)?; - random::insecure_seed::add_to_linker::>(l, f)?; + random::random::add_to_linker::(l, |t| &mut t.ctx().random)?; + random::insecure::add_to_linker::(l, |t| &mut t.ctx().random)?; + random::insecure_seed::add_to_linker::(l, |t| &mut t.ctx().random)?; cli::exit::add_to_linker::>(l, &options.into(), f)?; cli::environment::add_to_linker::>(l, f)?; cli::stdin::add_to_linker::>(l, f)?; @@ -404,7 +405,7 @@ fn add_proxy_interfaces_nonblocking( let f: fn(&mut T) -> WasiImpl<&mut T> = |t| WasiImpl(IoImpl(t)); clocks::wall_clock::add_to_linker::>(l, f)?; clocks::monotonic_clock::add_to_linker::>(l, f)?; - random::random::add_to_linker::>(l, f)?; + random::random::add_to_linker::(l, |t| &mut t.ctx().random)?; cli::stdin::add_to_linker::>(l, f)?; cli::stdout::add_to_linker::>(l, f)?; cli::stderr::add_to_linker::>(l, f)?; diff --git a/crates/wasi/src/p2/network.rs b/crates/wasi/src/p2/network.rs index 0a3090308a..d608624411 100644 --- a/crates/wasi/src/p2/network.rs +++ b/crates/wasi/src/p2/network.rs @@ -1,5 +1,8 @@ -use crate::TrappableError; -use crate::p2::bindings::sockets::network::{ErrorCode, Ipv4Address, Ipv6Address}; +use core::net::SocketAddr; + +use crate::p2::bindings::sockets::network::ErrorCode; +use crate::sockets::SocketAddrCheck; +use crate::{SocketAddrUse, TrappableError}; pub type SocketResult = Result; @@ -23,22 +26,44 @@ impl From for SocketError { } } -pub(crate) fn to_ipv4_addr(addr: Ipv4Address) -> std::net::Ipv4Addr { - let (x0, x1, x2, x3) = addr; - std::net::Ipv4Addr::new(x0, x1, x2, x3) +impl From for SocketError { + fn from(error: crate::sockets::util::ErrorCode) -> Self { + ErrorCode::from(error).into() + } } -pub(crate) fn from_ipv4_addr(addr: std::net::Ipv4Addr) -> Ipv4Address { - let [x0, x1, x2, x3] = addr.octets(); - (x0, x1, x2, x3) +impl From for ErrorCode { + fn from(error: crate::sockets::util::ErrorCode) -> Self { + match error { + crate::sockets::util::ErrorCode::Unknown => Self::Unknown, + crate::sockets::util::ErrorCode::AccessDenied => Self::AccessDenied, + crate::sockets::util::ErrorCode::NotSupported => Self::NotSupported, + crate::sockets::util::ErrorCode::InvalidArgument => Self::InvalidArgument, + crate::sockets::util::ErrorCode::OutOfMemory => Self::OutOfMemory, + crate::sockets::util::ErrorCode::Timeout => Self::Timeout, + crate::sockets::util::ErrorCode::InvalidState => Self::InvalidState, + crate::sockets::util::ErrorCode::AddressNotBindable => Self::AddressNotBindable, + crate::sockets::util::ErrorCode::AddressInUse => Self::AddressInUse, + crate::sockets::util::ErrorCode::RemoteUnreachable => Self::RemoteUnreachable, + crate::sockets::util::ErrorCode::ConnectionRefused => Self::ConnectionRefused, + crate::sockets::util::ErrorCode::ConnectionReset => Self::ConnectionReset, + crate::sockets::util::ErrorCode::ConnectionAborted => Self::ConnectionAborted, + crate::sockets::util::ErrorCode::DatagramTooLarge => Self::DatagramTooLarge, + } + } } -pub(crate) fn to_ipv6_addr(addr: Ipv6Address) -> std::net::Ipv6Addr { - let (x0, x1, x2, x3, x4, x5, x6, x7) = addr; - std::net::Ipv6Addr::new(x0, x1, x2, x3, x4, x5, x6, x7) +pub struct Network { + pub socket_addr_check: SocketAddrCheck, + pub allow_ip_name_lookup: bool, } -pub(crate) fn from_ipv6_addr(addr: std::net::Ipv6Addr) -> Ipv6Address { - let [x0, x1, x2, x3, x4, x5, x6, x7] = addr.segments(); - (x0, x1, x2, x3, x4, x5, x6, x7) +impl Network { + pub async fn check_socket_addr( + &self, + addr: SocketAddr, + reason: SocketAddrUse, + ) -> std::io::Result<()> { + self.socket_addr_check.check(addr, reason).await + } } diff --git a/crates/wasi/src/p2/stdio.rs b/crates/wasi/src/p2/stdio.rs index 8ff1799abb..eeb72c56b7 100644 --- a/crates/wasi/src/p2/stdio.rs +++ b/crates/wasi/src/p2/stdio.rs @@ -1,3 +1,4 @@ +use crate::cli::IsTerminal; use crate::p2::bindings::cli::{ stderr, stdin, stdout, terminal_input, terminal_output, terminal_stderr, terminal_stdin, terminal_stdout, @@ -7,7 +8,6 @@ use crate::p2::{ InputStream, IoView, OutputStream, Pollable, StreamError, StreamResult, WasiImpl, WasiView, }; use bytes::Bytes; -use std::io::IsTerminal; use std::sync::Arc; use tokio::sync::Mutex; use wasmtime::component::Resource; @@ -20,7 +20,7 @@ use wasmtime_wasi_io::streams; /// /// Built-in implementations are provided for [`Stdin`], /// [`pipe::MemoryInputPipe`], and [`pipe::ClosedInputStream`]. -pub trait StdinStream: Send { +pub trait StdinStream: IsTerminal + Send { /// Creates a fresh stream which is reading stdin. /// /// Note that the returned stream must share state with all other streams @@ -33,17 +33,16 @@ pub trait StdinStream: Send { /// mean that all the others are no longer ready for reading. This is /// basically a consequence of the way the WIT APIs are designed today. fn stream(&self) -> Box; - - /// Returns whether this stream is backed by a TTY. - fn isatty(&self) -> bool; } impl StdinStream for pipe::MemoryInputPipe { fn stream(&self) -> Box { Box::new(self.clone()) } +} - fn isatty(&self) -> bool { +impl IsTerminal for pipe::MemoryInputPipe { + fn is_terminal(&self) -> bool { false } } @@ -52,8 +51,10 @@ impl StdinStream for pipe::ClosedInputStream { fn stream(&self) -> Box { Box::new(*self) } +} - fn isatty(&self) -> bool { +impl IsTerminal for pipe::ClosedInputStream { + fn is_terminal(&self) -> bool { false } } @@ -79,8 +80,10 @@ impl StdinStream for InputFile { file: Arc::clone(&self.file), }) } +} - fn isatty(&self) -> bool { +impl IsTerminal for InputFile { + fn is_terminal(&self) -> bool { false } } @@ -151,7 +154,10 @@ impl StdinStream for AsyncStdinStream { fn stream(&self) -> Box { Box::new(Self(self.0.clone())) } - fn isatty(&self) -> bool { +} + +impl IsTerminal for AsyncStdinStream { + fn is_terminal(&self) -> bool { false } } @@ -192,7 +198,7 @@ mod worker_thread_stdin; pub use self::worker_thread_stdin::{Stdin, stdin}; /// Similar to [`StdinStream`], except for output. -pub trait StdoutStream: Send { +pub trait StdoutStream: IsTerminal + Send { /// Returns a fresh new stream which can write to this output stream. /// /// Note that all output streams should output to the same logical source. @@ -206,17 +212,16 @@ pub trait StdoutStream: Send { /// /// Implementations must be able to handle this fn stream(&self) -> Box; - - /// Returns whether this stream is backed by a TTY. - fn isatty(&self) -> bool; } impl StdoutStream for pipe::MemoryOutputPipe { fn stream(&self) -> Box { Box::new(self.clone()) } +} - fn isatty(&self) -> bool { +impl IsTerminal for pipe::MemoryOutputPipe { + fn is_terminal(&self) -> bool { false } } @@ -225,8 +230,10 @@ impl StdoutStream for pipe::SinkOutputStream { fn stream(&self) -> Box { Box::new(*self) } +} - fn isatty(&self) -> bool { +impl IsTerminal for pipe::SinkOutputStream { + fn is_terminal(&self) -> bool { false } } @@ -235,8 +242,10 @@ impl StdoutStream for pipe::ClosedOutputStream { fn stream(&self) -> Box { Box::new(*self) } +} - fn isatty(&self) -> bool { +impl IsTerminal for pipe::ClosedOutputStream { + fn is_terminal(&self) -> bool { false } } @@ -262,8 +271,10 @@ impl StdoutStream for OutputFile { file: Arc::clone(&self.file), }) } +} - fn isatty(&self) -> bool { +impl IsTerminal for OutputFile { + fn is_terminal(&self) -> bool { false } } @@ -315,8 +326,10 @@ impl StdoutStream for Stdout { fn stream(&self) -> Box { Box::new(StdioOutputStream::Stdout) } +} - fn isatty(&self) -> bool { +impl IsTerminal for Stdout { + fn is_terminal(&self) -> bool { std::io::stdout().is_terminal() } } @@ -339,8 +352,10 @@ impl StdoutStream for Stderr { fn stream(&self) -> Box { Box::new(StdioOutputStream::Stderr) } +} - fn isatty(&self) -> bool { +impl IsTerminal for Stderr { + fn is_terminal(&self) -> bool { std::io::stderr().is_terminal() } } @@ -398,7 +413,10 @@ impl StdoutStream for AsyncStdoutStream { fn stream(&self) -> Box { Box::new(Self(self.0.clone())) } - fn isatty(&self) -> bool { +} + +impl IsTerminal for AsyncStdoutStream { + fn is_terminal(&self) -> bool { false } } @@ -520,7 +538,7 @@ where T: WasiView, { fn get_terminal_stdin(&mut self) -> anyhow::Result>> { - if self.ctx().stdin.isatty() { + if self.ctx().stdin.is_terminal() { let fd = self.table().push(TerminalInput)?; Ok(Some(fd)) } else { @@ -533,7 +551,7 @@ where T: WasiView, { fn get_terminal_stdout(&mut self) -> anyhow::Result>> { - if self.ctx().stdout.isatty() { + if self.ctx().stdout.is_terminal() { let fd = self.table().push(TerminalOutput)?; Ok(Some(fd)) } else { @@ -546,7 +564,7 @@ where T: WasiView, { fn get_terminal_stderr(&mut self) -> anyhow::Result>> { - if self.ctx().stderr.isatty() { + if self.ctx().stderr.is_terminal() { let fd = self.table().push(TerminalOutput)?; Ok(Some(fd)) } else { diff --git a/crates/wasi/src/p2/stdio/worker_thread_stdin.rs b/crates/wasi/src/p2/stdio/worker_thread_stdin.rs index bf7bef781b..7557e561d4 100644 --- a/crates/wasi/src/p2/stdio/worker_thread_stdin.rs +++ b/crates/wasi/src/p2/stdio/worker_thread_stdin.rs @@ -23,9 +23,10 @@ //! This module is one that's likely to change over time though as new systems //! are encountered along with preexisting bugs. +use crate::cli::IsTerminal; use crate::p2::stdio::StdinStream; use bytes::{Bytes, BytesMut}; -use std::io::{IsTerminal, Read}; +use std::io::Read; use std::mem; use std::sync::{Condvar, Mutex, OnceLock}; use tokio::sync::Notify; @@ -114,8 +115,10 @@ impl StdinStream for Stdin { fn stream(&self) -> Box { Box::new(Stdin) } +} - fn isatty(&self) -> bool { +impl IsTerminal for Stdin { + fn is_terminal(&self) -> bool { std::io::stdin().is_terminal() } } diff --git a/crates/wasi/src/p2/tcp.rs b/crates/wasi/src/p2/tcp.rs index f552b145d6..188b2148d8 100644 --- a/crates/wasi/src/p2/tcp.rs +++ b/crates/wasi/src/p2/tcp.rs @@ -1,11 +1,16 @@ -use crate::net::{DEFAULT_TCP_BACKLOG, SocketAddressFamily}; use crate::p2::bindings::sockets::tcp::ErrorCode; -use crate::p2::host::network; use crate::p2::{ DynInputStream, DynOutputStream, InputStream, OutputStream, Pollable, SocketError, SocketResult, StreamError, }; use crate::runtime::{AbortOnDropJoinHandle, with_ambient_tokio_runtime}; +use crate::sockets::util::{ + get_unicast_hop_limit, is_valid_address_family, is_valid_remote_address, + is_valid_unicast_address, receive_buffer_size, send_buffer_size, set_keep_alive_count, + set_keep_alive_idle_time, set_keep_alive_interval, set_receive_buffer_size, + set_send_buffer_size, set_unicast_hop_limit, tcp_bind, +}; +use crate::sockets::{DEFAULT_TCP_BACKLOG, SocketAddressFamily}; use anyhow::Result; use cap_net_ext::AddressFamily; use futures::Future; @@ -96,13 +101,13 @@ pub struct TcpSocket { // on all platforms. So we keep track of which options have been explicitly // set and manually apply those values to newly accepted clients. #[cfg(target_os = "macos")] - receive_buffer_size: Option, + receive_buffer_size: Option, #[cfg(target_os = "macos")] - send_buffer_size: Option, + send_buffer_size: Option, #[cfg(target_os = "macos")] hop_limit: Option, #[cfg(target_os = "macos")] - keep_alive_idle_time: Option, + keep_alive_idle_time: Option, } impl TcpSocket { @@ -166,49 +171,21 @@ impl TcpSocket { } impl TcpSocket { - pub fn start_bind(&mut self, local_address: SocketAddr) -> io::Result<()> { + pub fn start_bind(&mut self, local_address: SocketAddr) -> Result<(), ErrorCode> { let tokio_socket = match &self.tcp_state { TcpState::Default(socket) => socket, TcpState::BindStarted(..) => return Err(Errno::ALREADY.into()), - _ => return Err(Errno::ISCONN.into()), + _ => return Err(ErrorCode::InvalidState), }; - network::util::validate_unicast(&local_address)?; - network::util::validate_address_family(&local_address, &self.family)?; - + if !is_valid_unicast_address(local_address.ip()) + || !is_valid_address_family(local_address.ip(), self.family) { - // Automatically bypass the TIME_WAIT state when the user is trying - // to bind to a specific port: - let reuse_addr = local_address.port() > 0; - - // Unconditionally (re)set SO_REUSEADDR, even when the value is false. - // This ensures we're not accidentally affected by any socket option - // state left behind by a previous failed call to this method (start_bind). - network::util::set_tcp_reuseaddr(&tokio_socket, reuse_addr)?; - - // Perform the OS bind call. - tokio_socket.bind(local_address).map_err(|error| { - match Errno::from_io_error(&error) { - // From https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html: - // > [EAFNOSUPPORT] The specified address is not a valid address for the address family of the specified socket - // - // The most common reasons for this error should have already - // been handled by our own validation slightly higher up in this - // function. This error mapping is here just in case there is - // an edge case we didn't catch. - Some(Errno::AFNOSUPPORT) => io::Error::new( - io::ErrorKind::InvalidInput, - "The specified address is not a valid address for the address family of the specified socket", - ), - - // See: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-bind#:~:text=WSAENOBUFS - // Windows returns WSAENOBUFS when the ephemeral ports have been exhausted. - #[cfg(windows)] - Some(Errno::NOBUFS) => io::Error::new(io::ErrorKind::AddrInUse, "no more free local ports"), + return Err(ErrorCode::InvalidArgument); + }; - _ => error, - } - })?; + { + tcp_bind(&tokio_socket, local_address)?; self.tcp_state = match std::mem::replace(&mut self.tcp_state, TcpState::Closed) { TcpState::Default(socket) => TcpState::BindStarted(socket), @@ -244,9 +221,12 @@ impl TcpSocket { _ => return Err(ErrorCode::InvalidState.into()), }; - network::util::validate_unicast(&remote_address)?; - network::util::validate_remote_address(&remote_address)?; - network::util::validate_address_family(&remote_address, &self.family)?; + if !is_valid_unicast_address(remote_address.ip()) + || !is_valid_remote_address(remote_address) + || !is_valid_address_family(remote_address.ip(), self.family) + { + return Err(ErrorCode::InvalidArgument.into()); + }; let (TcpState::Default(tokio_socket) | TcpState::Bound(tokio_socket)) = std::mem::replace(&mut self.tcp_state, TcpState::Closed) @@ -420,20 +400,20 @@ impl TcpSocket { // and only if a specific value was explicitly set on the listener. if let Some(size) = self.receive_buffer_size { - _ = network::util::set_socket_recv_buffer_size(&client, size); // Ignore potential error. + _ = set_receive_buffer_size(&client, size); // Ignore potential error. } if let Some(size) = self.send_buffer_size { - _ = network::util::set_socket_send_buffer_size(&client, size); // Ignore potential error. + _ = set_send_buffer_size(&client, size); // Ignore potential error. } // For some reason, IP_TTL is inherited, but IPV6_UNICAST_HOPS isn't. if let (SocketAddressFamily::Ipv6, Some(ttl)) = (self.family, self.hop_limit) { - _ = network::util::set_ipv6_unicast_hops(&client, ttl); // Ignore potential error. + _ = rustix::net::sockopt::set_ipv6_unicast_hops(&client, Some(ttl)); // Ignore potential error. } if let Some(value) = self.keep_alive_idle_time { - _ = network::util::set_tcp_keepidle(&client, value); // Ignore potential error. + _ = set_keep_alive_idle_time(&client, value); // Ignore potential error. } } @@ -530,15 +510,15 @@ impl TcpSocket { Ok(sockopt::tcp_keepidle(view)?) } - pub fn set_keep_alive_idle_time(&mut self, duration: std::time::Duration) -> SocketResult<()> { + pub fn set_keep_alive_idle_time(&mut self, value: u64) -> SocketResult<()> { { let view = &*self.as_std_view()?; - network::util::set_tcp_keepidle(view, duration)?; + set_keep_alive_idle_time(view, value)?; } #[cfg(target_os = "macos")] { - self.keep_alive_idle_time = Some(duration); + self.keep_alive_idle_time = Some(value); } Ok(()) @@ -551,7 +531,7 @@ impl TcpSocket { pub fn set_keep_alive_interval(&self, duration: std::time::Duration) -> SocketResult<()> { let view = &*self.as_std_view()?; - Ok(network::util::set_tcp_keepintvl(view, duration)?) + Ok(set_keep_alive_interval(view, duration)?) } pub fn keep_alive_count(&self) -> SocketResult { @@ -561,17 +541,13 @@ impl TcpSocket { pub fn set_keep_alive_count(&self, value: u32) -> SocketResult<()> { let view = &*self.as_std_view()?; - Ok(network::util::set_tcp_keepcnt(view, value)?) + Ok(set_keep_alive_count(view, value)?) } pub fn hop_limit(&self) -> SocketResult { let view = &*self.as_std_view()?; - let ttl = match self.family { - SocketAddressFamily::Ipv4 => network::util::get_ip_ttl(view)?, - SocketAddressFamily::Ipv6 => network::util::get_ipv6_unicast_hops(view)?, - }; - + let ttl = get_unicast_hop_limit(view, self.family)?; Ok(ttl) } @@ -579,10 +555,7 @@ impl TcpSocket { { let view = &*self.as_std_view()?; - match self.family { - SocketAddressFamily::Ipv4 => network::util::set_ip_ttl(view, value)?, - SocketAddressFamily::Ipv6 => network::util::set_ipv6_unicast_hops(view, value)?, - } + set_unicast_hop_limit(view, self.family, value)?; } #[cfg(target_os = "macos")] @@ -593,17 +566,17 @@ impl TcpSocket { Ok(()) } - pub fn receive_buffer_size(&self) -> SocketResult { + pub fn receive_buffer_size(&self) -> SocketResult { let view = &*self.as_std_view()?; - Ok(network::util::get_socket_recv_buffer_size(view)?) + Ok(receive_buffer_size(view)?) } - pub fn set_receive_buffer_size(&mut self, value: usize) -> SocketResult<()> { + pub fn set_receive_buffer_size(&mut self, value: u64) -> SocketResult<()> { { let view = &*self.as_std_view()?; - network::util::set_socket_recv_buffer_size(view, value)?; + set_receive_buffer_size(view, value)?; } #[cfg(target_os = "macos")] @@ -614,17 +587,17 @@ impl TcpSocket { Ok(()) } - pub fn send_buffer_size(&self) -> SocketResult { + pub fn send_buffer_size(&self) -> SocketResult { let view = &*self.as_std_view()?; - Ok(network::util::get_socket_send_buffer_size(view)?) + Ok(send_buffer_size(view)?) } - pub fn set_send_buffer_size(&mut self, value: usize) -> SocketResult<()> { + pub fn set_send_buffer_size(&mut self, value: u64) -> SocketResult<()> { { let view = &*self.as_std_view()?; - network::util::set_socket_send_buffer_size(view, value)?; + set_send_buffer_size(view, value)?; } #[cfg(target_os = "macos")] diff --git a/crates/wasi/src/p2/udp.rs b/crates/wasi/src/p2/udp.rs index 3d98e56ac7..a066fb97eb 100644 --- a/crates/wasi/src/p2/udp.rs +++ b/crates/wasi/src/p2/udp.rs @@ -1,8 +1,8 @@ -use crate::net::{SocketAddrCheck, SocketAddressFamily}; -use crate::p2::host::network::util; use crate::runtime::with_ambient_tokio_runtime; +use crate::sockets::util::udp_socket; +use crate::sockets::{SocketAddrCheck, SocketAddressFamily}; use async_trait::async_trait; -use cap_net_ext::{AddressFamily, Blocking}; +use cap_net_ext::AddressFamily; use io_lifetimes::raw::{FromRawSocketlike, IntoRawSocketlike}; use std::io; use std::net::SocketAddr; @@ -59,7 +59,7 @@ impl UdpSocket { pub fn new(family: AddressFamily) -> io::Result { // Create a new host socket and set it to non-blocking, which is needed // by our async implementation. - let fd = util::udp_socket(family, Blocking::No)?; + let fd = udp_socket(family)?; let socket_address_family = match family { AddressFamily::Ipv4 => SocketAddressFamily::Ipv4, @@ -69,7 +69,7 @@ impl UdpSocket { } }; - let socket = Self::setup_tokio_udp_socket(fd)?; + let socket = Self::setup_tokio_udp_socket(fd.into())?; Ok(UdpSocket { inner: Arc::new(socket), diff --git a/crates/wasi/src/p3/bindings.rs b/crates/wasi/src/p3/bindings.rs index 20472f6bff..10c78d7545 100644 --- a/crates/wasi/src/p3/bindings.rs +++ b/crates/wasi/src/p3/bindings.rs @@ -11,10 +11,10 @@ //! use this modules's bindings rather than generate fresh bindings. That can be //! done using the `with` option to [`bindgen!`]: //! -//! ```rust,ignore(TODO fix after upstreaming is complete) -//! use wasmtime_wasi::p3::{WasiCtx, WasiView}; +//! ```rust,ignore(TODO fix when wasi finishes merging upstream) +//! use wasmtime_wasi::p3::{WasiCtx, WasiCtxView, WasiView}; //! use wasmtime::{Result, Engine, Config}; -//! use wasmtime::component::{Linker, HasSelf}; +//! use wasmtime::component::{Linker, HasSelf, ResourceTable}; //! //! wasmtime::component::bindgen!({ //! inline: " @@ -36,56 +36,12 @@ //! with: { //! "wasi": wasmtime_wasi::p3::bindings, //! }, -//! concurrent_exports: true, -//! concurrent_imports: true, -//! async: { -//! only_imports: [ -//! "wasi:cli/stdin@0.3.0#get-stdin", -//! "wasi:cli/stdout@0.3.0#set-stdout", -//! "wasi:cli/stderr@0.3.0#set-stderr", -//! "wasi:clocks/monotonic-clock@0.3.0#[async]wait-for", -//! "wasi:clocks/monotonic-clock@0.3.0#[async]wait-until", -//! "wasi:filesystem/types@0.3.0#[method]descriptor.read-via-stream", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.write-via-stream", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.append-via-stream", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.advise", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.sync-data", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.get-flags", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.get-type", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.set-size", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.set-times", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.read-directory", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.sync", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.create-directory-at", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.stat", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.stat-at", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.set-times-at", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.link-at", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.open-at", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.readlink-at", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.remove-directory-at", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.rename-at", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.symlink-at", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.unlink-file-at", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.is-same-object", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.metadata-hash", -//! "wasi:filesystem/types@0.3.0#[async method]descriptor.metadata-hash-at", -//! "wasi:sockets/ip-name-lookup@0.3.0#[async]resolve-addresses", -//! "wasi:sockets/types@0.3.0#[async method]tcp-socket.connect", -//! "wasi:sockets/types@0.3.0#[async method]tcp-socket.send", -//! "wasi:sockets/types@0.3.0#[async method]udp-socket.receive", -//! "wasi:sockets/types@0.3.0#[async method]udp-socket.send", -//! "wasi:sockets/types@0.3.0#[method]tcp-socket.bind", -//! "wasi:sockets/types@0.3.0#[method]tcp-socket.listen", -//! "wasi:sockets/types@0.3.0#[method]tcp-socket.receive", -//! "wasi:sockets/types@0.3.0#[method]udp-socket.bind", -//! "wasi:sockets/types@0.3.0#[method]udp-socket.connect", -//! ], -//! }, +//! require_store_data_send: true, //! }); //! //! struct MyState { //! ctx: WasiCtx, +//! table: ResourceTable, //! } //! //! impl example::wasi::custom_host::Host for MyState { @@ -95,7 +51,12 @@ //! } //! //! impl WasiView for MyState { -//! fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx } +//! fn ctx(&mut self) -> WasiCtxView<'_> { +//! WasiCtxView{ +//! ctx: &mut self.ctx, +//! table: &mut self.table, +//! } +//! } //! } //! //! fn main() -> Result<()> { @@ -117,60 +78,25 @@ mod generated { wasmtime::component::bindgen!({ path: "src/p3/wit", world: "wasi:cli/command", - tracing: true, - trappable_imports: true, - concurrent_exports: true, - concurrent_imports: true, - async: { - only_imports: [ - "wasi:cli/stdin@0.3.0#get-stdin", - "wasi:cli/stdout@0.3.0#set-stdout", - "wasi:cli/stderr@0.3.0#set-stderr", - "wasi:clocks/monotonic-clock@0.3.0#[async]wait-for", - "wasi:clocks/monotonic-clock@0.3.0#[async]wait-until", - "wasi:filesystem/types@0.3.0#[method]descriptor.read-via-stream", - "wasi:filesystem/types@0.3.0#[async method]descriptor.write-via-stream", - "wasi:filesystem/types@0.3.0#[async method]descriptor.append-via-stream", - "wasi:filesystem/types@0.3.0#[async method]descriptor.advise", - "wasi:filesystem/types@0.3.0#[async method]descriptor.sync-data", - "wasi:filesystem/types@0.3.0#[async method]descriptor.get-flags", - "wasi:filesystem/types@0.3.0#[async method]descriptor.get-type", - "wasi:filesystem/types@0.3.0#[async method]descriptor.set-size", - "wasi:filesystem/types@0.3.0#[async method]descriptor.set-times", - "wasi:filesystem/types@0.3.0#[async method]descriptor.read-directory", - "wasi:filesystem/types@0.3.0#[async method]descriptor.sync", - "wasi:filesystem/types@0.3.0#[async method]descriptor.create-directory-at", - "wasi:filesystem/types@0.3.0#[async method]descriptor.stat", - "wasi:filesystem/types@0.3.0#[async method]descriptor.stat-at", - "wasi:filesystem/types@0.3.0#[async method]descriptor.set-times-at", - "wasi:filesystem/types@0.3.0#[async method]descriptor.link-at", - "wasi:filesystem/types@0.3.0#[async method]descriptor.open-at", - "wasi:filesystem/types@0.3.0#[async method]descriptor.readlink-at", - "wasi:filesystem/types@0.3.0#[async method]descriptor.remove-directory-at", - "wasi:filesystem/types@0.3.0#[async method]descriptor.rename-at", - "wasi:filesystem/types@0.3.0#[async method]descriptor.symlink-at", - "wasi:filesystem/types@0.3.0#[async method]descriptor.unlink-file-at", - "wasi:filesystem/types@0.3.0#[async method]descriptor.is-same-object", - "wasi:filesystem/types@0.3.0#[async method]descriptor.metadata-hash", - "wasi:filesystem/types@0.3.0#[async method]descriptor.metadata-hash-at", - "wasi:sockets/ip-name-lookup@0.3.0#[async]resolve-addresses", - "wasi:sockets/types@0.3.0#[async method]tcp-socket.connect", - "wasi:sockets/types@0.3.0#[async method]tcp-socket.send", - "wasi:sockets/types@0.3.0#[async method]udp-socket.receive", - "wasi:sockets/types@0.3.0#[async method]udp-socket.send", - "wasi:sockets/types@0.3.0#[method]tcp-socket.bind", - "wasi:sockets/types@0.3.0#[method]tcp-socket.listen", - "wasi:sockets/types@0.3.0#[method]tcp-socket.receive", - "wasi:sockets/types@0.3.0#[method]udp-socket.bind", - "wasi:sockets/types@0.3.0#[method]udp-socket.connect", - ], + imports: { + "wasi:cli/stdin": async | store | tracing | trappable, + "wasi:cli/stdout": async | store | tracing | trappable, + "wasi:cli/stderr": async | store | tracing | trappable, + "wasi:filesystem/types/[method]descriptor.read-via-stream": async | store | tracing | trappable, + "wasi:sockets/types/[method]tcp-socket.bind": async | store | tracing | trappable, + "wasi:sockets/types/[method]tcp-socket.listen": async | store | tracing | trappable, + "wasi:sockets/types/[method]tcp-socket.receive": async | store | tracing | trappable, + "wasi:sockets/types/[method]udp-socket.bind": async | store | tracing | trappable, + "wasi:sockets/types/[method]udp-socket.connect": async | store | tracing | trappable, + default: tracing | trappable, }, + exports: { default: async | store }, with: { "wasi:cli/terminal-input/terminal-input": crate::p3::cli::TerminalInput, "wasi:cli/terminal-output/terminal-output": crate::p3::cli::TerminalOutput, - "wasi:filesystem/types/descriptor": crate::p3::filesystem::Descriptor, "wasi:sockets/types/tcp-socket": crate::p3::sockets::tcp::TcpSocket, "wasi:sockets/types/udp-socket": crate::p3::sockets::udp::UdpSocket, + "wasi:filesystem/types/descriptor": crate::p3::filesystem::Descriptor, } }); } @@ -190,8 +116,8 @@ pub use self::generated::wasi::*; /// /// ```no_run,ignore(TODO fix after upstreaming is complete) /// use wasmtime::{Engine, Result, Store, Config}; -/// use wasmtime::component::{Component, Linker}; -/// use wasmtime_wasi::p3::{WasiCtx, WasiView, WasiCtxBuilder}; +/// use wasmtime::component::{Component, Linker, ResourceTable}; +/// use wasmtime_wasi::p3::{WasiCtx, WasiCtxView, WasiCtxBuilder, WasiView}; /// use wasmtime_wasi::p3::bindings::Command; /// /// // This example is an example shim of executing a component based on the @@ -221,6 +147,7 @@ pub use self::generated::wasi::*; /// &engine, /// MyState { /// ctx: builder.build(), +/// table: ResourceTable::default(), /// }, /// ); /// @@ -238,10 +165,16 @@ pub use self::generated::wasi::*; /// /// struct MyState { /// ctx: WasiCtx, +/// table: ResourceTable, /// } /// /// impl WasiView for MyState { -/// fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx } +/// fn ctx(&mut self) -> WasiCtxView<'_> { +/// WasiCtxView{ +/// ctx: &mut self.ctx, +/// table: &mut self.table, +/// } +/// } /// } /// ``` /// @@ -257,8 +190,8 @@ pub use self::generated::Command; /// /// ```no_run,ignore(TODO fix after upstreaming is complete) /// use wasmtime::{Engine, Result, Store, Config}; -/// use wasmtime::component::{Linker, Component}; -/// use wasmtime_wasi::p3::{WasiCtx, WasiView, WasiCtxBuilder}; +/// use wasmtime::component::{Linker, Component, ResourceTable}; +/// use wasmtime_wasi::p3::{WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView}; /// use wasmtime_wasi::p3::bindings::CommandPre; /// /// // This example is an example shim of executing a component based on the @@ -289,6 +222,7 @@ pub use self::generated::Command; /// &engine, /// MyState { /// ctx: builder.build(), +/// table: ResourceTable::default(), /// }, /// ); /// @@ -306,10 +240,16 @@ pub use self::generated::Command; /// /// struct MyState { /// ctx: WasiCtx, +/// table: ResourceTable, /// } /// /// impl WasiView for MyState { -/// fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx } +/// fn ctx(&mut self) -> WasiCtxView<'_> { +/// WasiCtxView{ +/// ctx: &mut self.ctx, +/// table: &mut self.table, +/// } +/// } /// } /// ``` /// diff --git a/crates/wasi/src/p3/cli/host.rs b/crates/wasi/src/p3/cli/host.rs index 1e22790fe0..b5d25fb4e1 100644 --- a/crates/wasi/src/p3/cli/host.rs +++ b/crates/wasi/src/p3/cli/host.rs @@ -1,40 +1,43 @@ -use crate::p3::ResourceView as _; +use crate::I32Exit; +use crate::cli::IsTerminal; +use crate::p3::DEFAULT_BUFFER_CAPACITY; use crate::p3::bindings::cli::{ environment, exit, stderr, stdin, stdout, terminal_input, terminal_output, terminal_stderr, terminal_stdin, terminal_stdout, }; -use crate::p3::cli::{I32Exit, TerminalInput, TerminalOutput, WasiCli, WasiCliImpl, WasiCliView}; +use crate::p3::cli::{TerminalInput, TerminalOutput, WasiCli, WasiCliCtxView}; use anyhow::{Context as _, anyhow}; use bytes::BytesMut; use std::io::Cursor; use tokio::io::{AsyncRead, AsyncReadExt as _, AsyncWrite, AsyncWriteExt as _}; -use wasmtime::component::{Accessor, AccessorTask, Resource, StreamReader, StreamWriter}; +use wasmtime::component::{ + Accessor, AccessorTask, GuardedStreamReader, GuardedStreamWriter, HasData, Resource, + StreamReader, StreamWriter, +}; struct InputTask { - input: T, + rx: T, tx: StreamWriter, } -impl AccessorTask, wasmtime::Result<()>> for InputTask +impl AccessorTask> for InputTask where - T: 'static, - U: 'static, + U: HasData, V: AsyncRead + Send + Sync + Unpin + 'static, { - async fn run(mut self, store: &Accessor>) -> wasmtime::Result<()> { - let mut tx = self.tx; - let mut buf = BytesMut::with_capacity(8096); + async fn run(mut self, store: &Accessor) -> wasmtime::Result<()> { + let mut buf = BytesMut::with_capacity(DEFAULT_BUFFER_CAPACITY); + let mut tx = GuardedStreamWriter::new(store, self.tx); while !tx.is_closed() { - buf.clear(); - match self.input.read_buf(&mut buf).await { + match self.rx.read_buf(&mut buf).await { Ok(0) => return Ok(()), Ok(_) => { - buf = tx.write_all(store, Cursor::new(buf)).await.into_inner(); + buf = tx.write_all(Cursor::new(buf)).await.into_inner(); + buf.clear(); } Err(_err) => { - // TODO: Close the stream with an error context - drop(tx); - break; + // TODO: Report the error to the guest + return Ok(()); } } } @@ -43,74 +46,63 @@ where } struct OutputTask { - output: T, - data: StreamReader, + rx: StreamReader, + tx: T, } -impl AccessorTask, wasmtime::Result<()>> for OutputTask +impl AccessorTask> for OutputTask where - T: 'static, - U: 'static, + U: HasData, V: AsyncWrite + Send + Sync + Unpin + 'static, { - async fn run(mut self, store: &Accessor>) -> wasmtime::Result<()> { - let mut buf = BytesMut::with_capacity(8096); - while !self.data.is_closed() { - buf = self.data.read(store, buf).await; - match self.output.write_all(&buf).await { + async fn run(mut self, store: &Accessor) -> wasmtime::Result<()> { + let mut buf = BytesMut::with_capacity(DEFAULT_BUFFER_CAPACITY); + let mut rx = GuardedStreamReader::new(store, self.rx); + while !rx.is_closed() { + buf = rx.read(buf).await; + match self.tx.write_all(&buf).await { Ok(()) => { buf.clear(); continue; } Err(_err) => { // TODO: Report the error to the guest - drop(self.data); - break; + return Ok(()); } } } - Ok(()) } } -impl terminal_input::Host for WasiCliImpl where T: WasiCliView {} -impl terminal_output::Host for WasiCliImpl where T: WasiCliView {} +impl terminal_input::Host for WasiCliCtxView<'_> {} +impl terminal_output::Host for WasiCliCtxView<'_> {} -impl terminal_input::HostTerminalInput for WasiCliImpl -where - T: WasiCliView, -{ +impl terminal_input::HostTerminalInput for WasiCliCtxView<'_> { fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { - self.table() + self.table .delete(rep) - .context("failed to delete input resource from table")?; + .context("failed to delete terminal input resource from table")?; Ok(()) } } -impl terminal_output::HostTerminalOutput for WasiCliImpl -where - T: WasiCliView, -{ +impl terminal_output::HostTerminalOutput for WasiCliCtxView<'_> { fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { - self.table() + self.table .delete(rep) - .context("failed to delete output resource from table")?; + .context("failed to delete terminal output resource from table")?; Ok(()) } } -impl terminal_stdin::Host for WasiCliImpl -where - T: WasiCliView, -{ +impl terminal_stdin::Host for WasiCliCtxView<'_> { fn get_terminal_stdin(&mut self) -> wasmtime::Result>> { - if self.cli().stdin.is_terminal() { + if self.ctx.stdin.is_terminal() { let fd = self - .table() + .table .push(TerminalInput) - .context("failed to push terminal resource to table")?; + .context("failed to push terminal stdin resource to table")?; Ok(Some(fd)) } else { Ok(None) @@ -118,16 +110,13 @@ where } } -impl terminal_stdout::Host for WasiCliImpl -where - T: WasiCliView, -{ +impl terminal_stdout::Host for WasiCliCtxView<'_> { fn get_terminal_stdout(&mut self) -> wasmtime::Result>> { - if self.cli().stdout.is_terminal() { + if self.ctx.stdout.is_terminal() { let fd = self - .table() + .table .push(TerminalOutput) - .context("failed to push terminal resource to table")?; + .context("failed to push terminal stdout resource to table")?; Ok(Some(fd)) } else { Ok(None) @@ -135,16 +124,13 @@ where } } -impl terminal_stderr::Host for WasiCliImpl -where - T: WasiCliView, -{ +impl terminal_stderr::Host for WasiCliCtxView<'_> { fn get_terminal_stderr(&mut self) -> wasmtime::Result>> { - if self.cli().stderr.is_terminal() { + if self.ctx.stderr.is_terminal() { let fd = self - .table() + .table .push(TerminalOutput) - .context("failed to push terminal resource to table")?; + .context("failed to push terminal stderr resource to table")?; Ok(Some(fd)) } else { Ok(None) @@ -152,90 +138,76 @@ where } } -impl stdin::HostConcurrent for WasiCli -where - T: WasiCliView + 'static, -{ - async fn get_stdin( - store: &Accessor, - ) -> wasmtime::Result> { +impl stdin::HostWithStore for WasiCli { + async fn get_stdin(store: &Accessor) -> wasmtime::Result> { store.with(|mut view| { let instance = view.instance(); let (tx, rx) = instance .stream(&mut view) .context("failed to create stream")?; - let stdin = view.get().cli().stdin.reader(); - view.spawn(InputTask { input: stdin, tx }); + let stdin = view.get().ctx.stdin.reader(); + view.spawn(InputTask { + rx: Box::into_pin(stdin), + tx, + }); Ok(rx) }) } } -impl stdin::Host for WasiCliImpl where T: WasiCliView {} +impl stdin::Host for WasiCliCtxView<'_> {} -impl stdout::HostConcurrent for WasiCli -where - T: WasiCliView + 'static, -{ - async fn set_stdout( +impl stdout::HostWithStore for WasiCli { + async fn set_stdout( store: &Accessor, data: StreamReader, ) -> wasmtime::Result<()> { store.with(|mut view| { - let stdout = view.get().cli().stdout.writer(); + let tx = view.get().ctx.stdout.writer(); view.spawn(OutputTask { - output: stdout, - data, + rx: data, + tx: Box::into_pin(tx), }); Ok(()) }) } } -impl stdout::Host for WasiCliImpl where T: WasiCliView {} +impl stdout::Host for WasiCliCtxView<'_> {} -impl stderr::HostConcurrent for WasiCli -where - T: WasiCliView + 'static, -{ - async fn set_stderr( +impl stderr::HostWithStore for WasiCli { + async fn set_stderr( store: &Accessor, data: StreamReader, ) -> wasmtime::Result<()> { store.with(|mut view| { - let stderr = view.get().cli().stderr.writer(); + let tx = view.get().ctx.stderr.writer(); view.spawn(OutputTask { - output: stderr, - data, + rx: data, + tx: Box::into_pin(tx), }); Ok(()) }) } } -impl stderr::Host for WasiCliImpl where T: WasiCliView {} +impl stderr::Host for WasiCliCtxView<'_> {} -impl environment::Host for WasiCliImpl -where - T: WasiCliView, -{ +impl environment::Host for WasiCliCtxView<'_> { fn get_environment(&mut self) -> wasmtime::Result> { - Ok(self.cli().environment.clone()) + Ok(self.ctx.environment.clone()) } fn get_arguments(&mut self) -> wasmtime::Result> { - Ok(self.cli().arguments.clone()) + Ok(self.ctx.arguments.clone()) } fn initial_cwd(&mut self) -> wasmtime::Result> { - Ok(self.cli().initial_cwd.clone()) + Ok(self.ctx.initial_cwd.clone()) } } -impl exit::Host for WasiCliImpl -where - T: WasiCliView, -{ +impl exit::Host for WasiCliCtxView<'_> { fn exit(&mut self, status: Result<(), ()>) -> wasmtime::Result<()> { let status = match status { Ok(()) => 0, diff --git a/crates/wasi/src/p3/cli/mod.rs b/crates/wasi/src/p3/cli/mod.rs index eecf7f491d..4d4f3041f3 100644 --- a/crates/wasi/src/p3/cli/mod.rs +++ b/crates/wasi/src/p3/cli/mod.rs @@ -1,48 +1,23 @@ mod host; -use crate::p3::ResourceView; +use crate::cli::{IsTerminal, WasiCliCtx}; use crate::p3::bindings::cli; -use core::fmt; +use std::sync::Arc; use tokio::io::{ AsyncRead, AsyncWrite, Empty, Stderr, Stdin, Stdout, empty, stderr, stdin, stdout, }; use wasmtime::component::{HasData, Linker, ResourceTable}; -#[repr(transparent)] -pub struct WasiCliImpl(pub T); - -impl WasiCliView for &mut T { - fn cli(&mut self) -> &WasiCliCtx { - (**self).cli() - } -} - -impl WasiCliView for WasiCliImpl { - fn cli(&mut self) -> &WasiCliCtx { - self.0.cli() - } -} - -impl ResourceView for WasiCliImpl { - fn table(&mut self) -> &mut ResourceTable { - self.0.table() - } +pub struct WasiCliCtxView<'a> { + pub ctx: &'a mut WasiCliCtx, Box>, + pub table: &'a mut ResourceTable, } -pub trait WasiCliView: ResourceView + Send { - fn cli(&mut self) -> &WasiCliCtx; +pub trait WasiCliView: Send { + fn cli(&mut self) -> WasiCliCtxView<'_>; } -pub struct WasiCliCtx { - pub environment: Vec<(String, String)>, - pub arguments: Vec, - pub initial_cwd: Option, - pub stdin: Box, - pub stdout: Box, - pub stderr: Box, -} - -impl Default for WasiCliCtx { +impl Default for WasiCliCtx, Box> { fn default() -> Self { Self { environment: Vec::default(), @@ -57,28 +32,26 @@ impl Default for WasiCliCtx { /// Add all WASI interfaces from this module into the `linker` provided. /// -/// This function will add the `async` variant of all interfaces into the -/// [`Linker`] provided. By `async` this means that this function is only -/// compatible with [`Config::async_support(true)`][async]. For embeddings with -/// async support disabled see [`add_to_linker_sync`] instead. -/// -/// This function will add all interfaces implemented by this crate to the +/// This function will add all interfaces implemented by this module to the /// [`Linker`], which corresponds to the `wasi:cli/imports` world supported by -/// this crate. +/// this module. /// -/// [async]: wasmtime::Config::async_support +/// This is low-level API for advanced use cases, +/// [`wasmtime_wasi::p3::add_to_linker`](crate::p3::add_to_linker) can be used instead +/// to add *all* wasip3 interfaces (including the ones from this module) to the `linker`. /// /// # Example /// /// ``` /// use wasmtime::{Engine, Result, Store, Config}; -/// use wasmtime::component::{ResourceTable, Linker}; -/// use wasmtime_wasi::p3::cli::{WasiCliView, WasiCliCtx}; -/// use wasmtime_wasi::p3::ResourceView; +/// use wasmtime::component::{Linker, ResourceTable}; +/// use wasmtime_wasi::cli::WasiCliCtx; +/// use wasmtime_wasi::p3::cli::{InputStream, OutputStream, WasiCliView, WasiCliCtxView}; /// /// fn main() -> Result<()> { /// let mut config = Config::new(); /// config.async_support(true); +/// config.wasm_component_model_async(true); /// let engine = Engine::new(&config)?; /// /// let mut linker = Linker::::new(&engine); @@ -87,10 +60,7 @@ impl Default for WasiCliCtx { /// /// let mut store = Store::new( /// &engine, -/// MyState { -/// cli: WasiCliCtx::default(), -/// table: ResourceTable::default(), -/// }, +/// MyState::default(), /// ); /// /// // ... use `linker` to instantiate within `store` ... @@ -98,17 +68,19 @@ impl Default for WasiCliCtx { /// Ok(()) /// } /// +/// #[derive(Default)] /// struct MyState { -/// cli: WasiCliCtx, +/// cli: WasiCliCtx, Box>, /// table: ResourceTable, /// } /// -/// impl ResourceView for MyState { -/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } -/// } -/// /// impl WasiCliView for MyState { -/// fn cli(&mut self) -> &WasiCliCtx { &self.cli } +/// fn cli(&mut self) -> WasiCliCtxView<'_> { +/// WasiCliCtxView { +/// ctx: &mut self.cli, +/// table: &mut self.table, +/// } +/// } /// } /// ``` pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> @@ -127,172 +99,140 @@ pub fn add_to_linker_with_options( where T: WasiCliView + 'static, { - add_stdio_to_linker(linker)?; - - let f: fn(&mut T) -> WasiCliImpl<&mut T> = |x| WasiCliImpl(x); - cli::environment::add_to_linker::<_, WasiCli>(linker, f)?; - cli::exit::add_to_linker::<_, WasiCli>(linker, exit_options, f)?; - cli::terminal_input::add_to_linker::<_, WasiCli>(linker, f)?; - cli::terminal_output::add_to_linker::<_, WasiCli>(linker, f)?; - cli::terminal_stdin::add_to_linker::<_, WasiCli>(linker, f)?; - cli::terminal_stdout::add_to_linker::<_, WasiCli>(linker, f)?; - cli::terminal_stderr::add_to_linker::<_, WasiCli>(linker, f)?; - Ok(()) -} - -/// TODO -pub fn add_stdio_to_linker(linker: &mut Linker) -> wasmtime::Result<()> -where - T: WasiCliView + 'static, -{ - let f: fn(&mut T) -> WasiCliImpl<&mut T> = |x| WasiCliImpl(x); - cli::stdin::add_to_linker::<_, WasiCli>(linker, f)?; - cli::stdout::add_to_linker::<_, WasiCli>(linker, f)?; - cli::stderr::add_to_linker::<_, WasiCli>(linker, f)?; + cli::exit::add_to_linker::<_, WasiCli>(linker, exit_options, T::cli)?; + cli::environment::add_to_linker::<_, WasiCli>(linker, T::cli)?; + cli::stdin::add_to_linker::<_, WasiCli>(linker, T::cli)?; + cli::stdout::add_to_linker::<_, WasiCli>(linker, T::cli)?; + cli::stderr::add_to_linker::<_, WasiCli>(linker, T::cli)?; + cli::terminal_input::add_to_linker::<_, WasiCli>(linker, T::cli)?; + cli::terminal_output::add_to_linker::<_, WasiCli>(linker, T::cli)?; + cli::terminal_stdin::add_to_linker::<_, WasiCli>(linker, T::cli)?; + cli::terminal_stdout::add_to_linker::<_, WasiCli>(linker, T::cli)?; + cli::terminal_stderr::add_to_linker::<_, WasiCli>(linker, T::cli)?; Ok(()) } -struct WasiCli(T); +struct WasiCli; -impl HasData for WasiCli { - type Data<'a> = WasiCliImpl<&'a mut T>; +impl HasData for WasiCli { + type Data<'a> = WasiCliCtxView<'a>; } -/// An error returned from the `proc_exit` host syscall. -/// -/// Embedders can test if an error returned from wasm is this error, in which -/// case it may signal a non-fatal trap. -#[derive(Debug)] -pub struct I32Exit(pub i32); - -impl fmt::Display for I32Exit { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Exited with i32 exit status {}", self.0) - } -} - -impl std::error::Error for I32Exit {} - pub struct TerminalInput; pub struct TerminalOutput; -pub trait IsTerminal { - /// Returns whether this stream is backed by a TTY. - fn is_terminal(&self) -> bool; +pub trait InputStream: IsTerminal + Send { + fn reader(&self) -> Box; } -impl IsTerminal for Empty { - fn is_terminal(&self) -> bool { - false +impl InputStream for &T { + fn reader(&self) -> Box { + T::reader(self) } } -pub trait InputStream: IsTerminal { - fn reader(&self) -> Box; -} - -pub trait OutputStream: IsTerminal { - fn writer(&self) -> Box; +impl InputStream for &mut T { + fn reader(&self) -> Box { + T::reader(self) + } } -impl InputStream for Empty { - fn reader(&self) -> Box { - Box::new(empty()) +impl InputStream for Box { + fn reader(&self) -> Box { + T::reader(self) } } -impl OutputStream for Empty { - fn writer(&self) -> Box { - Box::new(empty()) +impl InputStream for Arc { + fn reader(&self) -> Box { + T::reader(self) } } -impl IsTerminal for std::io::Empty { - fn is_terminal(&self) -> bool { - false +impl InputStream for Empty { + fn reader(&self) -> Box { + Box::new(empty()) } } impl InputStream for std::io::Empty { - fn reader(&self) -> Box { + fn reader(&self) -> Box { Box::new(empty()) } } -impl OutputStream for std::io::Empty { - fn writer(&self) -> Box { - Box::new(empty()) +impl InputStream for Stdin { + fn reader(&self) -> Box { + Box::new(stdin()) } } -impl IsTerminal for Stdin { - fn is_terminal(&self) -> bool { - std::io::stdin().is_terminal() +impl InputStream for std::io::Stdin { + fn reader(&self) -> Box { + Box::new(stdin()) } } -impl InputStream for Stdin { - fn reader(&self) -> Box { - Box::new(stdin()) +pub trait OutputStream: IsTerminal + Send { + fn writer(&self) -> Box; +} + +impl OutputStream for &T { + fn writer(&self) -> Box { + T::writer(self) } } -impl IsTerminal for std::io::Stdin { - fn is_terminal(&self) -> bool { - std::io::IsTerminal::is_terminal(self) +impl OutputStream for &mut T { + fn writer(&self) -> Box { + T::writer(self) } } -impl InputStream for std::io::Stdin { - fn reader(&self) -> Box { - Box::new(stdin()) +impl OutputStream for Box { + fn writer(&self) -> Box { + T::writer(self) } } -impl IsTerminal for Stdout { - fn is_terminal(&self) -> bool { - std::io::stdout().is_terminal() +impl OutputStream for Arc { + fn writer(&self) -> Box { + T::writer(self) } } -impl OutputStream for Stdout { - fn writer(&self) -> Box { - Box::new(stdout()) +impl OutputStream for Empty { + fn writer(&self) -> Box { + Box::new(empty()) } } -impl IsTerminal for std::io::Stdout { - fn is_terminal(&self) -> bool { - std::io::IsTerminal::is_terminal(self) +impl OutputStream for std::io::Empty { + fn writer(&self) -> Box { + Box::new(empty()) } } -impl OutputStream for std::io::Stdout { - fn writer(&self) -> Box { +impl OutputStream for Stdout { + fn writer(&self) -> Box { Box::new(stdout()) } } -impl IsTerminal for Stderr { - fn is_terminal(&self) -> bool { - std::io::stderr().is_terminal() +impl OutputStream for std::io::Stdout { + fn writer(&self) -> Box { + Box::new(stdout()) } } impl OutputStream for Stderr { - fn writer(&self) -> Box { + fn writer(&self) -> Box { Box::new(stderr()) } } -impl IsTerminal for std::io::Stderr { - fn is_terminal(&self) -> bool { - std::io::IsTerminal::is_terminal(self) - } -} - impl OutputStream for std::io::Stderr { - fn writer(&self) -> Box { + fn writer(&self) -> Box { Box::new(stderr()) } } diff --git a/crates/wasi/src/p3/clocks/host.rs b/crates/wasi/src/p3/clocks/host.rs index 5fb024a1d5..52b3f317f6 100644 --- a/crates/wasi/src/p3/clocks/host.rs +++ b/crates/wasi/src/p3/clocks/host.rs @@ -4,8 +4,9 @@ use cap_std::time::SystemTime; use tokio::time::sleep; use wasmtime::component::Accessor; +use crate::clocks::WasiClocksCtx; use crate::p3::bindings::clocks::{monotonic_clock, wall_clock}; -use crate::p3::clocks::{WasiClocks, WasiClocksImpl, WasiClocksView}; +use crate::p3::clocks::WasiClocks; impl TryFrom for wall_clock::Datetime { type Error = wasmtime::Error; @@ -21,12 +22,9 @@ impl TryFrom for wall_clock::Datetime { } } -impl wall_clock::Host for WasiClocksImpl -where - T: WasiClocksView, -{ +impl wall_clock::Host for WasiClocksCtx { fn now(&mut self) -> wasmtime::Result { - let now = self.clocks().wall_clock.now(); + let now = self.wall_clock.now(); Ok(wall_clock::Datetime { seconds: now.as_secs(), nanoseconds: now.subsec_nanos(), @@ -34,7 +32,7 @@ where } fn resolution(&mut self) -> wasmtime::Result { - let res = self.clocks().wall_clock.resolution(); + let res = self.wall_clock.resolution(); Ok(wall_clock::Datetime { seconds: res.as_secs(), nanoseconds: res.subsec_nanos(), @@ -42,15 +40,12 @@ where } } -impl monotonic_clock::HostConcurrent for WasiClocks -where - T: WasiClocksView + 'static, -{ +impl monotonic_clock::HostWithStore for WasiClocks { async fn wait_until( store: &Accessor, when: monotonic_clock::Instant, ) -> wasmtime::Result<()> { - let clock_now = store.with(|mut view| view.get().clocks().monotonic_clock.now()); + let clock_now = store.with(|mut view| view.get().monotonic_clock.now()); if when > clock_now { sleep(Duration::from_nanos(when - clock_now)).await; }; @@ -68,15 +63,12 @@ where } } -impl monotonic_clock::Host for WasiClocksImpl -where - T: WasiClocksView, -{ +impl monotonic_clock::Host for WasiClocksCtx { fn now(&mut self) -> wasmtime::Result { - Ok(self.clocks().monotonic_clock.now()) + Ok(self.monotonic_clock.now()) } fn resolution(&mut self) -> wasmtime::Result { - Ok(self.clocks().monotonic_clock.resolution()) + Ok(self.monotonic_clock.resolution()) } } diff --git a/crates/wasi/src/p3/clocks/mod.rs b/crates/wasi/src/p3/clocks/mod.rs index 6d6167a550..d8e8f3bdcb 100644 --- a/crates/wasi/src/p3/clocks/mod.rs +++ b/crates/wasi/src/p3/clocks/mod.rs @@ -1,6 +1,6 @@ mod host; -use crate::clocks::{WasiClocksImpl, WasiClocksView}; +use crate::clocks::{WasiClocksCtx, WasiClocksView}; use crate::p3::bindings::clocks; use wasmtime::component::{HasData, Linker}; @@ -48,28 +48,20 @@ use wasmtime::component::{HasData, Linker}; /// } /// /// impl WasiClocksView for MyState { -/// fn clocks(&mut self) -> &WasiClocksCtx { &self.clocks } +/// fn clocks(&mut self) -> &mut WasiClocksCtx { &mut self.clocks } /// } /// ``` -pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> { - add_to_linker_impl(linker, |x| WasiClocksImpl(x)) -} - -pub(crate) fn add_to_linker_impl( - linker: &mut Linker, - host_getter: fn(&mut T) -> WasiClocksImpl<&mut U>, -) -> wasmtime::Result<()> +pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> where - T: Send, - U: WasiClocksView + 'static, + T: WasiClocksView + 'static, { - clocks::monotonic_clock::add_to_linker::<_, WasiClocks>(linker, host_getter)?; - clocks::wall_clock::add_to_linker::<_, WasiClocks>(linker, host_getter)?; + clocks::monotonic_clock::add_to_linker::<_, WasiClocks>(linker, T::clocks)?; + clocks::wall_clock::add_to_linker::<_, WasiClocks>(linker, T::clocks)?; Ok(()) } -struct WasiClocks(T); +struct WasiClocks; -impl HasData for WasiClocks { - type Data<'a> = WasiClocksImpl<&'a mut T>; +impl HasData for WasiClocks { + type Data<'a> = &'a mut WasiClocksCtx; } diff --git a/crates/wasi/src/p3/ctx.rs b/crates/wasi/src/p3/ctx.rs index 8e10e394fd..6876038f25 100644 --- a/crates/wasi/src/p3/ctx.rs +++ b/crates/wasi/src/p3/ctx.rs @@ -1,7 +1,9 @@ +use crate::cli::WasiCliCtx; use crate::clocks::{HostMonotonicClock, HostWallClock, WasiClocksCtx}; -use crate::net::SocketAddrUse; +use crate::p3::cli::{InputStream, OutputStream}; use crate::p3::filesystem::Dir; use crate::random::WasiRandomCtx; +use crate::sockets::{SocketAddrUse, WasiSocketsCtx}; use crate::{DirPerms, FilePerms, OpenMode}; use anyhow::Result; use cap_rand::RngCore; @@ -11,6 +13,7 @@ use std::mem; use std::net::SocketAddr; use std::path::Path; use std::pin::Pin; +use tokio::io::{empty, stderr, stdin, stdout}; /// Builder-style structure used to create a [`WasiCtx`]. /// @@ -33,15 +36,18 @@ use std::pin::Pin; /// /// [`Store`]: wasmtime::Store pub struct WasiCtxBuilder { - common: crate::WasiCtxBuilder, - // TODO: implement CLI and filesystem - stdin: Box<()>, - stdout: Box<()>, - stderr: Box<()>, + common: crate::WasiCtxBuilder, Box>, + // TODO: implement filesystem preopens: Vec<(Dir, String)>, built: bool, } +impl Default for crate::WasiCtxBuilder, Box> { + fn default() -> Self { + crate::WasiCtxBuilder::new(Box::new(empty()), Box::new(empty()), Box::new(empty())) + } +} + impl WasiCtxBuilder { /// Creates a builder for a new context with default parameters set. /// @@ -62,10 +68,7 @@ impl WasiCtxBuilder { /// methods below. pub fn new() -> Self { Self { - common: crate::WasiCtxBuilder::new(), - stdin: Box::default(), - stdout: Box::default(), - stderr: Box::default(), + common: crate::WasiCtxBuilder::default(), preopens: Vec::default(), built: false, } @@ -77,23 +80,20 @@ impl WasiCtxBuilder { /// /// Note that inheriting the process's stdin can also be done through /// [`inherit_stdin`](WasiCtxBuilder::inherit_stdin). - // TODO: implement - pub fn stdin(&mut self, stdin: ()) -> &mut Self { - self.stdin = Box::new(stdin); + pub fn stdin(&mut self, stdin: impl InputStream + 'static) -> &mut Self { + self.common.stdin(Box::new(stdin)); self } /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stdout. - // TODO: implement - pub fn stdout(&mut self, stdout: ()) -> &mut Self { - self.stdout = Box::new(stdout); + pub fn stdout(&mut self, stdout: impl OutputStream + 'static) -> &mut Self { + self.common.stdout(Box::new(stdout)); self } /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stderr. - // TODO: implement - pub fn stderr(&mut self, stderr: ()) -> &mut Self { - self.stderr = Box::new(stderr); + pub fn stderr(&mut self, stderr: impl OutputStream + 'static) -> &mut Self { + self.common.stderr(Box::new(stderr)); self } @@ -104,8 +104,7 @@ impl WasiCtxBuilder { /// when using this it's typically best to have a single wasm instance in /// the process using this. pub fn inherit_stdin(&mut self) -> &mut Self { - // TODO: implement - self.stdin(()) + self.stdin(stdin()) } /// Configures this context's stdout stream to write to the host process's @@ -114,8 +113,7 @@ impl WasiCtxBuilder { /// Note that unlike [`inherit_stdin`](WasiCtxBuilder::inherit_stdin) /// multiple instances printing to stdout works well. pub fn inherit_stdout(&mut self) -> &mut Self { - // TODO: implement - self.stdout(()) + self.stdout(stdout()) } /// Configures this context's stderr stream to write to the host process's @@ -124,8 +122,7 @@ impl WasiCtxBuilder { /// Note that unlike [`inherit_stdin`](WasiCtxBuilder::inherit_stdin) /// multiple instances printing to stderr works well. pub fn inherit_stderr(&mut self) -> &mut Self { - // TODO: implement - self.stderr(()) + self.stderr(stderr()) } /// Configures all of stdin, stdout, and stderr to be inherited from the @@ -430,13 +427,25 @@ impl WasiCtxBuilder { assert!(!self.built); let Self { - common: crate::WasiCtxBuilder { random, clocks, .. }, + common: + crate::WasiCtxBuilder { + random, + clocks, + cli, + sockets, + .. + }, built: _, .. } = mem::replace(self, Self::new()); self.built = true; - WasiCtx { random, clocks } + WasiCtx { + cli, + clocks, + random, + sockets, + } } } @@ -456,14 +465,21 @@ impl WasiCtxBuilder { /// # Example /// /// ``` -/// use wasmtime_wasi::p3::{WasiCtx, WasiView, WasiCtxBuilder}; +/// use wasmtime::component::ResourceTable; +/// use wasmtime_wasi::p3::{WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView}; /// /// struct MyState { /// ctx: WasiCtx, +/// table: ResourceTable, /// } /// /// impl WasiView for MyState { -/// fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx } +/// fn ctx(&mut self) -> WasiCtxView<'_> { +/// WasiCtxView{ +/// ctx: &mut self.ctx, +/// table: &mut self.table, +/// } +/// } /// } /// /// impl MyState { @@ -475,14 +491,17 @@ impl WasiCtxBuilder { /// /// MyState { /// ctx: wasi.build(), +/// table: ResourceTable::default(), /// } /// } /// } /// ``` #[derive(Default)] pub struct WasiCtx { - pub random: WasiRandomCtx, + pub cli: WasiCliCtx, Box>, pub clocks: WasiClocksCtx, + pub random: WasiRandomCtx, + pub sockets: WasiSocketsCtx, } impl WasiCtx { diff --git a/crates/wasi/src/p3/filesystem/host.rs b/crates/wasi/src/p3/filesystem/host.rs index 58e3eef5e7..bd8b35066c 100644 --- a/crates/wasi/src/p3/filesystem/host.rs +++ b/crates/wasi/src/p3/filesystem/host.rs @@ -4,8 +4,8 @@ use anyhow::{Context as _, anyhow}; use system_interface::fs::FileIoExt as _; use tokio::sync::mpsc; use wasmtime::component::{ - Accessor, AccessorTask, FutureReader, Lower, Resource, ResourceTable, StreamReader, - WithAccessor, WithAccessorAndValue, + Accessor, AccessorTask, FutureReader, FutureWriter, GuardedFutureReader, GuardedFutureWriter, + GuardedStreamReader, GuardedStreamWriter, Lower, Resource, ResourceTable, StreamReader, }; use crate::p3::bindings::filesystem::types::{ @@ -48,9 +48,7 @@ where impl types::Host for WasiFilesystemImpl where T: WasiFilesystemView {} -impl types::HostConcurrent for WasiFilesystem where T: WasiFilesystemView + 'static {} - -impl types::HostDescriptorConcurrent for WasiFilesystem +impl types::HostDescriptorWithStore for WasiFilesystem where T: WasiFilesystemView + 'static, { @@ -65,14 +63,14 @@ where .stream(&mut view) .context("failed to create stream")?; let res = instance - .future(&mut view) + .future(&mut view, || Ok(())) .context("failed to create future")?; anyhow::Ok((data, res)) })?; - let data_tx = WithAccessor::new(store, data_tx); - let data_rx = WithAccessor::new(store, data_rx); - let res_tx = WithAccessorAndValue::new(store, res_tx, Ok(())); - let res_rx = WithAccessor::new(store, res_rx); + let data_tx = GuardedStreamWriter::new(store, data_tx); + let data_rx = GuardedStreamReader::new(store, data_rx); + let res_tx = GuardedFutureWriter::new(store, res_tx); + let res_rx = GuardedFutureReader::new(store, res_rx); let result = store.with(|mut view| { let mut binding = view.get(); @@ -136,8 +134,8 @@ where Ok((task_rx, id, tasks)) => { store.spawn(ReadTask { io: IoTask { - data: data_tx.into_inner(), - result: res_tx.into_inner(), + data: data_tx.into(), + result: res_tx.into(), rx: task_rx, }, id, @@ -145,7 +143,7 @@ where }); } Err(err) => { - let res_tx = res_tx.into_inner(); + let res_tx = FutureWriter::from(res_tx); store.spawn_fn_box(move |store| { Box::pin(async move { res_tx.write(store, Err(err)).await; @@ -155,7 +153,7 @@ where } } - Ok((data_rx.into_inner(), res_rx.into_inner())) + Ok((data_rx.into(), res_rx.into())) } async fn write_via_stream( @@ -327,14 +325,14 @@ where .stream(&mut view) .context("failed to create stream")?; let res = instance - .future(&mut view) + .future(&mut view, || Ok(())) .context("failed to create future")?; anyhow::Ok((data, res)) })?; - let data_tx = WithAccessor::new(store, data_tx); - let data_rx = WithAccessor::new(store, data_rx); - let res_tx = WithAccessorAndValue::new(store, res_tx, Ok(())); - let res_rx = WithAccessor::new(store, res_rx); + let data_tx = GuardedStreamWriter::new(store, data_tx); + let data_rx = GuardedStreamReader::new(store, data_rx); + let res_tx = GuardedFutureWriter::new(store, res_tx); + let res_rx = GuardedFutureReader::new(store, res_rx); let result = store.with(|mut view| { let mut binding = view.get(); @@ -435,8 +433,8 @@ where Ok((task_rx, id, tasks)) => { store.spawn(ReadTask { io: IoTask { - data: data_tx.into_inner(), - result: res_tx.into_inner(), + data: data_tx.into(), + result: res_tx.into(), rx: task_rx, }, id, @@ -444,7 +442,7 @@ where }); } Err(err) => { - let res_tx = res_tx.into_inner(); + let res_tx = FutureWriter::from(res_tx); store.spawn_fn_box(move |store| { Box::pin(async move { res_tx.write(store, Err(err)).await; @@ -454,7 +452,7 @@ where } } - Ok((data_rx.into_inner(), res_rx.into_inner())) + Ok((data_rx.into(), res_rx.into())) } async fn sync( diff --git a/crates/wasi/src/p3/filesystem/mod.rs b/crates/wasi/src/p3/filesystem/mod.rs index ec3663b5f0..b106cc0944 100644 --- a/crates/wasi/src/p3/filesystem/mod.rs +++ b/crates/wasi/src/p3/filesystem/mod.rs @@ -1,4 +1,4 @@ -use crate::fs::{DirPerms, FilePerms, OpenMode}; +use crate::filesystem::{DirPerms, FilePerms, OpenMode}; use crate::p3::bindings::clocks::wall_clock; use crate::p3::bindings::filesystem; use crate::p3::bindings::filesystem::types::{ diff --git a/crates/wasi/src/p3/mod.rs b/crates/wasi/src/p3/mod.rs index 15a1286ea6..506e4e3da1 100644 --- a/crates/wasi/src/p3/mod.rs +++ b/crates/wasi/src/p3/mod.rs @@ -1,6 +1,4 @@ -use crate::clocks::WasiClocksImpl; use crate::p3::bindings::LinkOptions; -use crate::random::WasiRandomImpl; use anyhow::{Result, anyhow, bail}; use core::future::Future; use core::ops::{Deref, DerefMut}; @@ -9,8 +7,8 @@ use std::pin::Pin; use std::sync::Arc; use tokio::sync::mpsc; use wasmtime::component::{ - AbortHandle, Access, Accessor, AccessorTask, FutureWriter, HasData, Linker, Lower, - ResourceTable, StreamWriter, VecBuffer, WithAccessor, + AbortHandle, Access, Accessor, AccessorTask, FutureWriter, GuardedStreamWriter, HasData, + Linker, Lower, ResourceTable, StreamWriter, VecBuffer, }; pub mod bindings; @@ -23,7 +21,10 @@ pub mod sockets; mod view; pub use self::ctx::{WasiCtx, WasiCtxBuilder}; -pub use self::view::{WasiImpl, WasiView}; +pub use self::view::{WasiCtxView, WasiView}; + +// Default buffer capacity to use for reads of byte-sized values. +const DEFAULT_BUFFER_CAPACITY: usize = 8192; pub struct AbortOnDropHandle(pub AbortHandle); @@ -50,13 +51,9 @@ impl Drop for AbortOnDropHandle { /// /// ```ignore(TODO fix after upstreaming is complete) /// use wasmtime::{Engine, Result, Store, Config}; -/// use wasmtime::component::{ResourceTable, Linker}; -/// use wasmtime_wasi::p3::cli::{WasiCliCtx, WasiCliView}; -/// use wasmtime_wasi::p3::clocks::{WasiClocksCtx, WasiClocksView}; /// use wasmtime_wasi::p3::filesystem::{WasiFilesystemCtx, WasiFilesystemView}; -/// use wasmtime_wasi::p3::random::{WasiRandomCtx, WasiRandomView}; -/// use wasmtime_wasi::p3::sockets::{WasiSocketsCtx, WasiSocketsView}; -/// use wasmtime_wasi::p3::ResourceView; +/// use wasmtime::component::{Linker, ResourceTable}; +/// use wasmtime_wasi::p3::{WasiCtx, WasiCtxView, WasiView}; /// /// fn main() -> Result<()> { /// let mut config = Config::new(); @@ -79,45 +76,28 @@ impl Drop for AbortOnDropHandle { /// /// #[derive(Default)] /// struct MyState { -/// cli: WasiCliCtx, -/// clocks: WasiClocksCtx, /// filesystem: WasiFilesystemCtx, -/// random: WasiRandomCtx, -/// sockets: WasiSocketsCtx, +/// ctx: WasiCtx, /// table: ResourceTable, /// } /// -/// impl ResourceView for MyState { -/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } -/// } -/// -/// impl WasiCliView for MyState { -/// fn cli(&mut self) -> &WasiCliCtx { &self.cli } -/// } -/// -/// impl WasiClocksView for MyState { -/// fn clocks(&mut self) -> &WasiClocksCtx { &self.clocks } -/// } /// /// impl WasiFilesystemView for MyState { /// fn filesystem(&self) -> &WasiFilesystemCtx { &self.filesystem } /// } /// -/// impl WasiRandomView for MyState { -/// fn random(&mut self) -> &mut WasiRandomCtx { &mut self.random } -/// } -/// -/// impl WasiSocketsView for MyState { -/// fn sockets(&self) -> &WasiSocketsCtx { &self.sockets } +/// impl WasiView for MyState { +/// fn ctx(&mut self) -> WasiCtxView<'_> { +/// WasiCtxView{ +/// ctx: &mut self.ctx, +/// table: &mut self.table, +/// } +/// } /// } /// ``` pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> where - T: WasiView - + sockets::WasiSocketsView - + filesystem::WasiFilesystemView - + cli::WasiCliView - + 'static, + T: WasiView + filesystem::WasiFilesystemView + 'static, { let options = LinkOptions::default(); add_to_linker_with_options(linker, &options) @@ -127,19 +107,15 @@ where pub fn add_to_linker_with_options( linker: &mut Linker, options: &LinkOptions, -) -> anyhow::Result<()> +) -> wasmtime::Result<()> where - T: WasiView - + sockets::WasiSocketsView - + filesystem::WasiFilesystemView - + cli::WasiCliView - + 'static, + T: WasiView + filesystem::WasiFilesystemView + 'static, { - clocks::add_to_linker_impl(linker, |x| WasiClocksImpl(&mut x.ctx().clocks))?; - random::add_to_linker_impl(linker, |x| WasiRandomImpl(&mut x.ctx().random))?; + cli::add_to_linker_with_options(linker, &options.into())?; + clocks::add_to_linker(linker)?; + random::add_to_linker(linker)?; sockets::add_to_linker(linker)?; filesystem::add_to_linker(linker)?; - cli::add_to_linker_with_options(linker, &options.into())?; Ok(()) } @@ -243,7 +219,7 @@ where E: Lower + Send + Sync + 'static, { async fn run(mut self, store: &Accessor) -> wasmtime::Result<()> { - let mut tx = WithAccessor::new(store, self.data); + let mut tx = GuardedStreamWriter::new(store, self.data); let res = loop { match self.rx.recv().await { None => { @@ -251,7 +227,7 @@ where break Ok(()); } Some(Ok(buf)) => { - tx.write_all(store, VecBuffer::from(buf)).await; + tx.write_all(VecBuffer::from(buf)).await; if tx.is_closed() { break Ok(()); } diff --git a/crates/wasi/src/p3/random/host.rs b/crates/wasi/src/p3/random/host.rs index c23904ca44..7e2b71640d 100644 --- a/crates/wasi/src/p3/random/host.rs +++ b/crates/wasi/src/p3/random/host.rs @@ -2,46 +2,37 @@ use cap_rand::Rng; use cap_rand::distributions::Standard; use crate::p3::bindings::random::{insecure, insecure_seed, random}; -use crate::p3::random::{WasiRandomImpl, WasiRandomView}; +use crate::random::WasiRandomCtx; -impl random::Host for WasiRandomImpl -where - T: WasiRandomView, -{ +impl random::Host for WasiRandomCtx { fn get_random_bytes(&mut self, len: u64) -> wasmtime::Result> { - Ok((&mut self.random().random) + Ok((&mut self.random) .sample_iter(Standard) .take(len as usize) .collect()) } fn get_random_u64(&mut self) -> wasmtime::Result { - Ok(self.random().random.sample(Standard)) + Ok(self.random.sample(Standard)) } } -impl insecure::Host for WasiRandomImpl -where - T: WasiRandomView, -{ +impl insecure::Host for WasiRandomCtx { fn get_insecure_random_bytes(&mut self, len: u64) -> wasmtime::Result> { - Ok((&mut self.random().insecure_random) + Ok((&mut self.insecure_random) .sample_iter(Standard) .take(len as usize) .collect()) } fn get_insecure_random_u64(&mut self) -> wasmtime::Result { - Ok(self.random().insecure_random.sample(Standard)) + Ok(self.insecure_random.sample(Standard)) } } -impl insecure_seed::Host for WasiRandomImpl -where - T: WasiRandomView, -{ +impl insecure_seed::Host for WasiRandomCtx { fn insecure_seed(&mut self) -> wasmtime::Result<(u64, u64)> { - let seed: u128 = self.random().insecure_random_seed; + let seed: u128 = self.insecure_random_seed; Ok((seed as u64, (seed >> 64) as u64)) } } diff --git a/crates/wasi/src/p3/random/mod.rs b/crates/wasi/src/p3/random/mod.rs index 8f41008e23..68a50520da 100644 --- a/crates/wasi/src/p3/random/mod.rs +++ b/crates/wasi/src/p3/random/mod.rs @@ -1,8 +1,8 @@ mod host; use crate::p3::bindings::random; -use crate::random::{WasiRandomImpl, WasiRandomView}; -use wasmtime::component::{HasData, Linker}; +use crate::random::{WasiRandom, WasiRandomView}; +use wasmtime::component::Linker; /// Add all WASI interfaces from this module into the `linker` provided. /// @@ -19,7 +19,7 @@ use wasmtime::component::{HasData, Linker}; /// /// ``` /// use wasmtime::{Engine, Result, Store, Config}; -/// use wasmtime::component::{ResourceTable, Linker}; +/// use wasmtime::component::Linker; /// use wasmtime_wasi::random::{WasiRandomView, WasiRandomCtx}; /// /// fn main() -> Result<()> { @@ -51,26 +51,12 @@ use wasmtime::component::{HasData, Linker}; /// fn random(&mut self) -> &mut WasiRandomCtx { &mut self.random } /// } /// ``` -pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> { - add_to_linker_impl(linker, |x| WasiRandomImpl(x)) -} - -pub(crate) fn add_to_linker_impl( - linker: &mut Linker, - host_getter: fn(&mut T) -> WasiRandomImpl<&mut U>, -) -> wasmtime::Result<()> +pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> where - T: Send, - U: WasiRandomView + 'static, + T: WasiRandomView + 'static, { - random::random::add_to_linker::<_, WasiRandom>(linker, host_getter)?; - random::insecure::add_to_linker::<_, WasiRandom>(linker, host_getter)?; - random::insecure_seed::add_to_linker::<_, WasiRandom>(linker, host_getter)?; + random::random::add_to_linker::<_, WasiRandom>(linker, T::random)?; + random::insecure::add_to_linker::<_, WasiRandom>(linker, T::random)?; + random::insecure_seed::add_to_linker::<_, WasiRandom>(linker, T::random)?; Ok(()) } - -struct WasiRandom(T); - -impl HasData for WasiRandom { - type Data<'a> = WasiRandomImpl<&'a mut T>; -} diff --git a/crates/wasi/src/p3/sockets/conv.rs b/crates/wasi/src/p3/sockets/conv.rs new file mode 100644 index 0000000000..ee4920dfa0 --- /dev/null +++ b/crates/wasi/src/p3/sockets/conv.rs @@ -0,0 +1,227 @@ +use core::net::{IpAddr, SocketAddr, SocketAddrV4, SocketAddrV6}; + +use std::net::ToSocketAddrs; + +use rustix::io::Errno; +use tracing::debug; + +use crate::p3::bindings::sockets::types; +use crate::sockets::util::{from_ipv4_addr, from_ipv6_addr, to_ipv4_addr, to_ipv6_addr}; + +impl From for types::IpAddress { + fn from(addr: IpAddr) -> Self { + match addr { + IpAddr::V4(v4) => Self::Ipv4(from_ipv4_addr(v4)), + IpAddr::V6(v6) => Self::Ipv6(from_ipv6_addr(v6)), + } + } +} + +impl From for IpAddr { + fn from(addr: types::IpAddress) -> Self { + match addr { + types::IpAddress::Ipv4(v4) => Self::V4(to_ipv4_addr(v4)), + types::IpAddress::Ipv6(v6) => Self::V6(to_ipv6_addr(v6)), + } + } +} + +impl From for SocketAddr { + fn from(addr: types::IpSocketAddress) -> Self { + match addr { + types::IpSocketAddress::Ipv4(ipv4) => Self::V4(ipv4.into()), + types::IpSocketAddress::Ipv6(ipv6) => Self::V6(ipv6.into()), + } + } +} + +impl From for types::IpSocketAddress { + fn from(addr: SocketAddr) -> Self { + match addr { + SocketAddr::V4(v4) => Self::Ipv4(v4.into()), + SocketAddr::V6(v6) => Self::Ipv6(v6.into()), + } + } +} + +impl From for SocketAddrV4 { + fn from(addr: types::Ipv4SocketAddress) -> Self { + Self::new(to_ipv4_addr(addr.address), addr.port) + } +} + +impl From for types::Ipv4SocketAddress { + fn from(addr: SocketAddrV4) -> Self { + Self { + address: from_ipv4_addr(*addr.ip()), + port: addr.port(), + } + } +} + +impl From for SocketAddrV6 { + fn from(addr: types::Ipv6SocketAddress) -> Self { + Self::new( + to_ipv6_addr(addr.address), + addr.port, + addr.flow_info, + addr.scope_id, + ) + } +} + +impl From for types::Ipv6SocketAddress { + fn from(addr: SocketAddrV6) -> Self { + Self { + address: from_ipv6_addr(*addr.ip()), + port: addr.port(), + flow_info: addr.flowinfo(), + scope_id: addr.scope_id(), + } + } +} + +impl ToSocketAddrs for types::IpSocketAddress { + type Iter = ::Iter; + + fn to_socket_addrs(&self) -> std::io::Result { + SocketAddr::from(*self).to_socket_addrs() + } +} + +impl ToSocketAddrs for types::Ipv4SocketAddress { + type Iter = ::Iter; + + fn to_socket_addrs(&self) -> std::io::Result { + SocketAddrV4::from(*self).to_socket_addrs() + } +} + +impl ToSocketAddrs for types::Ipv6SocketAddress { + type Iter = ::Iter; + + fn to_socket_addrs(&self) -> std::io::Result { + SocketAddrV6::from(*self).to_socket_addrs() + } +} + +impl From for cap_net_ext::AddressFamily { + fn from(family: types::IpAddressFamily) -> Self { + match family { + types::IpAddressFamily::Ipv4 => Self::Ipv4, + types::IpAddressFamily::Ipv6 => Self::Ipv6, + } + } +} + +impl From for types::IpAddressFamily { + fn from(family: cap_net_ext::AddressFamily) -> Self { + match family { + cap_net_ext::AddressFamily::Ipv4 => Self::Ipv4, + cap_net_ext::AddressFamily::Ipv6 => Self::Ipv6, + } + } +} + +impl From for types::ErrorCode { + fn from(value: std::io::Error) -> Self { + (&value).into() + } +} + +impl From<&std::io::Error> for types::ErrorCode { + fn from(value: &std::io::Error) -> Self { + // Attempt the more detailed native error code first: + if let Some(errno) = Errno::from_io_error(value) { + return errno.into(); + } + + match value.kind() { + std::io::ErrorKind::AddrInUse => Self::AddressInUse, + std::io::ErrorKind::AddrNotAvailable => Self::AddressNotBindable, + std::io::ErrorKind::ConnectionAborted => Self::ConnectionAborted, + std::io::ErrorKind::ConnectionRefused => Self::ConnectionRefused, + std::io::ErrorKind::ConnectionReset => Self::ConnectionReset, + std::io::ErrorKind::InvalidInput => Self::InvalidArgument, + std::io::ErrorKind::NotConnected => Self::InvalidState, + std::io::ErrorKind::OutOfMemory => Self::OutOfMemory, + std::io::ErrorKind::PermissionDenied => Self::AccessDenied, + std::io::ErrorKind::TimedOut => Self::Timeout, + std::io::ErrorKind::Unsupported => Self::NotSupported, + _ => { + debug!("unknown I/O error: {value}"); + Self::Unknown + } + } + } +} + +impl From for types::ErrorCode { + fn from(value: Errno) -> Self { + (&value).into() + } +} + +impl From<&Errno> for types::ErrorCode { + fn from(value: &Errno) -> Self { + match *value { + #[cfg(not(windows))] + Errno::PERM => Self::AccessDenied, + Errno::ACCESS => Self::AccessDenied, + Errno::ADDRINUSE => Self::AddressInUse, + Errno::ADDRNOTAVAIL => Self::AddressNotBindable, + Errno::TIMEDOUT => Self::Timeout, + Errno::CONNREFUSED => Self::ConnectionRefused, + Errno::CONNRESET => Self::ConnectionReset, + Errno::CONNABORTED => Self::ConnectionAborted, + Errno::INVAL => Self::InvalidArgument, + Errno::HOSTUNREACH => Self::RemoteUnreachable, + Errno::HOSTDOWN => Self::RemoteUnreachable, + Errno::NETDOWN => Self::RemoteUnreachable, + Errno::NETUNREACH => Self::RemoteUnreachable, + #[cfg(target_os = "linux")] + Errno::NONET => Self::RemoteUnreachable, + Errno::ISCONN => Self::InvalidState, + Errno::NOTCONN => Self::InvalidState, + Errno::DESTADDRREQ => Self::InvalidState, + Errno::MSGSIZE => Self::DatagramTooLarge, + #[cfg(not(windows))] + Errno::NOMEM => Self::OutOfMemory, + Errno::NOBUFS => Self::OutOfMemory, + Errno::OPNOTSUPP => Self::NotSupported, + Errno::NOPROTOOPT => Self::NotSupported, + Errno::PFNOSUPPORT => Self::NotSupported, + Errno::PROTONOSUPPORT => Self::NotSupported, + Errno::PROTOTYPE => Self::NotSupported, + Errno::SOCKTNOSUPPORT => Self::NotSupported, + Errno::AFNOSUPPORT => Self::NotSupported, + + // FYI, EINPROGRESS should have already been handled by connect. + _ => { + debug!("unknown I/O error: {value}"); + Self::Unknown + } + } + } +} + +impl From for types::ErrorCode { + fn from(code: crate::sockets::util::ErrorCode) -> Self { + match code { + crate::sockets::util::ErrorCode::Unknown => Self::Unknown, + crate::sockets::util::ErrorCode::AccessDenied => Self::AccessDenied, + crate::sockets::util::ErrorCode::NotSupported => Self::NotSupported, + crate::sockets::util::ErrorCode::InvalidArgument => Self::InvalidArgument, + crate::sockets::util::ErrorCode::OutOfMemory => Self::OutOfMemory, + crate::sockets::util::ErrorCode::Timeout => Self::Timeout, + crate::sockets::util::ErrorCode::InvalidState => Self::InvalidState, + crate::sockets::util::ErrorCode::AddressNotBindable => Self::AddressNotBindable, + crate::sockets::util::ErrorCode::AddressInUse => Self::AddressInUse, + crate::sockets::util::ErrorCode::RemoteUnreachable => Self::RemoteUnreachable, + crate::sockets::util::ErrorCode::ConnectionRefused => Self::ConnectionRefused, + crate::sockets::util::ErrorCode::ConnectionReset => Self::ConnectionReset, + crate::sockets::util::ErrorCode::ConnectionAborted => Self::ConnectionAborted, + crate::sockets::util::ErrorCode::DatagramTooLarge => Self::DatagramTooLarge, + } + } +} diff --git a/crates/wasi/src/p3/sockets/host/ip_name_lookup.rs b/crates/wasi/src/p3/sockets/host/ip_name_lookup.rs index eb49886423..d4d3dbbc9a 100644 --- a/crates/wasi/src/p3/sockets/host/ip_name_lookup.rs +++ b/crates/wasi/src/p3/sockets/host/ip_name_lookup.rs @@ -1,34 +1,21 @@ -use core::net::Ipv6Addr; -use core::str::FromStr as _; - use tokio::net::lookup_host; use wasmtime::component::Accessor; -use crate::p3::bindings::sockets::ip_name_lookup::{ErrorCode, Host, HostConcurrent}; +use crate::p3::bindings::sockets::ip_name_lookup::{ErrorCode, Host, HostWithStore}; use crate::p3::bindings::sockets::types; -use crate::p3::sockets::util::{from_ipv4_addr, from_ipv6_addr}; -use crate::p3::sockets::{WasiSockets, WasiSocketsImpl, WasiSocketsView}; +use crate::p3::sockets::WasiSockets; +use crate::sockets::WasiSocketsCtxView; +use crate::sockets::util::{from_ipv4_addr, from_ipv6_addr, parse_host}; -impl HostConcurrent for WasiSockets -where - T: WasiSocketsView + 'static, -{ +impl HostWithStore for WasiSockets { async fn resolve_addresses( store: &Accessor, name: String, ) -> wasmtime::Result, ErrorCode>> { - // `url::Host::parse` serves us two functions: - // 1. validate the input is a valid domain name or IP, - // 2. convert unicode domains to punycode. - let host = if let Ok(host) = url::Host::parse(&name) { - host - } else if let Ok(addr) = Ipv6Addr::from_str(&name) { - // `url::Host::parse` doesn't understand bare IPv6 addresses without [brackets] - url::Host::Ipv6(addr) - } else { + let Ok(host) = parse_host(&name) else { return Ok(Err(ErrorCode::InvalidArgument)); }; - if !store.with(|mut view| view.get().sockets().allowed_network_uses.ip_name_lookup) { + if !store.with(|mut view| view.get().ctx.allowed_network_uses.ip_name_lookup) { return Ok(Err(ErrorCode::PermanentResolverFailure)); } match host { @@ -49,4 +36,4 @@ where } } -impl Host for WasiSocketsImpl where T: WasiSocketsView {} +impl Host for WasiSocketsCtxView<'_> {} diff --git a/crates/wasi/src/p3/sockets/host/types/mod.rs b/crates/wasi/src/p3/sockets/host/types/mod.rs index 94de2e462f..b64fd44dbd 100644 --- a/crates/wasi/src/p3/sockets/host/types/mod.rs +++ b/crates/wasi/src/p3/sockets/host/types/mod.rs @@ -2,32 +2,23 @@ use core::net::SocketAddr; use wasmtime::component::Accessor; -use crate::p3::bindings::sockets::types::{Host, HostConcurrent}; -use crate::p3::sockets::{ - SocketAddrCheck, SocketAddrUse, WasiSockets, WasiSocketsImpl, WasiSocketsView, -}; +use crate::p3::bindings::sockets::types::Host; +use crate::p3::sockets::WasiSockets; +use crate::sockets::{SocketAddrCheck, SocketAddrUse, WasiSocketsCtxView}; mod tcp; mod udp; -impl Host for WasiSocketsImpl where T: WasiSocketsView {} +impl Host for WasiSocketsCtxView<'_> {} -impl HostConcurrent for WasiSockets where T: WasiSocketsView + 'static {} - -fn get_socket_addr_check(store: &Accessor>) -> SocketAddrCheck -where - U: WasiSocketsView + 'static, -{ - store.with(|mut view| view.get().sockets().socket_addr_check.clone()) +fn get_socket_addr_check(store: &Accessor) -> SocketAddrCheck { + store.with(|mut view| view.get().ctx.socket_addr_check.clone()) } -async fn is_addr_allowed( - store: &Accessor>, +async fn is_addr_allowed( + store: &Accessor, addr: SocketAddr, reason: SocketAddrUse, -) -> bool -where - U: WasiSocketsView + 'static, -{ +) -> bool { get_socket_addr_check(store)(addr, reason).await } diff --git a/crates/wasi/src/p3/sockets/host/types/tcp.rs b/crates/wasi/src/p3/sockets/host/types/tcp.rs index 7d868d1b98..de33a6d8e5 100644 --- a/crates/wasi/src/p3/sockets/host/types/tcp.rs +++ b/crates/wasi/src/p3/sockets/host/types/tcp.rs @@ -1,38 +1,39 @@ +use core::future::poll_fn; use core::mem; use core::net::SocketAddr; +use core::pin::pin; +use core::task::Poll; +use std::io::Cursor; use std::net::Shutdown; use std::sync::Arc; -use anyhow::{Context as _, bail, ensure}; +use anyhow::{Context as _, ensure}; +use bytes::BytesMut; use io_lifetimes::AsSocketlike as _; use rustix::io::Errno; -use tokio::sync::mpsc; +use tokio::net::{TcpListener, TcpStream}; use wasmtime::component::{ - Accessor, AccessorTask, FutureReader, Resource, ResourceTable, StreamReader, StreamWriter, - WithAccessor, WithAccessorAndValue, + Accessor, AccessorTask, FutureReader, FutureWriter, GuardedFutureWriter, GuardedStreamWriter, + Resource, ResourceTable, StreamReader, StreamWriter, }; +use crate::p3::DEFAULT_BUFFER_CAPACITY; use crate::p3::bindings::sockets::types::{ - Duration, ErrorCode, HostTcpSocket, HostTcpSocketConcurrent, IpAddressFamily, IpSocketAddress, + Duration, ErrorCode, HostTcpSocket, HostTcpSocketWithStore, IpAddressFamily, IpSocketAddress, TcpSocket, }; -use crate::p3::sockets::tcp::TcpState; -use crate::p3::sockets::util::{ +use crate::p3::sockets::WasiSockets; +use crate::p3::sockets::tcp::{NonInheritedOptions, TcpState}; +use crate::sockets::util::{ is_valid_address_family, is_valid_remote_address, is_valid_unicast_address, }; -use crate::p3::sockets::{ - SocketAddrUse, SocketAddressFamily, WasiSockets, WasiSocketsImpl, WasiSocketsView, -}; -use crate::p3::{AbortOnDropHandle, IoTask, ResourceView as _, SpawnExt}; +use crate::sockets::{SocketAddrUse, SocketAddressFamily, WasiSocketsCtxView}; use super::is_addr_allowed; -fn is_tcp_allowed(store: &Accessor>) -> bool -where - U: WasiSocketsView + 'static, -{ - store.with(|mut view| view.get().sockets().allowed_network_uses.tcp) +fn is_tcp_allowed(store: &Accessor) -> bool { + store.with(|mut view| view.get().ctx.allowed_network_uses.tcp) } fn get_socket<'a>( @@ -54,88 +55,31 @@ fn get_socket_mut<'a>( } struct ListenTask { + listener: Arc, family: SocketAddressFamily, tx: StreamWriter>, - rx: mpsc::Receiver>, - - // The socket options below are not automatically inherited from the listener - // on all platforms. So we keep track of which options have been explicitly - // set and manually apply those values to newly accepted clients. - #[cfg(target_os = "macos")] - receive_buffer_size: Arc, - #[cfg(target_os = "macos")] - send_buffer_size: Arc, - #[cfg(target_os = "macos")] - hop_limit: Arc, - #[cfg(target_os = "macos")] - keep_alive_idle_time: Arc, // nanoseconds + options: NonInheritedOptions, } -impl AccessorTask, wasmtime::Result<()>> for ListenTask -where - U: WasiSocketsView + 'static, -{ - async fn run(mut self, store: &Accessor>) -> wasmtime::Result<()> { - let mut tx = self.tx; - while let Some(res) = self.rx.recv().await { +impl AccessorTask> for ListenTask { + async fn run(self, store: &Accessor) -> wasmtime::Result<()> { + let mut tx = GuardedStreamWriter::new(store, self.tx); + while !tx.is_closed() { + let Some(res) = ({ + let mut accept = pin!(self.listener.accept()); + let mut tx = pin!(tx.watch_reader()); + poll_fn(|cx| match tx.as_mut().poll(cx) { + Poll::Ready(()) => return Poll::Ready(None), + Poll::Pending => accept.as_mut().poll(cx).map(Some), + }) + .await + }) else { + return Ok(()); + }; let state = match res { Ok((stream, _addr)) => { - #[cfg(target_os = "macos")] - { - // Manually inherit socket options from listener. We only have to - // do this on platforms that don't already do this automatically - // and only if a specific value was explicitly set on the listener. - - let receive_buffer_size = self - .receive_buffer_size - .load(core::sync::atomic::Ordering::Relaxed); - if receive_buffer_size > 0 { - // Ignore potential error. - _ = rustix::net::sockopt::set_socket_recv_buffer_size( - &stream, - receive_buffer_size, - ); - } - - let send_buffer_size = self - .send_buffer_size - .load(core::sync::atomic::Ordering::Relaxed); - if send_buffer_size > 0 { - // Ignore potential error. - _ = rustix::net::sockopt::set_socket_send_buffer_size( - &stream, - send_buffer_size, - ); - } - - // For some reason, IP_TTL is inherited, but IPV6_UNICAST_HOPS isn't. - if self.family == SocketAddressFamily::Ipv6 { - let hop_limit = - self.hop_limit.load(core::sync::atomic::Ordering::Relaxed); - if hop_limit > 0 { - // Ignore potential error. - _ = rustix::net::sockopt::set_ipv6_unicast_hops( - &stream, - Some(hop_limit), - ); - } - } - - let keep_alive_idle_time = self - .keep_alive_idle_time - .load(core::sync::atomic::Ordering::Relaxed); - if keep_alive_idle_time > 0 { - // Ignore potential error. - _ = rustix::net::sockopt::set_tcp_keepidle( - &stream, - core::time::Duration::from_nanos(keep_alive_idle_time), - ); - } - } - TcpState::Connected { - stream: Arc::new(stream), - rx_task: None, - } + self.options.apply(self.family, &stream); + TcpState::Connected(Arc::new(stream)) } Err(err) => { match Errno::from_io_error(&err) { @@ -174,12 +118,18 @@ where }; let socket = store.with(|mut view| { view.get() - .table() + .table .push(TcpSocket::from_state(state, self.family)) - .context("failed to push socket to table") + .context("failed to push socket resource to table") })?; - tx.write(store, Some(socket)).await; - if tx.is_closed() { + if let Some(socket) = tx.write(Some(socket)).await { + debug_assert!(tx.is_closed()); + store.with(|mut view| { + view.get() + .table + .delete(socket) + .context("failed to delete socket resource from table") + })?; return Ok(()); } } @@ -187,12 +137,82 @@ where } } -impl HostTcpSocketConcurrent for WasiSockets -where - T: WasiSocketsView + 'static, -{ - async fn bind( - store: &Accessor, +struct ResultWriteTask { + result: Result<(), ErrorCode>, + result_tx: FutureWriter>, +} + +impl AccessorTask> for ResultWriteTask { + async fn run(self, store: &Accessor) -> wasmtime::Result<()> { + GuardedFutureWriter::new(store, self.result_tx) + .write(self.result) + .await; + Ok(()) + } +} + +struct ReceiveTask { + stream: Arc, + data_tx: StreamWriter, + result_tx: FutureWriter>, +} + +impl AccessorTask> for ReceiveTask { + async fn run(self, store: &Accessor) -> wasmtime::Result<()> { + let mut buf = BytesMut::with_capacity(DEFAULT_BUFFER_CAPACITY); + let mut data_tx = GuardedStreamWriter::new(store, self.data_tx); + let result_tx = GuardedFutureWriter::new(store, self.result_tx); + let res = loop { + match self.stream.try_read_buf(&mut buf) { + Ok(0) => { + break Ok(()); + } + Ok(..) => { + buf = data_tx.write_all(Cursor::new(buf)).await.into_inner(); + if data_tx.is_closed() { + break Ok(()); + } + buf.clear(); + } + Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => { + let Some(res) = ({ + let mut readable = pin!(self.stream.readable()); + let mut tx = pin!(data_tx.watch_reader()); + poll_fn(|cx| match tx.as_mut().poll(cx) { + Poll::Ready(()) => return Poll::Ready(None), + Poll::Pending => readable.as_mut().poll(cx).map(Some), + }) + .await + }) else { + break Ok(()); + }; + if let Err(err) = res { + break Err(err.into()); + } + } + Err(err) => { + break Err(err.into()); + } + } + }; + _ = self + .stream + .as_socketlike_view::() + .shutdown(Shutdown::Read); + + // Write the result async from a separate task to ensure that all resources used by this + // task are freed + store.spawn(ResultWriteTask { + result: res, + result_tx: result_tx.into(), + }); + Ok(()) + } +} + +impl HostTcpSocketWithStore for WasiSockets { + async fn bind( + store: &Accessor, socket: Resource, local_address: IpSocketAddress, ) -> wasmtime::Result> { @@ -203,14 +223,13 @@ where return Ok(Err(ErrorCode::AccessDenied)); } store.with(|mut view| { - let mut binding = view.get(); - let socket = get_socket_mut(binding.table(), &socket)?; + let socket = get_socket_mut(view.get().table, &socket)?; Ok(socket.bind(local_address)) }) } - async fn connect( - store: &Accessor, + async fn connect( + store: &Accessor, socket: Resource, remote_address: IpSocketAddress, ) -> wasmtime::Result> { @@ -222,13 +241,12 @@ where } match store.with(|mut view| { let ip = remote_address.ip(); - let mut binding = view.get(); - let socket = get_socket_mut(binding.table(), &socket)?; + let socket = get_socket_mut(view.get().table, &socket)?; if !is_valid_unicast_address(ip) || !is_valid_remote_address(remote_address) || !is_valid_address_family(ip, socket.family) { - return Ok(Err(ErrorCode::InvalidArgument)); + return anyhow::Ok(Err(ErrorCode::InvalidArgument)); } match mem::replace(&mut socket.tcp_state, TcpState::Connecting) { TcpState::Default(sock) | TcpState::Bound(sock) => Ok(Ok(sock)), @@ -237,22 +255,20 @@ where Ok(Err(ErrorCode::InvalidState)) } } - }) { - Ok(Ok(sock)) => { + })? { + Ok(sock) => { + // FIXME: handle possible cancellation of the outer `connect` + // https://github.com/bytecodealliance/wasmtime/pull/11291#discussion_r2223917986 let res = sock.connect(remote_address).await; store.with(|mut view| { - let mut binding = view.get(); - let socket = get_socket_mut(binding.table(), &socket)?; + let socket = get_socket_mut(view.get().table, &socket)?; ensure!( matches!(socket.tcp_state, TcpState::Connecting), "corrupted socket state" ); match res { Ok(stream) => { - socket.tcp_state = TcpState::Connected { - stream: Arc::new(stream), - rx_task: None, - }; + socket.tcp_state = TcpState::Connected(Arc::new(stream)); Ok(Ok(())) } Err(err) => { @@ -262,87 +278,33 @@ where } }) } - Ok(Err(err)) => Ok(Err(err)), - Err(err) => Err(err), + Err(err) => Ok(Err(err)), } } - async fn listen( - store: &Accessor, + async fn listen( + store: &Accessor, socket: Resource, ) -> wasmtime::Result>, ErrorCode>> { - match store.with(|mut view| { - if !view.get().sockets().allowed_network_uses.tcp { - return Ok(Err(ErrorCode::AccessDenied)); + store.with(|mut view| { + if !view.get().ctx.allowed_network_uses.tcp { + return anyhow::Ok(Err(ErrorCode::AccessDenied)); } - let sock = { - let mut binding = view.get(); - let socket = get_socket_mut(binding.table(), &socket)?; - match mem::replace(&mut socket.tcp_state, TcpState::Closed) { - TcpState::Default(sock) | TcpState::Bound(sock) => sock, - tcp_state => { - socket.tcp_state = tcp_state; - return Ok(Err(ErrorCode::InvalidState)); - } - } - }; - let instance = view.instance(); - let (tx, rx) = instance - .stream(&mut view) - .context("failed to create stream")?; - let &TcpSocket { + let TcpSocket { + tcp_state, listen_backlog_size, - .. - } = get_socket(view.get().table(), &socket)?; - - match sock.listen(listen_backlog_size) { - Ok(listener) => { - let listener = Arc::new(listener); - let (task_tx, task_rx) = mpsc::channel(1); - let task = view.spawn_fn({ - let listener = Arc::clone(&listener); - |_| async move { - while let Ok(tx) = task_tx.reserve().await { - tx.send(listener.accept().await) - } - Ok(()) - } - }); - let mut binding = view.get(); - let TcpSocket { - tcp_state, - family, - #[cfg(target_os = "macos")] - receive_buffer_size, - #[cfg(target_os = "macos")] - send_buffer_size, - #[cfg(target_os = "macos")] - hop_limit, - #[cfg(target_os = "macos")] - keep_alive_idle_time, - .. - } = get_socket_mut(binding.table(), &socket)?; - *tcp_state = TcpState::Listening { - listener, - task: AbortOnDropHandle(task), - }; - Ok(Ok(( - rx, - ListenTask { - family: *family, - tx, - rx: task_rx, - #[cfg(target_os = "macos")] - receive_buffer_size: Arc::clone(&receive_buffer_size), - #[cfg(target_os = "macos")] - send_buffer_size: Arc::clone(&send_buffer_size), - #[cfg(target_os = "macos")] - hop_limit: Arc::clone(&hop_limit), - #[cfg(target_os = "macos")] - keep_alive_idle_time: Arc::clone(&keep_alive_idle_time), - }, - ))) + family, + options, + } = get_socket_mut(view.get().table, &socket)?; + let sock = match mem::replace(tcp_state, TcpState::Closed) { + TcpState::Default(sock) | TcpState::Bound(sock) => sock, + prev => { + *tcp_state = prev; + return Ok(Err(ErrorCode::InvalidState)); } + }; + let listener = match sock.listen(*listen_backlog_size) { + Ok(listener) => listener, Err(err) => { match Errno::from_io_error(&err) { // See: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-listen#:~:text=WSAEMFILE @@ -355,31 +317,39 @@ where // observed by any of the wasmtime authors, so we're relying fully // on Microsoft's documentation here. #[cfg(windows)] - Some(Errno::MFILE) => Ok(Err(ErrorCode::OutOfMemory)), + Some(Errno::MFILE) => return Ok(Err(ErrorCode::OutOfMemory)), - _ => Ok(Err(err.into())), + _ => return Ok(Err(err.into())), } } - } - }) { - Ok(Ok((rx, task))) => { - store.spawn(task); - Ok(Ok(rx)) - } - Ok(Err(err)) => Ok(Err(err)), - Err(err) => Err(err), - } + }; + let listener = Arc::new(listener); + *tcp_state = TcpState::Listening(Arc::clone(&listener)); + let family = *family; + let options = options.clone(); + let (tx, rx) = view + .instance() + .stream(&mut view) + .context("failed to create stream")?; + let task = ListenTask { + listener, + family, + tx, + options, + }; + view.spawn(task); + Ok(Ok(rx)) + }) } - async fn send( - store: &Accessor, + async fn send( + store: &Accessor, socket: Resource, data: StreamReader, ) -> wasmtime::Result> { let (stream, mut data) = match store.with(|mut view| -> wasmtime::Result<_> { - let mut binding = view.get(); - let sock = get_socket(binding.table(), &socket)?; - if let TcpState::Connected { stream, .. } = &sock.tcp_state { + let sock = get_socket(view.get().table, &socket)?; + if let TcpState::Connected(stream) | TcpState::Receiving(stream) = &sock.tcp_state { Ok(Ok((Arc::clone(&stream), data))) } else { Ok(Err(ErrorCode::InvalidState)) @@ -416,106 +386,47 @@ where Ok(result) } - async fn receive( - store: &Accessor, + async fn receive( + store: &Accessor, socket: Resource, ) -> wasmtime::Result<(StreamReader, FutureReader>)> { - let ((data_tx, data_rx), (res_tx, res_rx)) = store.with(|mut view| { + store.with(|mut view| { let instance = view.instance(); - let data = instance + let (mut data_tx, data_rx) = instance .stream(&mut view) .context("failed to create stream")?; - let res = instance - .future(&mut view) - .context("failed to create future")?; - anyhow::Ok((data, res)) - })?; - let data_tx = WithAccessor::new(store, data_tx); - let data_rx = WithAccessor::new(store, data_rx); - let res_tx = WithAccessorAndValue::new(store, res_tx, Ok(())); - let res_rx = WithAccessor::new(store, res_rx); - - let result = store.with(|mut view| { - let mut binding = view.get(); - let sock = get_socket(binding.table(), &socket)?; - anyhow::Ok(match &sock.tcp_state { - TcpState::Connected { - stream, - rx_task: None, - } => { - let (task_tx, task_rx) = mpsc::channel(1); - let stream = Arc::clone(&stream); - let task = view.spawn_fn(|_| async move { - while let Ok(tx) = task_tx.reserve().await { - let mut buf = vec![0; 8096]; - match stream.try_read(&mut buf) { - Ok(0) => break, - Ok(n) => { - buf.truncate(n); - tx.send(Ok(buf)); - } - Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => { - if let Err(err) = stream.readable().await { - tx.send(Err(err.into())); - break; - } - } - Err(err) => { - tx.send(Err(err.into())); - break; - } - } - } - _ = stream - .as_socketlike_view::() - .shutdown(Shutdown::Read); - Ok(()) + let TcpSocket { tcp_state, .. } = get_socket_mut(view.get().table, &socket)?; + match mem::replace(tcp_state, TcpState::Closed) { + TcpState::Connected(stream) => { + *tcp_state = TcpState::Receiving(Arc::clone(&stream)); + let (result_tx, result_rx) = instance + .future(&mut view, || unreachable!()) + .context("failed to create future")?; + view.spawn(ReceiveTask { + stream, + data_tx, + result_tx, }); - let mut binding = view.get(); - let TcpSocket { - tcp_state: TcpState::Connected { rx_task, .. }, - .. - } = get_socket_mut(binding.table(), &socket)? - else { - bail!("corrupted socket state"); - }; - *rx_task = Some(AbortOnDropHandle(task)); - Ok(task_rx) + Ok((data_rx, result_rx)) + } + prev => { + *tcp_state = prev; + let (mut result_tx, result_rx) = instance + .future(&mut view, || Err(ErrorCode::InvalidState)) + .context("failed to create future")?; + result_tx.close(&mut view); + data_tx.close(&mut view); + Ok((data_rx, result_rx)) } - _ => Err(ErrorCode::InvalidState), - }) - })?; - - match result { - Ok(task_rx) => { - store.spawn(IoTask { - data: data_tx.into_inner(), - result: res_tx.into_inner(), - rx: task_rx, - }); - } - Err(err) => { - let res_tx = res_tx.into_inner(); - store.spawn_fn_box(move |store| { - Box::pin(async move { - res_tx.write(store, Err(err)).await; - Ok(()) - }) - }); } - } - - Ok((data_rx.into_inner(), res_rx.into_inner())) + }) } } -impl HostTcpSocket for WasiSocketsImpl -where - T: WasiSocketsView, -{ +impl HostTcpSocket for WasiSocketsCtxView<'_> { fn new(&mut self, address_family: IpAddressFamily) -> wasmtime::Result> { let socket = TcpSocket::new(address_family.into()).context("failed to create socket")?; - self.table() + self.table .push(socket) .context("failed to push socket resource to table") } @@ -524,7 +435,7 @@ where &mut self, socket: Resource, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.local_address()) } @@ -532,17 +443,17 @@ where &mut self, socket: Resource, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.remote_address()) } fn is_listening(&mut self, socket: Resource) -> wasmtime::Result { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.is_listening()) } fn address_family(&mut self, socket: Resource) -> wasmtime::Result { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.address_family()) } @@ -551,7 +462,7 @@ where socket: Resource, value: u64, ) -> wasmtime::Result> { - let sock = get_socket_mut(self.table(), &socket)?; + let sock = get_socket_mut(self.table, &socket)?; Ok(sock.set_listen_backlog_size(value)) } @@ -559,7 +470,7 @@ where &mut self, socket: Resource, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.keep_alive_enabled()) } @@ -568,7 +479,7 @@ where socket: Resource, value: bool, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.set_keep_alive_enabled(value)) } @@ -576,7 +487,7 @@ where &mut self, socket: Resource, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.keep_alive_idle_time()) } @@ -585,7 +496,7 @@ where socket: Resource, value: Duration, ) -> wasmtime::Result> { - let sock = get_socket_mut(self.table(), &socket)?; + let sock = get_socket_mut(self.table, &socket)?; Ok(sock.set_keep_alive_idle_time(value)) } @@ -593,7 +504,7 @@ where &mut self, socket: Resource, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.keep_alive_interval()) } @@ -602,7 +513,7 @@ where socket: Resource, value: Duration, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.set_keep_alive_interval(value)) } @@ -610,7 +521,7 @@ where &mut self, socket: Resource, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.keep_alive_count()) } @@ -619,7 +530,7 @@ where socket: Resource, value: u32, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.set_keep_alive_count(value)) } @@ -627,7 +538,7 @@ where &mut self, socket: Resource, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.hop_limit()) } @@ -636,7 +547,7 @@ where socket: Resource, value: u8, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket_mut(self.table, &socket)?; Ok(sock.set_hop_limit(value)) } @@ -644,7 +555,7 @@ where &mut self, socket: Resource, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.receive_buffer_size()) } @@ -653,7 +564,7 @@ where socket: Resource, value: u64, ) -> wasmtime::Result> { - let sock = get_socket_mut(self.table(), &socket)?; + let sock = get_socket_mut(self.table, &socket)?; Ok(sock.set_receive_buffer_size(value)) } @@ -661,7 +572,7 @@ where &mut self, socket: Resource, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.send_buffer_size()) } @@ -670,13 +581,13 @@ where socket: Resource, value: u64, ) -> wasmtime::Result> { - let sock = get_socket_mut(self.table(), &socket)?; + let sock = get_socket_mut(self.table, &socket)?; Ok(sock.set_send_buffer_size(value)) } - fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { - self.table() - .delete(rep) + fn drop(&mut self, sock: Resource) -> wasmtime::Result<()> { + self.table + .delete(sock) .context("failed to delete socket resource from table")?; Ok(()) } diff --git a/crates/wasi/src/p3/sockets/host/types/udp.rs b/crates/wasi/src/p3/sockets/host/types/udp.rs index 321bcdf0c4..2518590c8b 100644 --- a/crates/wasi/src/p3/sockets/host/types/udp.rs +++ b/crates/wasi/src/p3/sockets/host/types/udp.rs @@ -3,20 +3,17 @@ use core::net::SocketAddr; use anyhow::Context as _; use wasmtime::component::{Accessor, Resource, ResourceTable}; -use crate::p3::ResourceView as _; use crate::p3::bindings::sockets::types::{ - ErrorCode, HostUdpSocket, HostUdpSocketConcurrent, IpAddressFamily, IpSocketAddress, + ErrorCode, HostUdpSocket, HostUdpSocketWithStore, IpAddressFamily, IpSocketAddress, }; -use crate::p3::sockets::udp::{MAX_UDP_DATAGRAM_SIZE, UdpSocket}; -use crate::p3::sockets::{SocketAddrUse, WasiSockets, WasiSocketsImpl, WasiSocketsView}; +use crate::p3::sockets::WasiSockets; +use crate::p3::sockets::udp::UdpSocket; +use crate::sockets::{MAX_UDP_DATAGRAM_SIZE, SocketAddrUse, WasiSocketsCtxView}; use super::is_addr_allowed; -fn is_udp_allowed(store: &Accessor>) -> bool -where - U: WasiSocketsView + 'static, -{ - store.with(|mut view| view.get().sockets().allowed_network_uses.udp) +fn is_udp_allowed(store: &Accessor) -> bool { + store.with(|mut view| view.get().ctx.allowed_network_uses.udp) } fn get_socket<'a>( @@ -37,12 +34,9 @@ fn get_socket_mut<'a>( .context("failed to get socket resource from table") } -impl HostUdpSocketConcurrent for WasiSockets -where - T: WasiSocketsView + 'static, -{ - async fn bind( - store: &Accessor, +impl HostUdpSocketWithStore for WasiSockets { + async fn bind( + store: &Accessor, socket: Resource, local_address: IpSocketAddress, ) -> wasmtime::Result> { @@ -53,14 +47,13 @@ where return Ok(Err(ErrorCode::AccessDenied)); } store.with(|mut view| { - let mut binding = view.get(); - let socket = get_socket_mut(binding.table(), &socket)?; + let socket = get_socket_mut(view.get().table, &socket)?; Ok(socket.bind(local_address)) }) } - async fn connect( - store: &Accessor, + async fn connect( + store: &Accessor, socket: Resource, remote_address: IpSocketAddress, ) -> wasmtime::Result> { @@ -71,14 +64,13 @@ where return Ok(Err(ErrorCode::AccessDenied)); } store.with(|mut view| { - let mut binding = view.get(); - let socket = get_socket_mut(binding.table(), &socket)?; + let socket = get_socket_mut(view.get().table, &socket)?; Ok(socket.connect(remote_address)) }) } - async fn send( - store: &Accessor, + async fn send( + store: &Accessor, socket: Resource, data: Vec, remote_address: Option, @@ -95,37 +87,34 @@ where return Ok(Err(ErrorCode::AccessDenied)); } let fut = store.with(|mut view| { - get_socket(view.get().table(), &socket).map(|sock| sock.send_to(data, addr)) + get_socket(view.get().table, &socket).map(|sock| sock.send_to(data, addr)) })?; Ok(fut.await) } else { let fut = store.with(|mut view| { - get_socket(view.get().table(), &socket).map(|sock| sock.send(data)) + get_socket(view.get().table, &socket).map(|sock| sock.send(data)) })?; Ok(fut.await) } } - async fn receive( - store: &Accessor, + async fn receive( + store: &Accessor, socket: Resource, ) -> wasmtime::Result, IpSocketAddress), ErrorCode>> { if !is_udp_allowed(store) { return Ok(Err(ErrorCode::AccessDenied)); } let fut = store - .with(|mut view| get_socket(view.get().table(), &socket).map(|sock| sock.receive()))?; + .with(|mut view| get_socket(view.get().table, &socket).map(|sock| sock.receive()))?; Ok(fut.await) } } -impl HostUdpSocket for WasiSocketsImpl -where - T: WasiSocketsView, -{ +impl HostUdpSocket for WasiSocketsCtxView<'_> { fn new(&mut self, address_family: IpAddressFamily) -> wasmtime::Result> { let socket = UdpSocket::new(address_family.into()).context("failed to create socket")?; - self.table() + self.table .push(socket) .context("failed to push socket resource to table") } @@ -134,7 +123,7 @@ where &mut self, socket: Resource, ) -> wasmtime::Result> { - let socket = get_socket_mut(self.table(), &socket)?; + let socket = get_socket_mut(self.table, &socket)?; Ok(socket.disconnect()) } @@ -142,7 +131,7 @@ where &mut self, socket: Resource, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.local_address()) } @@ -150,12 +139,12 @@ where &mut self, socket: Resource, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.remote_address()) } fn address_family(&mut self, socket: Resource) -> wasmtime::Result { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.address_family()) } @@ -163,7 +152,7 @@ where &mut self, socket: Resource, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.unicast_hop_limit()) } @@ -172,7 +161,7 @@ where socket: Resource, value: u8, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.set_unicast_hop_limit(value)) } @@ -180,7 +169,7 @@ where &mut self, socket: Resource, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.receive_buffer_size()) } @@ -189,7 +178,7 @@ where socket: Resource, value: u64, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.set_receive_buffer_size(value)) } @@ -197,7 +186,7 @@ where &mut self, socket: Resource, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.send_buffer_size()) } @@ -206,13 +195,13 @@ where socket: Resource, value: u64, ) -> wasmtime::Result> { - let sock = get_socket(self.table(), &socket)?; + let sock = get_socket(self.table, &socket)?; Ok(sock.set_send_buffer_size(value)) } - fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { - self.table() - .delete(rep) + fn drop(&mut self, sock: Resource) -> wasmtime::Result<()> { + self.table + .delete(sock) .context("failed to delete socket resource from table")?; Ok(()) } diff --git a/crates/wasi/src/p3/sockets/mod.rs b/crates/wasi/src/p3/sockets/mod.rs index 248e60c665..322b48a9e8 100644 --- a/crates/wasi/src/p3/sockets/mod.rs +++ b/crates/wasi/src/p3/sockets/mod.rs @@ -1,166 +1,33 @@ -use crate::p3::ResourceView; -use core::future::Future; -use core::net::SocketAddr; -use core::ops::Deref; -use core::pin::Pin; -use std::sync::Arc; -use wasmtime::component::{HasData, Linker, ResourceTable}; +use crate::p3::bindings::sockets; +use crate::sockets::{WasiSocketsCtxView, WasiSocketsView}; +use wasmtime::component::{HasData, Linker}; +mod conv; mod host; pub mod tcp; pub mod udp; -pub mod util; - -#[repr(transparent)] -pub struct WasiSocketsImpl(pub T); - -impl WasiSocketsView for &mut T { - fn sockets(&self) -> &WasiSocketsCtx { - (**self).sockets() - } -} - -impl WasiSocketsView for WasiSocketsImpl { - fn sockets(&self) -> &WasiSocketsCtx { - self.0.sockets() - } -} - -impl ResourceView for WasiSocketsImpl { - fn table(&mut self) -> &mut ResourceTable { - self.0.table() - } -} - -pub trait WasiSocketsView: ResourceView + Send { - fn sockets(&self) -> &WasiSocketsCtx; -} - -#[derive(Clone, Default)] -pub struct WasiSocketsCtx { - pub socket_addr_check: SocketAddrCheck, - pub allowed_network_uses: AllowedNetworkUses, -} - -pub struct Network { - pub socket_addr_check: SocketAddrCheck, - pub allow_ip_name_lookup: bool, -} - -/// A check that will be called for each socket address that is used of whether the address is permitted. -#[derive(Clone)] -pub struct SocketAddrCheck( - pub(crate) Arc< - dyn Fn(SocketAddr, SocketAddrUse) -> Pin + Send + Sync>> - + Send - + Sync, - >, -); - -impl SocketAddrCheck { - /// A check that will be called for each socket address that is used. - /// - /// Returning `true` will permit socket connections to the `SocketAddr`, - /// while returning `false` will reject the connection. - pub fn new( - f: impl Fn(SocketAddr, SocketAddrUse) -> Pin + Send + Sync>> - + Send - + Sync - + 'static, - ) -> Self { - Self(Arc::new(f)) - } - - pub async fn check(&self, addr: SocketAddr, reason: SocketAddrUse) -> std::io::Result<()> { - if (self.0)(addr, reason).await { - Ok(()) - } else { - Err(std::io::Error::new( - std::io::ErrorKind::PermissionDenied, - "An address was not permitted by the socket address check.", - )) - } - } -} - -impl Deref for SocketAddrCheck { - type Target = dyn Fn(SocketAddr, SocketAddrUse) -> Pin + Send + Sync>> - + Send - + Sync; - - fn deref(&self) -> &Self::Target { - self.0.as_ref() - } -} - -impl Default for SocketAddrCheck { - fn default() -> Self { - Self(Arc::new(|_, _| Box::pin(async { false }))) - } -} - -/// The reason what a socket address is being used for. -#[derive(Clone, Copy, Debug)] -pub enum SocketAddrUse { - /// Binding TCP socket - TcpBind, - /// Connecting TCP socket - TcpConnect, - /// Binding UDP socket - UdpBind, - /// Connecting UDP socket - UdpConnect, - /// Sending datagram on non-connected UDP socket - UdpOutgoingDatagram, -} - -#[derive(Copy, Clone, Eq, PartialEq)] -pub enum SocketAddressFamily { - Ipv4, - Ipv6, -} - -#[derive(Copy, Clone)] -pub struct AllowedNetworkUses { - pub ip_name_lookup: bool, - pub udp: bool, - pub tcp: bool, -} - -impl Default for AllowedNetworkUses { - fn default() -> Self { - Self { - ip_name_lookup: false, - udp: true, - tcp: true, - } - } -} /// Add all WASI interfaces from this module into the `linker` provided. /// -/// This function will add the `async` variant of all interfaces into the -/// [`Linker`] provided. By `async` this means that this function is only -/// compatible with [`Config::async_support(true)`][async]. For embeddings with -/// async support disabled see [`add_to_linker_sync`] instead. -/// -/// This function will add all interfaces implemented by this crate to the +/// This function will add all interfaces implemented by this module to the /// [`Linker`], which corresponds to the `wasi:sockets/imports` world supported by -/// this crate. +/// this module. /// -/// [async]: wasmtime::Config::async_support +/// This is low-level API for advanced use cases, +/// [`wasmtime_wasi::p3::add_to_linker`](crate::p3::add_to_linker) can be used instead +/// to add *all* wasip3 interfaces (including the ones from this module) to the `linker`. /// /// # Example /// /// ``` /// use wasmtime::{Engine, Result, Store, Config}; -/// use wasmtime::component::{ResourceTable, Linker}; -/// use wasmtime_wasi::p3::sockets::{WasiSocketsView, WasiSocketsCtx}; -/// use wasmtime_wasi::p3::ResourceView; +/// use wasmtime::component::{Linker, ResourceTable}; +/// use wasmtime_wasi::sockets::{WasiSocketsCtx, WasiSocketsCtxView, WasiSocketsView}; /// /// fn main() -> Result<()> { /// let mut config = Config::new(); /// config.async_support(true); +/// config.wasm_component_model_async(true); /// let engine = Engine::new(&config)?; /// /// let mut linker = Linker::::new(&engine); @@ -169,10 +36,7 @@ impl Default for AllowedNetworkUses { /// /// let mut store = Store::new( /// &engine, -/// MyState { -/// sockets: WasiSocketsCtx::default(), -/// table: ResourceTable::default(), -/// }, +/// MyState::default(), /// ); /// /// // ... use `linker` to instantiate within `store` ... @@ -180,32 +44,32 @@ impl Default for AllowedNetworkUses { /// Ok(()) /// } /// +/// #[derive(Default)] /// struct MyState { /// sockets: WasiSocketsCtx, /// table: ResourceTable, /// } /// -/// impl ResourceView for MyState { -/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } -/// } -/// /// impl WasiSocketsView for MyState { -/// fn sockets(&self) -> &WasiSocketsCtx { &self.sockets } +/// fn sockets(&mut self) -> WasiSocketsCtxView<'_> { +/// WasiSocketsCtxView { +/// ctx: &mut self.sockets, +/// table: &mut self.table, +/// } +/// } /// } /// ``` -pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> { - crate::p3::bindings::sockets::types::add_to_linker::<_, WasiSockets>(linker, |x| { - WasiSocketsImpl(x) - })?; - crate::p3::bindings::sockets::ip_name_lookup::add_to_linker::<_, WasiSockets>( - linker, - |x| WasiSocketsImpl(x), - )?; +pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> +where + T: WasiSocketsView + 'static, +{ + sockets::ip_name_lookup::add_to_linker::<_, WasiSockets>(linker, T::sockets)?; + sockets::types::add_to_linker::<_, WasiSockets>(linker, T::sockets)?; Ok(()) } -struct WasiSockets(T); +struct WasiSockets; -impl HasData for WasiSockets { - type Data<'a> = WasiSocketsImpl<&'a mut T>; +impl HasData for WasiSockets { + type Data<'a> = WasiSocketsCtxView<'a>; } diff --git a/crates/wasi/src/p3/sockets/tcp.rs b/crates/wasi/src/p3/sockets/tcp.rs index 78c1d8ae15..e327b1ef75 100644 --- a/crates/wasi/src/p3/sockets/tcp.rs +++ b/crates/wasi/src/p3/sockets/tcp.rs @@ -7,20 +7,16 @@ use std::sync::Arc; use cap_net_ext::AddressFamily; use io_lifetimes::AsSocketlike as _; use io_lifetimes::views::SocketlikeView; -use rustix::io::Errno; use rustix::net::sockopt; -use crate::p3::AbortOnDropHandle; use crate::p3::bindings::sockets::types::{Duration, ErrorCode, IpAddressFamily, IpSocketAddress}; -use crate::p3::sockets::SocketAddressFamily; -use crate::p3::sockets::util::{ +use crate::runtime::with_ambient_tokio_runtime; +use crate::sockets::util::{ get_unicast_hop_limit, is_valid_address_family, is_valid_unicast_address, receive_buffer_size, - send_buffer_size, set_receive_buffer_size, set_send_buffer_size, set_unicast_hop_limit, + send_buffer_size, set_keep_alive_count, set_keep_alive_idle_time, set_keep_alive_interval, + set_receive_buffer_size, set_send_buffer_size, set_unicast_hop_limit, tcp_bind, }; -use crate::runtime::with_ambient_tokio_runtime; - -/// Value taken from rust std library. -const DEFAULT_BACKLOG: u32 = 128; +use crate::sockets::{DEFAULT_TCP_BACKLOG, SocketAddressFamily}; /// The state of a TCP socket. /// @@ -34,19 +30,16 @@ pub enum TcpState { Bound(tokio::net::TcpSocket), /// The socket is now listening and waiting for an incoming connection. - Listening { - listener: Arc, - task: AbortOnDropHandle, - }, + Listening(Arc), /// An outgoing connection is started. Connecting, /// A connection has been established. - Connected { - stream: Arc, - rx_task: Option, - }, + Connected(Arc), + + /// A connection has been established and `receive` has been called. + Receiving(Arc), Error(ErrorCode), @@ -61,6 +54,7 @@ impl Debug for TcpState { Self::Listening { .. } => f.debug_tuple("Listening").finish(), Self::Connecting => f.debug_tuple("Connecting").finish(), Self::Connected { .. } => f.debug_tuple("Connected").finish(), + Self::Receiving { .. } => f.debug_tuple("Receiving").finish(), Self::Error(..) => f.debug_tuple("Error").finish(), Self::Closed => write!(f, "Closed"), } @@ -77,17 +71,7 @@ pub struct TcpSocket { pub family: SocketAddressFamily, - // The socket options below are not automatically inherited from the listener - // on all platforms. So we keep track of which options have been explicitly - // set and manually apply those values to newly accepted clients. - #[cfg(target_os = "macos")] - pub receive_buffer_size: Arc, - #[cfg(target_os = "macos")] - pub send_buffer_size: Arc, - #[cfg(target_os = "macos")] - pub hop_limit: Arc, - #[cfg(target_os = "macos")] - pub keep_alive_idle_time: Arc, // nanoseconds + pub options: NonInheritedOptions, } impl TcpSocket { @@ -114,24 +98,19 @@ impl TcpSocket { pub fn from_state(state: TcpState, family: SocketAddressFamily) -> Self { Self { tcp_state: state, - listen_backlog_size: DEFAULT_BACKLOG, + listen_backlog_size: DEFAULT_TCP_BACKLOG, family, - #[cfg(target_os = "macos")] - receive_buffer_size: Arc::default(), - #[cfg(target_os = "macos")] - send_buffer_size: Arc::default(), - #[cfg(target_os = "macos")] - hop_limit: Arc::default(), - #[cfg(target_os = "macos")] - keep_alive_idle_time: Arc::default(), + options: Default::default(), } } pub fn as_std_view(&self) -> Result, ErrorCode> { match &self.tcp_state { TcpState::Default(socket) | TcpState::Bound(socket) => Ok(socket.as_socketlike_view()), - TcpState::Connected { stream, .. } => Ok(stream.as_socketlike_view()), - TcpState::Listening { listener, .. } => Ok(listener.as_socketlike_view()), + TcpState::Connected(stream) | TcpState::Receiving(stream) => { + Ok(stream.as_socketlike_view()) + } + TcpState::Listening(listener) => Ok(listener.as_socketlike_view()), TcpState::Connecting | TcpState::Closed => Err(ErrorCode::InvalidState), TcpState::Error(err) => Err(*err), } @@ -144,9 +123,9 @@ impl TcpSocket { } match mem::replace(&mut self.tcp_state, TcpState::Closed) { TcpState::Default(sock) => { - if let Err(err) = bind(&sock, addr) { + if let Err(err) = tcp_bind(&sock, addr) { self.tcp_state = TcpState::Default(sock); - Err(err) + Err(err.into()) } else { self.tcp_state = TcpState::Bound(sock); Ok(()) @@ -165,11 +144,11 @@ impl TcpSocket { let addr = socket.local_addr()?; Ok(addr.into()) } - TcpState::Connected { stream, .. } => { + TcpState::Connected(stream) | TcpState::Receiving(stream) => { let addr = stream.local_addr()?; Ok(addr.into()) } - TcpState::Listening { listener, .. } => { + TcpState::Listening(listener) => { let addr = listener.local_addr()?; Ok(addr.into()) } @@ -180,7 +159,7 @@ impl TcpSocket { pub fn remote_address(&self) -> Result { match &self.tcp_state { - TcpState::Connected { stream, .. } => { + TcpState::Connected(stream) | TcpState::Receiving(stream) => { let addr = stream.peer_addr()?; Ok(addr.into()) } @@ -218,7 +197,7 @@ impl TcpSocket { self.listen_backlog_size = value; Ok(()) } - TcpState::Listening { listener, .. } => { + TcpState::Listening(listener) => { // Try to update the backlog by calling `listen` again. // Not all platforms support this. We'll only update our own value if the OS supports changing the backlog size after the fact. if rustix::net::listen(&listener, value.try_into().unwrap_or(i32::MAX)).is_err() { @@ -251,26 +230,11 @@ impl TcpSocket { } pub fn set_keep_alive_idle_time(&mut self, value: Duration) -> Result<(), ErrorCode> { - const NANOS_PER_SEC: u64 = 1_000_000_000; - - // Ensure that the value passed to the actual syscall never gets rounded down to 0. - const MIN: u64 = NANOS_PER_SEC; - - // Cap it at Linux' maximum, which appears to have the lowest limit across our supported platforms. - const MAX: u64 = (i16::MAX as u64) * NANOS_PER_SEC; - - let fd = &*self.as_std_view()?; - if value == 0 { - // WIT: "If the provided value is 0, an `invalid-argument` error is returned." - return Err(ErrorCode::InvalidArgument); - } - let value = value.clamp(MIN, MAX); - sockopt::set_tcp_keepidle(fd, core::time::Duration::from_nanos(value))?; - #[cfg(target_os = "macos")] - { - self.keep_alive_idle_time - .store(value, core::sync::atomic::Ordering::Relaxed); - } + let value = { + let fd = self.as_std_view()?; + set_keep_alive_idle_time(&*fd, value)? + }; + self.options.set_keep_alive_idle_time(value); Ok(()) } @@ -281,21 +245,8 @@ impl TcpSocket { } pub fn set_keep_alive_interval(&self, value: Duration) -> Result<(), ErrorCode> { - // Ensure that any fractional value passed to the actual syscall never gets rounded down to 0. - const MIN_SECS: core::time::Duration = core::time::Duration::from_secs(1); - - // Cap it at Linux' maximum, which appears to have the lowest limit across our supported platforms. - const MAX_SECS: core::time::Duration = core::time::Duration::from_secs(i16::MAX as u64); - let fd = &*self.as_std_view()?; - if value == 0 { - // WIT: "If the provided value is 0, an `invalid-argument` error is returned." - return Err(ErrorCode::InvalidArgument); - } - sockopt::set_tcp_keepintvl( - fd, - core::time::Duration::from_nanos(value).clamp(MIN_SECS, MAX_SECS), - )?; + set_keep_alive_interval(fd, core::time::Duration::from_nanos(value))?; Ok(()) } @@ -306,104 +257,153 @@ impl TcpSocket { } pub fn set_keep_alive_count(&self, value: u32) -> Result<(), ErrorCode> { - const MIN_CNT: u32 = 1; - // Cap it at Linux' maximum, which appears to have the lowest limit across our supported platforms. - const MAX_CNT: u32 = i8::MAX as u32; - let fd = &*self.as_std_view()?; - if value == 0 { - // WIT: "If the provided value is 0, an `invalid-argument` error is returned." - return Err(ErrorCode::InvalidArgument); - } - sockopt::set_tcp_keepcnt(fd, value.clamp(MIN_CNT, MAX_CNT))?; + set_keep_alive_count(fd, value)?; Ok(()) } pub fn hop_limit(&self) -> Result { let fd = &*self.as_std_view()?; - get_unicast_hop_limit(fd, self.family) + let n = get_unicast_hop_limit(fd, self.family)?; + Ok(n) } - pub fn set_hop_limit(&self, value: u8) -> Result<(), ErrorCode> { - let fd = &*self.as_std_view()?; - set_unicast_hop_limit(fd, self.family, value)?; - #[cfg(target_os = "macos")] + pub fn set_hop_limit(&mut self, value: u8) -> Result<(), ErrorCode> { { - self.hop_limit - .store(value, core::sync::atomic::Ordering::Relaxed); + let fd = &*self.as_std_view()?; + set_unicast_hop_limit(fd, self.family, value)?; } + self.options.set_hop_limit(value); Ok(()) } pub fn receive_buffer_size(&self) -> Result { let fd = &*self.as_std_view()?; - receive_buffer_size(fd) + let n = receive_buffer_size(fd)?; + Ok(n) } pub fn set_receive_buffer_size(&mut self, value: u64) -> Result<(), ErrorCode> { - let fd = &*self.as_std_view()?; - let res = set_receive_buffer_size(fd, value); - #[cfg(target_os = "macos")] - { - let value = res?; - self.receive_buffer_size - .store(value, core::sync::atomic::Ordering::Relaxed); - } - #[cfg(not(target_os = "macos"))] - { - res?; - } + let res = { + let fd = &*self.as_std_view()?; + set_receive_buffer_size(fd, value)? + }; + self.options.set_receive_buffer_size(res); Ok(()) } pub fn send_buffer_size(&self) -> Result { let fd = &*self.as_std_view()?; - send_buffer_size(fd) + let n = send_buffer_size(fd)?; + Ok(n) } pub fn set_send_buffer_size(&mut self, value: u64) -> Result<(), ErrorCode> { - let fd = &*self.as_std_view()?; - let res = set_send_buffer_size(fd, value); - #[cfg(target_os = "macos")] - { - let value = res?; - self.send_buffer_size - .store(value, core::sync::atomic::Ordering::Relaxed); - } - #[cfg(not(target_os = "macos"))] - { - res?; - } + let res = { + let fd = &*self.as_std_view()?; + set_send_buffer_size(fd, value)? + }; + self.options.set_send_buffer_size(res); Ok(()) } } -fn bind(socket: &tokio::net::TcpSocket, local_address: SocketAddr) -> Result<(), ErrorCode> { - // Automatically bypass the TIME_WAIT state when binding to a specific port - // Unconditionally (re)set SO_REUSEADDR, even when the value is false. - // This ensures we're not accidentally affected by any socket option - // state left behind by a previous failed call to this method. - #[cfg(not(windows))] - if let Err(err) = sockopt::set_socket_reuseaddr(&socket, local_address.port() > 0) { - return Err(err.into()); +#[cfg(not(target_os = "macos"))] +pub use inherits_option::*; +#[cfg(not(target_os = "macos"))] +mod inherits_option { + use crate::sockets::SocketAddressFamily; + use tokio::net::TcpStream; + + #[derive(Default, Clone)] + pub struct NonInheritedOptions; + + impl NonInheritedOptions { + pub fn set_keep_alive_idle_time(&mut self, _value: u64) {} + + pub fn set_hop_limit(&mut self, _value: u8) {} + + pub fn set_receive_buffer_size(&mut self, _value: usize) {} + + pub fn set_send_buffer_size(&mut self, _value: usize) {} + + pub fn apply(&self, _family: SocketAddressFamily, _stream: &TcpStream) {} + } +} + +#[cfg(target_os = "macos")] +pub use does_not_inherit_options::*; +#[cfg(target_os = "macos")] +mod does_not_inherit_options { + use crate::sockets::SocketAddressFamily; + use rustix::net::sockopt; + use std::sync::Arc; + use std::sync::atomic::{AtomicU8, AtomicU64, AtomicUsize, Ordering::Relaxed}; + use std::time::Duration; + use tokio::net::TcpStream; + + // The socket options below are not automatically inherited from the listener + // on all platforms. So we keep track of which options have been explicitly + // set and manually apply those values to newly accepted clients. + #[derive(Default, Clone)] + pub struct NonInheritedOptions(Arc); + + #[derive(Default)] + struct Inner { + receive_buffer_size: AtomicUsize, + send_buffer_size: AtomicUsize, + hop_limit: AtomicU8, + keep_alive_idle_time: AtomicU64, // nanoseconds } - // Perform the OS bind call. - socket - .bind(local_address) - .map_err(|err| match Errno::from_io_error(&err) { - // From https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html: - // > [EAFNOSUPPORT] The specified address is not a valid address for the address family of the specified socket - // - // The most common reasons for this error should have already - // been handled by our own validation slightly higher up in this - // function. This error mapping is here just in case there is - // an edge case we didn't catch. - Some(Errno::AFNOSUPPORT) => ErrorCode::InvalidArgument, - // See: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-bind#:~:text=WSAENOBUFS - // Windows returns WSAENOBUFS when the ephemeral ports have been exhausted. - #[cfg(windows)] - Some(Errno::NOBUFS) => ErrorCode::AddressInUse, - _ => err.into(), - }) + impl NonInheritedOptions { + pub fn set_keep_alive_idle_time(&mut self, value: u64) { + self.0.keep_alive_idle_time.store(value, Relaxed); + } + + pub fn set_hop_limit(&mut self, value: u8) { + self.0.hop_limit.store(value, Relaxed); + } + + pub fn set_receive_buffer_size(&mut self, value: usize) { + self.0.receive_buffer_size.store(value, Relaxed); + } + + pub fn set_send_buffer_size(&mut self, value: usize) { + self.0.send_buffer_size.store(value, Relaxed); + } + + pub fn apply(&self, family: SocketAddressFamily, stream: &TcpStream) { + // Manually inherit socket options from listener. We only have to + // do this on platforms that don't already do this automatically + // and only if a specific value was explicitly set on the listener. + + let receive_buffer_size = self.0.receive_buffer_size.load(Relaxed); + if receive_buffer_size > 0 { + // Ignore potential error. + _ = sockopt::set_socket_recv_buffer_size(&stream, receive_buffer_size); + } + + let send_buffer_size = self.0.send_buffer_size.load(Relaxed); + if send_buffer_size > 0 { + // Ignore potential error. + _ = sockopt::set_socket_send_buffer_size(&stream, send_buffer_size); + } + + // For some reason, IP_TTL is inherited, but IPV6_UNICAST_HOPS isn't. + if family == SocketAddressFamily::Ipv6 { + let hop_limit = self.0.hop_limit.load(Relaxed); + if hop_limit > 0 { + // Ignore potential error. + _ = sockopt::set_ipv6_unicast_hops(&stream, Some(hop_limit)); + } + } + + let keep_alive_idle_time = self.0.keep_alive_idle_time.load(Relaxed); + if keep_alive_idle_time > 0 { + // Ignore potential error. + _ = sockopt::set_tcp_keepidle(&stream, Duration::from_nanos(keep_alive_idle_time)); + } + } + } } diff --git a/crates/wasi/src/p3/sockets/udp.rs b/crates/wasi/src/p3/sockets/udp.rs index bdd3fa92fb..6e2d635cfd 100644 --- a/crates/wasi/src/p3/sockets/udp.rs +++ b/crates/wasi/src/p3/sockets/udp.rs @@ -3,26 +3,21 @@ use core::net::SocketAddr; use std::sync::Arc; -use cap_net_ext::{AddressFamily, Blocking, UdpSocketExt as _}; +use cap_net_ext::AddressFamily; use io_lifetimes::AsSocketlike as _; use io_lifetimes::raw::{FromRawSocketlike as _, IntoRawSocketlike as _}; -use rustix::fd::AsFd; use rustix::io::Errno; -use rustix::net::{connect, connect_unspec}; +use rustix::net::connect; use tracing::debug; use crate::p3::bindings::sockets::types::{ErrorCode, IpAddressFamily, IpSocketAddress}; -use crate::p3::sockets::SocketAddressFamily; -use crate::p3::sockets::util::{ +use crate::runtime::with_ambient_tokio_runtime; +use crate::sockets::util::{ get_unicast_hop_limit, is_valid_address_family, is_valid_remote_address, receive_buffer_size, send_buffer_size, set_receive_buffer_size, set_send_buffer_size, set_unicast_hop_limit, + udp_bind, udp_disconnect, udp_socket, }; -use crate::runtime::with_ambient_tokio_runtime; - -/// Theoretical maximum byte size of a UDP datagram, the real limit is lower, -/// but we do not account for e.g. the transport layer here for simplicity. -/// In practice, datagrams are typically less than 1500 bytes. -pub const MAX_UDP_DATAGRAM_SIZE: usize = u16::MAX as usize; +use crate::sockets::{MAX_UDP_DATAGRAM_SIZE, SocketAddressFamily}; /// The state of a UDP socket. /// @@ -62,7 +57,7 @@ impl UdpSocket { // - Set the NONBLOCK and CLOEXEC flags. Either immediately during socket creation, // or afterwards using ioctl or fcntl. Exact method depends on the platform. - let fd = cap_std::net::UdpSocket::new(family, Blocking::No)?; + let fd = udp_socket(family)?; let socket_address_family = match family { AddressFamily::Ipv4 => SocketAddressFamily::Ipv4, @@ -92,7 +87,7 @@ impl UdpSocket { if !is_valid_address_family(addr.ip(), self.family) { return Err(ErrorCode::InvalidArgument); } - bind(&self.socket, addr)?; + udp_bind(&self.socket, addr)?; self.udp_state = UdpState::Bound; Ok(()) } @@ -101,7 +96,7 @@ impl UdpSocket { if !matches!(self.udp_state, UdpState::Connected(..)) { return Err(ErrorCode::InvalidState); } - disconnect(&self.socket)?; + udp_disconnect(&self.socket)?; self.udp_state = UdpState::Bound; Ok(()) } @@ -119,12 +114,12 @@ impl UdpSocket { // Step #1: Disconnect if let UdpState::Connected(..) = self.udp_state { - disconnect(&self.socket)?; + udp_disconnect(&self.socket)?; self.udp_state = UdpState::Bound; } // Step #2: (Re)connect connect(&self.socket, &addr).map_err(|error| match error { - Errno::AFNOSUPPORT => ErrorCode::InvalidArgument, // See `bind` implementation. + Errno::AFNOSUPPORT => ErrorCode::InvalidArgument, // See `udp_bind` implementation. Errno::INPROGRESS => { debug!("UDP connect returned EINPROGRESS, which should never happen"); ErrorCode::Unknown @@ -231,15 +226,18 @@ impl UdpSocket { } pub fn unicast_hop_limit(&self) -> Result { - get_unicast_hop_limit(&self.socket, self.family) + let n = get_unicast_hop_limit(&self.socket, self.family)?; + Ok(n) } pub fn set_unicast_hop_limit(&self, value: u8) -> Result<(), ErrorCode> { - set_unicast_hop_limit(&self.socket, self.family, value) + set_unicast_hop_limit(&self.socket, self.family, value)?; + Ok(()) } pub fn receive_buffer_size(&self) -> Result { - receive_buffer_size(&self.socket) + let n = receive_buffer_size(&self.socket)?; + Ok(n) } pub fn set_receive_buffer_size(&self, value: u64) -> Result<(), ErrorCode> { @@ -248,7 +246,8 @@ impl UdpSocket { } pub fn send_buffer_size(&self) -> Result { - send_buffer_size(&self.socket) + let n = send_buffer_size(&self.socket)?; + Ok(n) } pub fn set_send_buffer_size(&self, value: u64) -> Result<(), ErrorCode> { @@ -257,57 +256,30 @@ impl UdpSocket { } } -fn bind(sockfd: impl AsFd, addr: SocketAddr) -> Result<(), ErrorCode> { - rustix::net::bind(sockfd, &addr).map_err(|err| match err { - // See: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-bind#:~:text=WSAENOBUFS - // Windows returns WSAENOBUFS when the ephemeral ports have been exhausted. - #[cfg(windows)] - Errno::NOBUFS => ErrorCode::AddressInUse, - Errno::AFNOSUPPORT => ErrorCode::InvalidArgument, - _ => err.into(), - }) -} - -fn disconnect(sockfd: impl AsFd) -> Result<(), ErrorCode> { - match connect_unspec(sockfd) { - // BSD platforms return an error even if the UDP socket was disconnected successfully. - // - // MacOS was kind enough to document this: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/connect.2.html - // > Datagram sockets may dissolve the association by connecting to an - // > invalid address, such as a null address or an address with the address - // > family set to AF_UNSPEC (the error EAFNOSUPPORT will be harmlessly - // > returned). - // - // ... except that this appears to be incomplete, because experiments - // have shown that MacOS actually returns EINVAL, depending on the - // address family of the socket. - #[cfg(target_os = "macos")] - Err(Errno::INVAL | Errno::AFNOSUPPORT) => Ok(()), - Err(err) => Err(err.into()), - Ok(()) => Ok(()), - } -} - -async fn send(socket: &tokio::net::UdpSocket, mut buf: &[u8]) -> Result<(), ErrorCode> { - loop { - let n = socket.send(buf).await?; - if n == buf.len() { - return Ok(()); - } - buf = &buf[n..] +async fn send(socket: &tokio::net::UdpSocket, buf: &[u8]) -> Result<(), ErrorCode> { + let n = socket.send(buf).await?; + // From Rust stdlib docs: + // > Note that the operating system may refuse buffers larger than 65507. + // > However, partial writes are not possible until buffer sizes above `i32::MAX`. + // + // For example, on Windows, at most `i32::MAX` bytes will be written + if n != buf.len() { + Err(ErrorCode::Unknown) + } else { + Ok(()) } } async fn send_to( socket: &tokio::net::UdpSocket, - mut buf: &[u8], + buf: &[u8], addr: SocketAddr, ) -> Result<(), ErrorCode> { - loop { - let n = socket.send_to(buf, addr).await?; - if n == buf.len() { - return Ok(()); - } - buf = &buf[n..] + let n = socket.send_to(buf, addr).await?; + // See [`send`] documentation + if n != buf.len() { + Err(ErrorCode::Unknown) + } else { + Ok(()) } } diff --git a/crates/wasi/src/p3/view.rs b/crates/wasi/src/p3/view.rs index c455eae7dd..4613ceace8 100644 --- a/crates/wasi/src/p3/view.rs +++ b/crates/wasi/src/p3/view.rs @@ -1,3 +1,5 @@ +use wasmtime::component::ResourceTable; + use crate::p3::ctx::WasiCtx; /// A trait which provides access to the [`WasiCtx`] inside the embedder's `T` @@ -12,14 +14,21 @@ use crate::p3::ctx::WasiCtx; /// # Example /// /// ``` -/// use wasmtime_wasi::p3::{WasiCtx, WasiView, WasiCtxBuilder}; +/// use wasmtime_wasi::p3::{WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView}; +/// use wasmtime::component::ResourceTable; /// /// struct MyState { /// ctx: WasiCtx, +/// table: ResourceTable, /// } /// /// impl WasiView for MyState { -/// fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx } +/// fn ctx(&mut self) -> WasiCtxView<'_> { +/// WasiCtxView{ +/// ctx: &mut self.ctx, +/// table: &mut self.table, +/// } +/// } /// } /// ``` /// [`Store`]: wasmtime::Store @@ -28,37 +37,42 @@ use crate::p3::ctx::WasiCtx; pub trait WasiView: Send { /// Yields mutable access to the [`WasiCtx`] configuration used for this /// context. - fn ctx(&mut self) -> &mut WasiCtx; + fn ctx(&mut self) -> WasiCtxView<'_>; +} + +pub struct WasiCtxView<'a> { + pub ctx: &'a mut WasiCtx, + pub table: &'a mut ResourceTable, } -impl WasiView for &mut T { - fn ctx(&mut self) -> &mut WasiCtx { - T::ctx(self) +impl crate::sockets::WasiSocketsView for T { + fn sockets(&mut self) -> crate::sockets::WasiSocketsCtxView<'_> { + let WasiCtxView { ctx, table } = self.ctx(); + crate::sockets::WasiSocketsCtxView { + ctx: &mut ctx.sockets, + table, + } } } -impl WasiView for Box { - fn ctx(&mut self) -> &mut WasiCtx { - T::ctx(self) +impl crate::clocks::WasiClocksView for T { + fn clocks(&mut self) -> &mut crate::clocks::WasiClocksCtx { + &mut self.ctx().ctx.clocks } } -/// A small newtype wrapper which serves as the basis for implementations of -/// `Host` WASI traits in this crate. -/// -/// This type is used as the basis for the implementation of all `Host` traits -/// generated by `bindgen!` for WASI interfaces. This is used automatically with -/// [`add_to_linker`](crate::p3::add_to_linker_sync). -/// -/// This type is otherwise provided if you're calling the `add_to_linker` -/// functions generated by `bindgen!` from the [`bindings` -/// module](crate::p3::bindings). In this situation you'll want to create a value of -/// this type in the closures added to a `Linker`. -#[repr(transparent)] -pub struct WasiImpl(pub T); +impl crate::random::WasiRandomView for T { + fn random(&mut self) -> &mut crate::random::WasiRandomCtx { + &mut self.ctx().ctx.random + } +} -impl WasiView for WasiImpl { - fn ctx(&mut self) -> &mut WasiCtx { - T::ctx(&mut self.0) +impl crate::p3::cli::WasiCliView for T { + fn cli(&mut self) -> crate::p3::cli::WasiCliCtxView<'_> { + let WasiCtxView { ctx, table } = self.ctx(); + crate::p3::cli::WasiCliCtxView { + ctx: &mut ctx.cli, + table, + } } } diff --git a/crates/wasi/src/preview1.rs b/crates/wasi/src/preview1.rs index cc124f0b2f..bf9261f6f7 100644 --- a/crates/wasi/src/preview1.rs +++ b/crates/wasi/src/preview1.rs @@ -74,9 +74,8 @@ use crate::p2::bindings::{ }; use crate::p2::{FsError, IsATTY, WasiCtx, WasiImpl, WasiView}; use anyhow::{Context, bail}; -use std::collections::{BTreeMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashSet, btree_map}; use std::mem::{self, size_of, size_of_val}; -use std::ops::{Deref, DerefMut}; use std::slice; use std::sync::Arc; use std::sync::atomic::{AtomicU64, Ordering}; @@ -286,21 +285,7 @@ struct WasiPreview1Adapter { #[derive(Debug, Default)] struct Descriptors { used: BTreeMap, - free: Vec, -} - -impl Deref for Descriptors { - type Target = BTreeMap; - - fn deref(&self) -> &Self::Target { - &self.used - } -} - -impl DerefMut for Descriptors { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.used - } + free: BTreeSet, } impl Descriptors { @@ -377,42 +362,34 @@ impl Descriptors { /// Returns next descriptor number, which was never assigned fn unused(&self) -> Result { - match self.last_key_value() { + match self.used.last_key_value() { Some((fd, _)) => { if let Some(fd) = fd.checked_add(1) { return Ok(fd); } - if self.len() == u32::MAX as usize { + if self.used.len() == u32::MAX as usize { return Err(types::Errno::Loop.into()); } // TODO: Optimize Ok((0..u32::MAX) .rev() - .find(|fd| !self.contains_key(fd)) + .find(|fd| !self.used.contains_key(fd)) .expect("failed to find an unused file descriptor")) } None => Ok(0), } } - /// Removes the [Descriptor] corresponding to `fd` - fn remove(&mut self, fd: types::Fd) -> Option { - let fd = fd.into(); - let desc = self.used.remove(&fd)?; - self.free.push(fd); - Some(desc) - } - /// Pushes the [Descriptor] returning corresponding number. /// This operation will try to reuse numbers previously removed via [`Self::remove`] /// and rely on [`Self::unused`] if no free numbers are recorded fn push(&mut self, desc: Descriptor) -> Result { - let fd = if let Some(fd) = self.free.pop() { + let fd = if let Some(fd) = self.free.pop_last() { fd } else { self.unused()? }; - assert!(self.insert(fd, desc).is_none()); + assert!(self.used.insert(fd, desc).is_none()); Ok(fd) } } @@ -453,7 +430,7 @@ impl Transaction<'_> { /// Returns [`types::Errno::Badf`] if no [`Descriptor`] is found fn get_descriptor(&self, fd: types::Fd) -> Result<&Descriptor> { let fd = fd.into(); - let desc = self.descriptors.get(&fd).ok_or(types::Errno::Badf)?; + let desc = self.descriptors.used.get(&fd).ok_or(types::Errno::Badf)?; Ok(desc) } @@ -461,7 +438,7 @@ impl Transaction<'_> { /// if it describes a [`Descriptor::File`] fn get_file(&self, fd: types::Fd) -> Result<&File> { let fd = fd.into(); - match self.descriptors.get(&fd) { + match self.descriptors.used.get(&fd) { Some(Descriptor::File(file)) => Ok(file), _ => Err(types::Errno::Badf.into()), } @@ -471,7 +448,7 @@ impl Transaction<'_> { /// if it describes a [`Descriptor::File`] fn get_file_mut(&mut self, fd: types::Fd) -> Result<&mut File> { let fd = fd.into(); - match self.descriptors.get_mut(&fd) { + match self.descriptors.used.get_mut(&fd) { Some(Descriptor::File(file)) => Ok(file), _ => Err(types::Errno::Badf.into()), } @@ -485,7 +462,7 @@ impl Transaction<'_> { /// Returns [`types::Errno::Spipe`] if the descriptor corresponds to stdio fn get_seekable(&self, fd: types::Fd) -> Result<&File> { let fd = fd.into(); - match self.descriptors.get(&fd) { + match self.descriptors.used.get(&fd) { Some(Descriptor::File(file)) => Ok(file), Some( Descriptor::Stdin { .. } | Descriptor::Stdout { .. } | Descriptor::Stderr { .. }, @@ -518,7 +495,7 @@ impl Transaction<'_> { /// if it describes a [`Descriptor::Directory`] fn get_dir_fd(&self, fd: types::Fd) -> Result> { let fd = fd.into(); - match self.descriptors.get(&fd) { + match self.descriptors.used.get(&fd) { Some(Descriptor::Directory { fd, .. }) => Ok(fd.borrowed()), _ => Err(types::Errno::Badf.into()), } @@ -1067,11 +1044,9 @@ impl From for types::Error { PtrOverflow { .. } | PtrOutOfBounds { .. } | PtrNotAligned { .. } => { types::Error::trap(err.into()) } - PtrBorrowed { .. } => types::Errno::Fault.into(), InvalidUtf8 { .. } => types::Errno::Ilseq.into(), TryFromIntError { .. } => types::Errno::Overflow.into(), SliceLengthsDiffer { .. } => types::Errno::Fault.into(), - BorrowCheckerOutOfHandles { .. } => types::Errno::Fault.into(), InFunc { err, .. } => types::Error::from(*err), } } @@ -1327,11 +1302,13 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { _memory: &mut GuestMemory<'_>, fd: types::Fd, ) -> Result<(), types::Error> { - let desc = self - .transact()? - .descriptors - .remove(fd) - .ok_or(types::Errno::Badf)?; + let desc = { + let fd = fd.into(); + let mut st = self.transact()?; + let desc = st.descriptors.used.remove(&fd).ok_or(types::Errno::Badf)?; + st.descriptors.free.insert(fd); + desc + }; match desc { Descriptor::Stdin { stream, .. } => { streams::HostInputStream::drop(&mut self.as_io_impl(), stream) @@ -1830,8 +1807,17 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { to: types::Fd, ) -> Result<(), types::Error> { let mut st = self.transact()?; - let desc = st.descriptors.remove(from).ok_or(types::Errno::Badf)?; - st.descriptors.insert(to.into(), desc); + let from = from.into(); + let btree_map::Entry::Occupied(desc) = st.descriptors.used.entry(from) else { + return Err(types::Errno::Badf.into()); + }; + let to = to.into(); + if from != to { + let desc = desc.remove(); + st.descriptors.free.insert(from); + st.descriptors.free.remove(&to); + st.descriptors.used.insert(to, desc); + } Ok(()) } @@ -2562,7 +2548,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { buf_len: types::Size, ) -> Result<(), types::Error> { let rand = self - .as_wasi_impl() + .wasi + .random .get_random_bytes(buf_len.into()) .context("failed to call `get-random-bytes`") .map_err(types::Error::trap)?; diff --git a/crates/wasi/src/random.rs b/crates/wasi/src/random.rs index f3df9c6af6..c9121091a6 100644 --- a/crates/wasi/src/random.rs +++ b/crates/wasi/src/random.rs @@ -1,28 +1,10 @@ use cap_rand::{Rng as _, RngCore, SeedableRng as _}; +use wasmtime::component::HasData; -#[repr(transparent)] -pub struct WasiRandomImpl(pub T); +pub(crate) struct WasiRandom; -impl WasiRandomView for &mut T { - fn random(&mut self) -> &mut WasiRandomCtx { - (**self).random() - } -} - -impl WasiRandomView for WasiRandomImpl { - fn random(&mut self) -> &mut WasiRandomCtx { - self.0.random() - } -} - -impl WasiRandomView for WasiRandomCtx { - fn random(&mut self) -> &mut WasiRandomCtx { - self - } -} - -pub trait WasiRandomView: Send { - fn random(&mut self) -> &mut WasiRandomCtx; +impl HasData for WasiRandom { + type Data<'a> = &'a mut WasiRandomCtx; } pub struct WasiRandomCtx { @@ -52,6 +34,16 @@ impl Default for WasiRandomCtx { } } +pub trait WasiRandomView: Send { + fn random(&mut self) -> &mut WasiRandomCtx; +} + +impl WasiRandomView for WasiRandomCtx { + fn random(&mut self) -> &mut WasiRandomCtx { + self + } +} + /// Implement `insecure-random` using a deterministic cycle of bytes. pub struct Deterministic { cycle: std::iter::Cycle>, diff --git a/crates/wasi/src/sockets/mod.rs b/crates/wasi/src/sockets/mod.rs new file mode 100644 index 0000000000..463c80adca --- /dev/null +++ b/crates/wasi/src/sockets/mod.rs @@ -0,0 +1,147 @@ +use core::future::Future; +use core::ops::Deref; + +use std::net::SocketAddr; +use std::pin::Pin; +use std::sync::Arc; + +pub(crate) mod util; + +use wasmtime::component::ResourceTable; + +/// Value taken from rust std library. +pub const DEFAULT_TCP_BACKLOG: u32 = 128; + +/// Theoretical maximum byte size of a UDP datagram, the real limit is lower, +/// but we do not account for e.g. the transport layer here for simplicity. +/// In practice, datagrams are typically less than 1500 bytes. +pub const MAX_UDP_DATAGRAM_SIZE: usize = u16::MAX as usize; + +#[derive(Clone, Default)] +pub struct WasiSocketsCtx { + pub socket_addr_check: SocketAddrCheck, + pub allowed_network_uses: AllowedNetworkUses, +} + +pub struct WasiSocketsCtxView<'a> { + pub ctx: &'a mut WasiSocketsCtx, + pub table: &'a mut ResourceTable, +} + +pub trait WasiSocketsView: Send { + fn sockets(&mut self) -> WasiSocketsCtxView<'_>; +} + +#[derive(Copy, Clone)] +pub struct AllowedNetworkUses { + pub ip_name_lookup: bool, + pub udp: bool, + pub tcp: bool, +} + +impl Default for AllowedNetworkUses { + fn default() -> Self { + Self { + ip_name_lookup: false, + udp: true, + tcp: true, + } + } +} + +impl AllowedNetworkUses { + pub(crate) fn check_allowed_udp(&self) -> std::io::Result<()> { + if !self.udp { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "UDP is not allowed", + )); + } + + Ok(()) + } + + pub(crate) fn check_allowed_tcp(&self) -> std::io::Result<()> { + if !self.tcp { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "TCP is not allowed", + )); + } + + Ok(()) + } +} + +/// A check that will be called for each socket address that is used of whether the address is permitted. +#[derive(Clone)] +pub struct SocketAddrCheck( + pub(crate) Arc< + dyn Fn(SocketAddr, SocketAddrUse) -> Pin + Send + Sync>> + + Send + + Sync, + >, +); + +impl SocketAddrCheck { + /// A check that will be called for each socket address that is used. + /// + /// Returning `true` will permit socket connections to the `SocketAddr`, + /// while returning `false` will reject the connection. + pub fn new( + f: impl Fn(SocketAddr, SocketAddrUse) -> Pin + Send + Sync>> + + Send + + Sync + + 'static, + ) -> Self { + Self(Arc::new(f)) + } + + pub async fn check(&self, addr: SocketAddr, reason: SocketAddrUse) -> std::io::Result<()> { + if (self.0)(addr, reason).await { + Ok(()) + } else { + Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "An address was not permitted by the socket address check.", + )) + } + } +} + +impl Deref for SocketAddrCheck { + type Target = dyn Fn(SocketAddr, SocketAddrUse) -> Pin + Send + Sync>> + + Send + + Sync; + + fn deref(&self) -> &Self::Target { + self.0.as_ref() + } +} + +impl Default for SocketAddrCheck { + fn default() -> Self { + Self(Arc::new(|_, _| Box::pin(async { false }))) + } +} + +/// The reason what a socket address is being used for. +#[derive(Clone, Copy, Debug)] +pub enum SocketAddrUse { + /// Binding TCP socket + TcpBind, + /// Connecting TCP socket + TcpConnect, + /// Binding UDP socket + UdpBind, + /// Connecting UDP socket + UdpConnect, + /// Sending datagram on non-connected UDP socket + UdpOutgoingDatagram, +} + +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum SocketAddressFamily { + Ipv4, + Ipv6, +} diff --git a/crates/wasi/src/sockets/util.rs b/crates/wasi/src/sockets/util.rs new file mode 100644 index 0000000000..dafe6e2584 --- /dev/null +++ b/crates/wasi/src/sockets/util.rs @@ -0,0 +1,431 @@ +use core::fmt; +use core::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use core::str::FromStr as _; +use core::time::Duration; + +use cap_net_ext::{AddressFamily, Blocking, UdpSocketExt}; +use rustix::fd::AsFd; +use rustix::io::Errno; +use rustix::net::{bind, connect_unspec, sockopt}; +use tracing::debug; + +use crate::sockets::SocketAddressFamily; + +#[derive(Debug)] +pub enum ErrorCode { + Unknown, + AccessDenied, + NotSupported, + InvalidArgument, + OutOfMemory, + Timeout, + InvalidState, + AddressNotBindable, + AddressInUse, + RemoteUnreachable, + ConnectionRefused, + ConnectionReset, + ConnectionAborted, + DatagramTooLarge, +} + +impl fmt::Display for ErrorCode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +impl std::error::Error for ErrorCode {} + +fn is_deprecated_ipv4_compatible(addr: Ipv6Addr) -> bool { + matches!(addr.segments(), [0, 0, 0, 0, 0, 0, _, _]) + && addr != Ipv6Addr::UNSPECIFIED + && addr != Ipv6Addr::LOCALHOST +} + +pub fn is_valid_address_family(addr: IpAddr, socket_family: SocketAddressFamily) -> bool { + match (socket_family, addr) { + (SocketAddressFamily::Ipv4, IpAddr::V4(..)) => true, + (SocketAddressFamily::Ipv6, IpAddr::V6(ipv6)) => { + // Reject IPv4-*compatible* IPv6 addresses. They have been deprecated + // since 2006, OS handling of them is inconsistent and our own + // validations don't take them into account either. + // Note that these are not the same as IPv4-*mapped* IPv6 addresses. + !is_deprecated_ipv4_compatible(ipv6) && ipv6.to_ipv4_mapped().is_none() + } + _ => false, + } +} + +pub fn is_valid_remote_address(addr: SocketAddr) -> bool { + !addr.ip().to_canonical().is_unspecified() && addr.port() != 0 +} + +pub fn is_valid_unicast_address(addr: IpAddr) -> bool { + match addr.to_canonical() { + IpAddr::V4(ipv4) => !ipv4.is_multicast() && !ipv4.is_broadcast(), + IpAddr::V6(ipv6) => !ipv6.is_multicast(), + } +} + +pub fn to_ipv4_addr(addr: (u8, u8, u8, u8)) -> Ipv4Addr { + let (x0, x1, x2, x3) = addr; + Ipv4Addr::new(x0, x1, x2, x3) +} + +pub fn from_ipv4_addr(addr: Ipv4Addr) -> (u8, u8, u8, u8) { + let [x0, x1, x2, x3] = addr.octets(); + (x0, x1, x2, x3) +} + +pub fn to_ipv6_addr(addr: (u16, u16, u16, u16, u16, u16, u16, u16)) -> Ipv6Addr { + let (x0, x1, x2, x3, x4, x5, x6, x7) = addr; + Ipv6Addr::new(x0, x1, x2, x3, x4, x5, x6, x7) +} + +pub fn from_ipv6_addr(addr: Ipv6Addr) -> (u16, u16, u16, u16, u16, u16, u16, u16) { + let [x0, x1, x2, x3, x4, x5, x6, x7] = addr.segments(); + (x0, x1, x2, x3, x4, x5, x6, x7) +} + +/* + * Syscalls wrappers with (opinionated) portability fixes. + */ + +pub fn normalize_get_buffer_size(value: usize) -> usize { + if cfg!(target_os = "linux") { + // Linux doubles the value passed to setsockopt to allow space for bookkeeping overhead. + // getsockopt returns this internally doubled value. + // We'll half the value to at least get it back into the same ballpark that the application requested it in. + // + // This normalized behavior is tested for in: test-programs/src/bin/preview2_tcp_sockopts.rs + value / 2 + } else { + value + } +} + +pub fn normalize_set_buffer_size(value: usize) -> usize { + value.clamp(1, i32::MAX as usize) +} + +impl From for ErrorCode { + fn from(value: std::io::Error) -> Self { + (&value).into() + } +} + +impl From<&std::io::Error> for ErrorCode { + fn from(value: &std::io::Error) -> Self { + // Attempt the more detailed native error code first: + if let Some(errno) = Errno::from_io_error(value) { + return errno.into(); + } + + match value.kind() { + std::io::ErrorKind::AddrInUse => Self::AddressInUse, + std::io::ErrorKind::AddrNotAvailable => Self::AddressNotBindable, + std::io::ErrorKind::ConnectionAborted => Self::ConnectionAborted, + std::io::ErrorKind::ConnectionRefused => Self::ConnectionRefused, + std::io::ErrorKind::ConnectionReset => Self::ConnectionReset, + std::io::ErrorKind::InvalidInput => Self::InvalidArgument, + std::io::ErrorKind::NotConnected => Self::InvalidState, + std::io::ErrorKind::OutOfMemory => Self::OutOfMemory, + std::io::ErrorKind::PermissionDenied => Self::AccessDenied, + std::io::ErrorKind::TimedOut => Self::Timeout, + std::io::ErrorKind::Unsupported => Self::NotSupported, + _ => { + debug!("unknown I/O error: {value}"); + Self::Unknown + } + } + } +} + +impl From for ErrorCode { + fn from(value: Errno) -> Self { + (&value).into() + } +} + +impl From<&Errno> for ErrorCode { + fn from(value: &Errno) -> Self { + match *value { + #[cfg(not(windows))] + Errno::PERM => Self::AccessDenied, + Errno::ACCESS => Self::AccessDenied, + Errno::ADDRINUSE => Self::AddressInUse, + Errno::ADDRNOTAVAIL => Self::AddressNotBindable, + Errno::TIMEDOUT => Self::Timeout, + Errno::CONNREFUSED => Self::ConnectionRefused, + Errno::CONNRESET => Self::ConnectionReset, + Errno::CONNABORTED => Self::ConnectionAborted, + Errno::INVAL => Self::InvalidArgument, + Errno::HOSTUNREACH => Self::RemoteUnreachable, + Errno::HOSTDOWN => Self::RemoteUnreachable, + Errno::NETDOWN => Self::RemoteUnreachable, + Errno::NETUNREACH => Self::RemoteUnreachable, + #[cfg(target_os = "linux")] + Errno::NONET => Self::RemoteUnreachable, + Errno::ISCONN => Self::InvalidState, + Errno::NOTCONN => Self::InvalidState, + Errno::DESTADDRREQ => Self::InvalidState, + Errno::MSGSIZE => Self::DatagramTooLarge, + #[cfg(not(windows))] + Errno::NOMEM => Self::OutOfMemory, + Errno::NOBUFS => Self::OutOfMemory, + Errno::OPNOTSUPP => Self::NotSupported, + Errno::NOPROTOOPT => Self::NotSupported, + Errno::PFNOSUPPORT => Self::NotSupported, + Errno::PROTONOSUPPORT => Self::NotSupported, + Errno::PROTOTYPE => Self::NotSupported, + Errno::SOCKTNOSUPPORT => Self::NotSupported, + Errno::AFNOSUPPORT => Self::NotSupported, + + // FYI, EINPROGRESS should have already been handled by connect. + _ => { + debug!("unknown I/O error: {value}"); + Self::Unknown + } + } + } +} + +pub fn get_ip_ttl(fd: impl AsFd) -> Result { + let v = sockopt::ip_ttl(fd)?; + let Ok(v) = v.try_into() else { + return Err(ErrorCode::NotSupported); + }; + Ok(v) +} + +pub fn get_ipv6_unicast_hops(fd: impl AsFd) -> Result { + let v = sockopt::ipv6_unicast_hops(fd)?; + Ok(v) +} + +pub fn get_unicast_hop_limit(fd: impl AsFd, family: SocketAddressFamily) -> Result { + match family { + SocketAddressFamily::Ipv4 => get_ip_ttl(fd), + SocketAddressFamily::Ipv6 => get_ipv6_unicast_hops(fd), + } +} + +pub fn set_unicast_hop_limit( + fd: impl AsFd, + family: SocketAddressFamily, + value: u8, +) -> Result<(), ErrorCode> { + if value == 0 { + // WIT: "If the provided value is 0, an `invalid-argument` error is returned." + // + // A well-behaved IP application should never send out new packets with TTL 0. + // We validate the value ourselves because OS'es are not consistent in this. + // On Linux the validation is even inconsistent between their IPv4 and IPv6 implementation. + return Err(ErrorCode::InvalidArgument); + } + match family { + SocketAddressFamily::Ipv4 => { + sockopt::set_ip_ttl(fd, value.into())?; + } + SocketAddressFamily::Ipv6 => { + sockopt::set_ipv6_unicast_hops(fd, Some(value))?; + } + } + Ok(()) +} + +pub fn receive_buffer_size(fd: impl AsFd) -> Result { + let v = sockopt::socket_recv_buffer_size(fd)?; + Ok(normalize_get_buffer_size(v).try_into().unwrap_or(u64::MAX)) +} + +pub fn set_receive_buffer_size(fd: impl AsFd, value: u64) -> Result { + if value == 0 { + // WIT: "If the provided value is 0, an `invalid-argument` error is returned." + return Err(ErrorCode::InvalidArgument); + } + let value = value.try_into().unwrap_or(usize::MAX); + let value = normalize_set_buffer_size(value); + match sockopt::set_socket_recv_buffer_size(fd, value) { + // Most platforms (Linux, Windows, Fuchsia, Solaris, Illumos, Haiku, ESP-IDF, ..and more?) treat the value + // passed to SO_SNDBUF/SO_RCVBUF as a performance tuning hint and silently clamp the input if it exceeds + // their capability. + // As far as I can see, only the *BSD family views this option as a hard requirement and fails when the + // value is out of range. We normalize this behavior in favor of the more commonly understood + // "performance hint" semantics. In other words; even ENOBUFS is "Ok". + // A future improvement could be to query the corresponding sysctl on *BSD platforms and clamp the input + // `size` ourselves, to completely close the gap with other platforms. + // + // This normalized behavior is tested for in: test-programs/src/bin/preview2_tcp_sockopts.rs + Err(Errno::NOBUFS) => {} + Err(err) => return Err(err.into()), + _ => {} + }; + Ok(value) +} + +pub fn send_buffer_size(fd: impl AsFd) -> Result { + let v = sockopt::socket_send_buffer_size(fd)?; + Ok(normalize_get_buffer_size(v).try_into().unwrap_or(u64::MAX)) +} + +pub fn set_send_buffer_size(fd: impl AsFd, value: u64) -> Result { + if value == 0 { + // WIT: "If the provided value is 0, an `invalid-argument` error is returned." + return Err(ErrorCode::InvalidArgument); + } + let value = value.try_into().unwrap_or(usize::MAX); + let value = normalize_set_buffer_size(value); + match sockopt::set_socket_send_buffer_size(fd, value) { + Err(Errno::NOBUFS) => {} + Err(err) => return Err(err.into()), + _ => {} + }; + Ok(value) +} + +pub fn set_keep_alive_idle_time(fd: impl AsFd, value: u64) -> Result { + const NANOS_PER_SEC: u64 = 1_000_000_000; + + // Ensure that the value passed to the actual syscall never gets rounded down to 0. + const MIN: u64 = NANOS_PER_SEC; + + // Cap it at Linux' maximum, which appears to have the lowest limit across our supported platforms. + const MAX: u64 = (i16::MAX as u64) * NANOS_PER_SEC; + + if value <= 0 { + // WIT: "If the provided value is 0, an `invalid-argument` error is returned." + return Err(ErrorCode::InvalidArgument); + } + let value = value.clamp(MIN, MAX); + sockopt::set_tcp_keepidle(fd, Duration::from_nanos(value))?; + Ok(value) +} + +pub fn set_keep_alive_interval(fd: impl AsFd, value: Duration) -> Result<(), ErrorCode> { + // Ensure that any fractional value passed to the actual syscall never gets rounded down to 0. + const MIN: Duration = Duration::from_secs(1); + + // Cap it at Linux' maximum, which appears to have the lowest limit across our supported platforms. + const MAX: Duration = Duration::from_secs(i16::MAX as u64); + + if value <= Duration::ZERO { + // WIT: "If the provided value is 0, an `invalid-argument` error is returned." + return Err(ErrorCode::InvalidArgument); + } + sockopt::set_tcp_keepintvl(fd, value.clamp(MIN, MAX))?; + Ok(()) +} + +pub fn set_keep_alive_count(fd: impl AsFd, value: u32) -> Result<(), ErrorCode> { + const MIN_CNT: u32 = 1; + // Cap it at Linux' maximum, which appears to have the lowest limit across our supported platforms. + const MAX_CNT: u32 = i8::MAX as u32; + + if value == 0 { + // WIT: "If the provided value is 0, an `invalid-argument` error is returned." + return Err(ErrorCode::InvalidArgument); + } + sockopt::set_tcp_keepcnt(fd, value.clamp(MIN_CNT, MAX_CNT))?; + Ok(()) +} + +pub fn tcp_bind( + socket: &tokio::net::TcpSocket, + local_address: SocketAddr, +) -> Result<(), ErrorCode> { + // Automatically bypass the TIME_WAIT state when binding to a specific port + // Unconditionally (re)set SO_REUSEADDR, even when the value is false. + // This ensures we're not accidentally affected by any socket option + // state left behind by a previous failed call to this method. + #[cfg(not(windows))] + if let Err(err) = sockopt::set_socket_reuseaddr(&socket, local_address.port() > 0) { + return Err(err.into()); + } + + // Perform the OS bind call. + socket + .bind(local_address) + .map_err(|err| match Errno::from_io_error(&err) { + // From https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html: + // > [EAFNOSUPPORT] The specified address is not a valid address for the address family of the specified socket + // + // The most common reasons for this error should have already + // been handled by our own validation slightly higher up in this + // function. This error mapping is here just in case there is + // an edge case we didn't catch. + Some(Errno::AFNOSUPPORT) => ErrorCode::InvalidArgument, + // See: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-bind#:~:text=WSAENOBUFS + // Windows returns WSAENOBUFS when the ephemeral ports have been exhausted. + #[cfg(windows)] + Some(Errno::NOBUFS) => ErrorCode::AddressInUse, + _ => err.into(), + }) +} + +pub fn udp_socket(family: AddressFamily) -> std::io::Result { + // Delegate socket creation to cap_net_ext. They handle a couple of things for us: + // - On Windows: call WSAStartup if not done before. + // - Set the NONBLOCK and CLOEXEC flags. Either immediately during socket creation, + // or afterwards using ioctl or fcntl. Exact method depends on the platform. + + let socket = cap_std::net::UdpSocket::new(family, Blocking::No)?; + Ok(socket) +} + +pub fn udp_bind(sockfd: impl AsFd, addr: SocketAddr) -> Result<(), ErrorCode> { + bind(sockfd, &addr).map_err(|err| match err { + // See: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-bind#:~:text=WSAENOBUFS + // Windows returns WSAENOBUFS when the ephemeral ports have been exhausted. + #[cfg(windows)] + Errno::NOBUFS => ErrorCode::AddressInUse, + // From https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html: + // > [EAFNOSUPPORT] The specified address is not a valid address for the address family of the specified socket + // + // The most common reasons for this error should have already + // been handled by our own validation slightly higher up in this + // function. This error mapping is here just in case there is + // an edge case we didn't catch. + Errno::AFNOSUPPORT => ErrorCode::InvalidArgument, + _ => err.into(), + }) +} + +pub fn udp_disconnect(sockfd: impl AsFd) -> Result<(), ErrorCode> { + match connect_unspec(sockfd) { + // BSD platforms return an error even if the UDP socket was disconnected successfully. + // + // MacOS was kind enough to document this: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/connect.2.html + // > Datagram sockets may dissolve the association by connecting to an + // > invalid address, such as a null address or an address with the address + // > family set to AF_UNSPEC (the error EAFNOSUPPORT will be harmlessly + // > returned). + // + // ... except that this appears to be incomplete, because experiments + // have shown that MacOS actually returns EINVAL, depending on the + // address family of the socket. + #[cfg(target_os = "macos")] + Err(Errno::INVAL | Errno::AFNOSUPPORT) => Ok(()), + Err(err) => Err(err.into()), + Ok(()) => Ok(()), + } +} + +pub fn parse_host(name: &str) -> Result { + // `url::Host::parse` serves us two functions: + // 1. validate the input is a valid domain name or IP, + // 2. convert unicode domains to punycode. + match url::Host::parse(&name) { + Ok(host) => Ok(host), + + // `url::Host::parse` doesn't understand bare IPv6 addresses without [brackets] + Err(_) => { + if let Ok(addr) = Ipv6Addr::from_str(name) { + Ok(url::Host::Ipv6(addr)) + } else { + Err(ErrorCode::InvalidArgument) + } + } + } +} diff --git a/crates/wasi/tests/all/p2/api.rs b/crates/wasi/tests/all/p2/api.rs index 29df15d9ae..e4448c5d78 100644 --- a/crates/wasi/tests/all/p2/api.rs +++ b/crates/wasi/tests/all/p2/api.rs @@ -133,7 +133,9 @@ fn api_proxy_forward_request() {} wasmtime::component::bindgen!({ path: "src/p2/wit", world: "test-reactor", - async: true, + imports: { default: async }, + exports: { default: async }, + require_store_data_send: true, with: { "wasi": wasmtime_wasi::p2::bindings }, ownership: Borrowing { duplicate_if_necessary: false diff --git a/crates/wasi/tests/all/p3/mod.rs b/crates/wasi/tests/all/p3/mod.rs index 2eaaf8219b..cbb0940422 100644 --- a/crates/wasi/tests/all/p3/mod.rs +++ b/crates/wasi/tests/all/p3/mod.rs @@ -6,14 +6,11 @@ use anyhow::{Context as _, anyhow}; use test_programs_artifacts::*; use wasmtime::Store; use wasmtime::component::{Component, Linker, ResourceTable}; -use wasmtime_wasi::p2::{IoView, WasiCtx, WasiCtxBuilder, WasiView}; +use wasmtime_wasi::p2::{self, IoView}; use wasmtime_wasi::p3::ResourceView; use wasmtime_wasi::p3::bindings::Command; -use wasmtime_wasi::p3::cli::{WasiCliCtx, WasiCliView}; use wasmtime_wasi::p3::filesystem::{WasiFilesystemCtx, WasiFilesystemView}; -use wasmtime_wasi::p3::sockets::{ - AllowedNetworkUses, SocketAddrCheck, WasiSocketsCtx, WasiSocketsView, -}; +use wasmtime_wasi::p3::{self, WasiCtxView}; use wasmtime_wasi::{DirPerms, FilePerms}; macro_rules! assert_test_exists { @@ -24,39 +21,35 @@ macro_rules! assert_test_exists { } struct Ctx { - cli: WasiCliCtx, filesystem: WasiFilesystemCtx, - sockets: WasiSocketsCtx, table: ResourceTable, - wasip2: WasiCtx, - wasip3: wasmtime_wasi::p3::WasiCtx, + p2: p2::WasiCtx, + p3: p3::WasiCtx, } impl Default for Ctx { fn default() -> Self { Self { - cli: WasiCliCtx::default(), filesystem: WasiFilesystemCtx::default(), - sockets: WasiSocketsCtx { - socket_addr_check: SocketAddrCheck::new(|_, _| Box::pin(async { true })), - allowed_network_uses: AllowedNetworkUses { - ip_name_lookup: true, - udp: true, - tcp: true, - }, - }, table: ResourceTable::default(), - wasip2: WasiCtxBuilder::new().inherit_stdio().build(), - wasip3: wasmtime_wasi::p3::WasiCtxBuilder::new() - .inherit_stdio() - .build(), + p2: p2::WasiCtxBuilder::new().inherit_stdio().build(), + p3: p3::WasiCtxBuilder::new().inherit_stdio().build(), } } } -impl WasiView for Ctx { - fn ctx(&mut self) -> &mut WasiCtx { - &mut self.wasip2 +impl p2::WasiView for Ctx { + fn ctx(&mut self) -> &mut p2::WasiCtx { + &mut self.p2 + } +} + +impl p3::WasiView for Ctx { + fn ctx(&mut self) -> WasiCtxView<'_> { + WasiCtxView { + ctx: &mut self.p3, + table: &mut self.table, + } } } @@ -72,33 +65,14 @@ impl ResourceView for Ctx { } } -impl WasiCliView for Ctx { - fn cli(&mut self) -> &WasiCliCtx { - &self.cli - } -} - impl WasiFilesystemView for Ctx { fn filesystem(&self) -> &WasiFilesystemCtx { &self.filesystem } } -impl WasiSocketsView for Ctx { - fn sockets(&self) -> &WasiSocketsCtx { - &self.sockets - } -} - -impl wasmtime_wasi::p3::WasiView for Ctx { - fn ctx(&mut self) -> &mut wasmtime_wasi::p3::WasiCtx { - &mut self.wasip3 - } -} - async fn run(path: &str) -> anyhow::Result<()> { - _ = env_logger::try_init(); - + let _ = env_logger::try_init(); let path = Path::new(path); let engine = test_programs_artifacts::engine(|config| { config.async_support(true); @@ -112,19 +86,39 @@ async fn run(path: &str) -> anyhow::Result<()> { wasmtime_wasi::p3::add_to_linker(&mut linker).context("failed to link `wasi:cli@0.3.x`")?; let mut filesystem = WasiFilesystemCtx::default(); + let table = ResourceTable::default(); + + let p2 = p2::WasiCtx::builder() + .inherit_stdout() + .inherit_stderr() + .build(); + + let mut p3 = p3::WasiCtxBuilder::new(); + let name = path.file_stem().unwrap().to_str().unwrap(); let tempdir = tempfile::Builder::new() .prefix(&format!( "wasi_components_{}_", path.file_stem().unwrap().to_str().unwrap() )) .tempdir()?; + p3.args(&[name, "."]) + .inherit_network() + .allow_ip_name_lookup(true); println!("preopen: {tempdir:?}"); filesystem.preopened_dir(tempdir.path(), ".", DirPerms::all(), FilePerms::all())?; + p3.preopened_dir(tempdir.path(), ".", DirPerms::all(), FilePerms::all())?; + for (var, val) in test_programs_artifacts::wasi_tests_environment() { + p3.env(var, val); + } + let p3 = p3.build(); + let mut store = Store::new( &engine, Ctx { + table, + p2, + p3, filesystem, - ..Ctx::default() }, ); let instance = linker.instantiate_async(&mut store, &component).await?; @@ -142,6 +136,11 @@ async fn run(path: &str) -> anyhow::Result<()> { foreach_p3!(assert_test_exists); +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn p3_cli() -> anyhow::Result<()> { + run(P3_CLI_COMPONENT).await +} + #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p3_clocks_sleep() -> anyhow::Result<()> { run(P3_CLOCKS_SLEEP_COMPONENT).await @@ -198,7 +197,6 @@ async fn p3_sockets_udp_connect() -> anyhow::Result<()> { } #[test_log::test(tokio::test(flavor = "multi_thread"))] -#[ignore = "https://github.com/bytecodealliance/wasip3-prototyping/issues/44"] async fn p3_sockets_udp_sample_application() -> anyhow::Result<()> { run(P3_SOCKETS_UDP_SAMPLE_APPLICATION_COMPONENT).await } diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml index 3b15458ba5..f8f67e81aa 100644 --- a/crates/wasmtime/Cargo.toml +++ b/crates/wasmtime/Cargo.toml @@ -19,7 +19,7 @@ all-features = true [dependencies] wasmtime-asm-macros = { workspace = true, optional = true } wasmtime-environ = { workspace = true } -wasmtime-jit-debug = { workspace = true, features = ["gdb_jit_int", "perf_jitdump"], optional = true } +wasmtime-jit-debug = { workspace = true, optional = true } wasmtime-jit-icache-coherence = { workspace = true, optional = true } wasmtime-cache = { workspace = true, optional = true } wasmtime-fiber = { workspace = true, optional = true } @@ -49,9 +49,8 @@ postcard = { workspace = true } indexmap = { workspace = true } once_cell = { version = "1.12.0", optional = true } rayon = { version = "1.0", optional = true } -object = { workspace = true } +object = { workspace = true, features = ['unaligned'] } async-trait = { workspace = true, optional = true } -trait-variant = { workspace = true, optional = true } encoding_rs = { version = "0.8.31", optional = true } bumpalo = "3.11.0" fxprof-processed-profile = { version = "0.6.0", optional = true } @@ -175,12 +174,12 @@ incremental-cache = ["wasmtime-cranelift?/incremental-cache", "std"] # Enables support for profiling guest modules. profiling = [ "dep:fxprof-processed-profile", - "dep:wasmtime-jit-debug", "dep:ittapi", "dep:rustix", "rustix/thread", "dep:serde_json", "std", + "wasmtime-jit-debug/perf_jitdump", ] # Enables parallel compilation of WebAssembly code. @@ -194,7 +193,6 @@ cache = ["dep:wasmtime-cache", "std"] async = [ "dep:wasmtime-fiber", "dep:async-trait", - "dep:trait-variant", "wasmtime-component-macro?/async", "runtime", ] @@ -243,7 +241,9 @@ coredump = ["dep:wasm-encoder", "runtime", "std"] # Export some symbols from the final binary to assist in debugging # Cranelift-generated code with native debuggers like GDB and LLDB. -debug-builtins = ["dep:wasmtime-jit-debug", "std"] +debug-builtins = [ + "wasmtime-jit-debug/gdb_jit_int", +] # Enable support for executing compiled Wasm modules. runtime = [ @@ -334,6 +334,7 @@ std = [ 'addr2line?/std', "dep:rustix", "wasmtime-jit-icache-coherence", + "wasmtime-jit-debug?/std", ] # Enables support for the `Store::call_hook` API which enables injecting custom diff --git a/crates/wasmtime/build.rs b/crates/wasmtime/build.rs index fef5f94d76..a0aab31f9d 100644 --- a/crates/wasmtime/build.rs +++ b/crates/wasmtime/build.rs @@ -27,10 +27,10 @@ fn main() { custom_cfg("has_virtual_memory", has_virtual_memory); custom_cfg("has_host_compiler_backend", has_host_compiler_backend); - // If this OS isn't supported or if Cranelift doesn't support the host then - // there's no need to build these helpers. + // If this OS isn't supported and no debug-builtins or if Cranelift doesn't support + // the host or there's no need to build these helpers. #[cfg(feature = "runtime")] - if supported_os && has_host_compiler_backend { + if has_host_compiler_backend && (supported_os || cfg!(feature = "debug-builtins")) { build_c_helpers(); } diff --git a/crates/wasmtime/src/compile.rs b/crates/wasmtime/src/compile.rs index b8d059b08b..9db99d0f9d 100644 --- a/crates/wasmtime/src/compile.rs +++ b/crates/wasmtime/src/compile.rs @@ -31,9 +31,15 @@ use std::{ borrow::Cow, collections::{BTreeMap, BTreeSet, btree_map}, mem, + ops::Range, }; +use call_graph::CallGraph; use wasmtime_environ::CompiledFunctionBody; +use wasmtime_environ::FuncIndex; +use wasmtime_environ::InliningCompiler; +use wasmtime_environ::IntraModuleInlining; +use wasmtime_environ::Tunables; #[cfg(feature = "component-model")] use wasmtime_environ::component::Translator; use wasmtime_environ::{ @@ -43,7 +49,9 @@ use wasmtime_environ::{ StaticModuleIndex, }; +mod call_graph; mod scc; +mod stratify; mod code_builder; pub use self::code_builder::{CodeBuilder, CodeHint, HashedEngineCompileEnv}; @@ -207,7 +215,7 @@ pub(crate) fn build_component_artifacts( Ok((result, Some(artifacts))) } -type CompileInput<'a> = Box Result + Send + 'a>; +type CompileInput<'a> = Box Result> + Send + 'a>; /// A sortable, comparable key for a compilation output. /// @@ -257,6 +265,11 @@ impl CompileKey { const KIND_OFFSET: u32 = 32 - Self::KIND_BITS; const KIND_MASK: u32 = ((1 << Self::KIND_BITS) - 1) << Self::KIND_OFFSET; + const fn new_kind(kind: u32) -> u32 { + assert!(kind < (1 << Self::KIND_BITS)); + kind << Self::KIND_OFFSET + } + fn kind(&self) -> CompileKind { let k = self.namespace & Self::KIND_MASK; if k == u32::from(CompileKind::WasmFunction) { @@ -289,9 +302,8 @@ impl CompileKey { StaticModuleIndex::from_u32(self.namespace & !Self::KIND_MASK) } - const fn new_kind(kind: u32) -> u32 { - assert!(kind < (1 << Self::KIND_BITS)); - kind << Self::KIND_OFFSET + fn defined_func_index(&self) -> DefinedFuncIndex { + DefinedFuncIndex::from_u32(self.index) } // NB: more kinds in the other `impl` block. @@ -352,6 +364,22 @@ enum CompiledFunction { } impl CompiledFunction { + fn as_function(&self) -> Option<&T> { + match self { + Self::Function(f) => Some(f), + #[cfg(feature = "component-model")] + Self::AllCallFunc(_) => None, + } + } + + fn as_function_mut(&mut self) -> Option<&mut T> { + match self { + Self::Function(f) => Some(f), + #[cfg(feature = "component-model")] + Self::AllCallFunc(_) => None, + } + } + fn unwrap_function(self) -> T { match self { Self::Function(f) => f, @@ -376,11 +404,26 @@ impl From> for CompiledFunction { key: CompileKey, symbol: String, function: CompiledFunction, start_srcloc: FilePos, + translation: Option<&'a ModuleTranslation<'a>>, + func_body: Option>, +} + +/// Inputs to our inlining heuristics. +struct InlineHeuristicParams<'a> { + tunables: &'a Tunables, + caller_size: u32, + caller_module: StaticModuleIndex, + caller_def_func: DefinedFuncIndex, + caller_needs_gc_heap: bool, + callee_size: u32, + callee_module: StaticModuleIndex, + callee_def_func: DefinedFuncIndex, + callee_needs_gc_heap: bool, } /// The collection of things we need to compile for a Wasm module or component. @@ -390,7 +433,10 @@ struct CompileInputs<'a> { } impl<'a> CompileInputs<'a> { - fn push_input(&mut self, f: impl FnOnce(&dyn Compiler) -> Result + Send + 'a) { + fn push_input( + &mut self, + f: impl FnOnce(&dyn Compiler) -> Result> + Send + 'a, + ) { self.inputs.push(Box::new(f)); } @@ -439,6 +485,8 @@ impl<'a> CompileInputs<'a> { .into(), symbol, start_srcloc: FilePos::default(), + translation: None, + func_body: None, }) }); } @@ -461,6 +509,8 @@ impl<'a> CompileInputs<'a> { function: CompiledFunction::Function(function), symbol, start_srcloc: FilePos::default(), + translation: None, + func_body: None, }) }); } @@ -511,7 +561,7 @@ impl<'a> CompileInputs<'a> { >, ) { for (module, translation, functions) in translations { - for (def_func_index, func_body) in functions { + for (def_func_index, func_body_data) in functions { self.push_input(move |compiler| { let func_index = translation.module.func_index(def_func_index); let symbol = match translation @@ -532,11 +582,18 @@ impl<'a> CompileInputs<'a> { func_index.as_u32() ), }; - let data = func_body.body.get_binary_reader(); + let func_body = func_body_data.body.clone(); + let data = func_body.get_binary_reader(); let offset = data.original_position(); let start_srcloc = FilePos::new(u32::try_from(offset).unwrap()); let function = compiler - .compile_function(translation, def_func_index, func_body, types, &symbol) + .compile_function( + translation, + def_func_index, + func_body_data, + types, + &symbol, + ) .with_context(|| format!("failed to compile: {symbol}"))?; Ok(CompileOutput { @@ -544,6 +601,8 @@ impl<'a> CompileInputs<'a> { symbol, function: CompiledFunction::Function(function), start_srcloc, + translation: Some(translation), + func_body: Some(func_body), }) }); @@ -569,6 +628,8 @@ impl<'a> CompileInputs<'a> { symbol, function: CompiledFunction::Function(trampoline), start_srcloc: FilePos::default(), + translation: None, + func_body: None, }) }); } @@ -595,6 +656,8 @@ impl<'a> CompileInputs<'a> { function: CompiledFunction::Function(trampoline), symbol, start_srcloc: FilePos::default(), + translation: None, + func_body: None, }) }); } @@ -602,7 +665,7 @@ impl<'a> CompileInputs<'a> { /// Compile these `CompileInput`s (maybe in parallel) and return the /// resulting `UnlinkedCompileOutput`s. - fn compile(self, engine: &Engine) -> Result { + fn compile(self, engine: &Engine) -> Result> { let compiler = engine.compiler(); if self.inputs.len() > 0 && cfg!(miri) { @@ -623,8 +686,43 @@ the use case. ); } - // Compile each individual input in parallel. - let mut raw_outputs = engine.run_maybe_parallel(self.inputs, |f| f(compiler))?; + let mut raw_outputs = if let Some(inlining_compiler) = compiler.inlining_compiler() { + if engine.tunables().inlining { + self.compile_with_inlining(engine, compiler, inlining_compiler)? + } else { + // Inlining compiler but inlining is disabled: compile each + // input and immediately finish its output in parallel, skipping + // call graph computation and all that. + engine.run_maybe_parallel::<_, _, Error, _>(self.inputs, |f| { + let mut compiled = f(compiler)?; + match &mut compiled.function { + CompiledFunction::Function(f) => inlining_compiler.finish_compiling( + f, + compiled.func_body.take(), + &compiled.symbol, + )?, + #[cfg(feature = "component-model")] + CompiledFunction::AllCallFunc(f) => { + debug_assert!(compiled.func_body.is_none()); + inlining_compiler.finish_compiling( + &mut f.array_call, + None, + &compiled.symbol, + )?; + inlining_compiler.finish_compiling( + &mut f.wasm_call, + None, + &compiled.symbol, + )?; + } + }; + Ok(compiled) + })? + } + } else { + // No inlining: just compile each individual input in parallel. + engine.run_maybe_parallel(self.inputs, |f| f(compiler))? + }; // Now that all functions have been compiled see if any // wasmtime-builtin functions are necessary. If so those need to be @@ -640,6 +738,328 @@ the use case. Ok(UnlinkedCompileOutputs { outputs }) } + + fn compile_with_inlining( + self, + engine: &Engine, + compiler: &dyn Compiler, + inlining_compiler: &dyn InliningCompiler, + ) -> Result>, Error> { + /// The index of a function (of any kind: Wasm function, trampoline, or + /// etc...) in our list of unlinked outputs. + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] + struct OutputIndex(u32); + wasmtime_environ::entity_impl!(OutputIndex); + + // Our list of unlinked outputs. + let mut outputs = PrimaryMap::>>::from( + engine.run_maybe_parallel(self.inputs, |f| f(compiler).map(Some))?, + ); + + /// Get just the output indices of the Wasm functions from our unlinked + /// outputs. + fn wasm_functions<'a>( + outputs: &'a PrimaryMap>>, + ) -> impl Iterator + 'a { + outputs.iter().filter_map(|(i, o)| { + if o.as_ref()?.key.kind() == CompileKind::WasmFunction { + Some(i) + } else { + None + } + }) + } + + // A map from a Wasm function's (module, defined-function-index) pair to + // its index in our unlinked outputs. + // + // We will generally just be working with `OutputIndex`es, but + // occassionally we must translate from these pairs back to our index + // space, for example when we know that one module's function import is + // always satisfied with a particular function defined in a particular + // module. This map enables that translation. + let pair_to_output: HashMap<(StaticModuleIndex, DefinedFuncIndex), OutputIndex> = outputs + .iter() + .filter(|(_, output)| output.as_ref().unwrap().key.kind() == CompileKind::WasmFunction) + .map(|(output_index, output)| { + let output = output.as_ref().unwrap(); + let module_index = output.key.module(); + let defined_func_index = output.key.defined_func_index(); + ((module_index, defined_func_index), output_index) + }) + .collect(); + + // Construct the call graph for inlining. + // + // We only inline Wasm functions, not trampolines, because we rely on + // trampolines being in their own stack frame when we save the entry and + // exit SP, FP, and PC for backtraces in trampolines. + let call_graph = CallGraph::::new(wasm_functions(&outputs), { + let mut func_indices = IndexSet::default(); + let outputs = &outputs; + let pair_to_output = &pair_to_output; + move |output_index, calls| { + debug_assert!(calls.is_empty()); + + let output = outputs[output_index].as_ref().unwrap(); + debug_assert_eq!(output.key.kind(), CompileKind::WasmFunction); + + let func = match &output.function { + CompiledFunction::Function(f) => f, + #[cfg(feature = "component-model")] + CompiledFunction::AllCallFunc(_) => { + unreachable!("wasm functions are not all-call functions") + } + }; + + // Get this function's call graph edges as `FuncIndex`es. + func_indices.clear(); + inlining_compiler.calls(func, &mut func_indices)?; + + // Translate each of those to (module, defined-function-index) + // pairs and then finally to output indices, which is what we + // actually need. + let caller_module = output.key.module(); + let translation = output + .translation + .expect("all wasm functions have translations"); + calls.extend(func_indices.iter().copied().filter_map(|callee_func| { + if let Some(callee_def_func) = + translation.module.defined_func_index(callee_func) + { + // Call to a function in the same module. + Some(pair_to_output[&(caller_module, callee_def_func)]) + } else if let Some(pair) = translation.known_imported_functions[callee_func] { + // Call to a statically-known imported function. + Some(pair_to_output[&pair]) + } else { + // Call to an unknown imported function or perhaps to + // multiple different functions in different + // instantiations. Can't inline these calls, so don't + // add them to the call graph. + None + } + })); + Ok(()) + } + })?; + + // Stratify the call graph into a sequence of layers. We process each + // layer in order, but process functions within a layer in parallel + // (because they either do not call each other or are part of a + // mutual-recursion cycle; either way we won't inline members of the + // same layer into each other). + let strata = stratify::Strata::::new(wasm_functions(&outputs), &call_graph); + let mut layer_outputs = vec![]; + for layer in strata.layers() { + // Temporarily take this layer's outputs out of our unlinked outputs + // list so that we can mutate these outputs (by inlining callee + // functions into them) while also accessing shared borrows of the + // unlinked outputs list (finding the callee functions we will + // inline). + debug_assert!(layer_outputs.is_empty()); + layer_outputs.extend(layer.iter().map(|f| outputs[*f].take().unwrap())); + + // Process this layer's members in parallel. + engine.run_maybe_parallel_mut( + &mut layer_outputs, + |output: &mut CompileOutput<'_>| { + debug_assert_eq!(output.key.kind(), CompileKind::WasmFunction); + + let caller_translation = output.translation.unwrap(); + let caller_module = output.key.module(); + let caller_def_func = output.key.defined_func_index(); + let caller_needs_gc_heap = caller_translation.module.needs_gc_heap; + + let caller = output + .function + .as_function_mut() + .expect("wasm functions are not all-call functions"); + + let mut caller_size = inlining_compiler.size(caller); + + inlining_compiler.inline(caller, &mut |callee: FuncIndex| { + let (callee_module, callee_def_func, callee_needs_gc_heap) = + if let Some(def_func) = + caller_translation.module.defined_func_index(callee) + { + (caller_module, def_func, Some(caller_needs_gc_heap)) + } else { + let (def_module, def_func) = + caller_translation.known_imported_functions[callee].expect( + "a direct call to an imported function must have a \ + statically-known import", + ); + (def_module, def_func, None) + }; + + let callee_output_index: OutputIndex = + pair_to_output[&(callee_module, callee_def_func)]; + let callee_output = outputs[callee_output_index].as_ref()?; + let callee_needs_gc_heap = callee_needs_gc_heap.unwrap_or_else(|| { + callee_output.translation.unwrap().module.needs_gc_heap + }); + + debug_assert_eq!(callee_output.key.kind(), CompileKind::WasmFunction); + let callee = callee_output + .function + .as_function() + .expect("wasm functions are not all-call functions"); + + let callee_size = inlining_compiler.size(callee); + + if Self::should_inline(InlineHeuristicParams { + tunables: engine.tunables(), + caller_size, + caller_module, + caller_def_func, + caller_needs_gc_heap, + callee_size, + callee_module, + callee_def_func, + callee_needs_gc_heap, + }) { + caller_size = caller_size.saturating_add(callee_size); + Some(callee) + } else { + None + } + }) + }, + )?; + + for (f, func) in layer.iter().zip(layer_outputs.drain(..)) { + debug_assert!(outputs[*f].is_none()); + outputs[*f] = Some(func); + } + } + + // Fan out in parallel again and finish compiling each function. + engine.run_maybe_parallel(outputs.into(), |output| { + let mut output = output.unwrap(); + match &mut output.function { + CompiledFunction::Function(f) => inlining_compiler.finish_compiling( + f, + output.func_body.take(), + &output.symbol, + )?, + #[cfg(feature = "component-model")] + CompiledFunction::AllCallFunc(f) => { + debug_assert!(output.func_body.is_none()); + inlining_compiler.finish_compiling(&mut f.array_call, None, &output.symbol)?; + inlining_compiler.finish_compiling(&mut f.wasm_call, None, &output.symbol)?; + } + }; + Ok(output) + }) + } + + /// Implementation of our inlining heuristics. + /// + /// TODO: We should improve our heuristics: + /// + /// * One potentially promising hint that we don't currently make use of is + /// how many times a function appears as the callee in call sites. For + /// example, a function that appears in only a single call site, and does + /// not otherwise escape, is often beneficial to inline regardless of its + /// size (assuming we can then GC away the non-inlined version of the + /// function, which we do not currently attempt to do). + /// + /// * Another potentially promising hint would be whether any of the call + /// site's actual arguments are constants. + /// + /// * A general improvement would be removing the decision-tree style of + /// control flow below and replacing it with (1) a pure estimated-benefit + /// formula and (2) a benefit threshold. Whenever the estimated benefit + /// reaches the threshold, we would inline the call. Both the formula and + /// the threshold would be parameterized by tunables. This would + /// effectively allow reprioritizing the relative importance of different + /// hint sources, rather than being stuck with the sequence hard-coded in + /// the decision tree below. + fn should_inline( + InlineHeuristicParams { + tunables, + caller_size, + caller_module, + caller_def_func, + caller_needs_gc_heap, + callee_size, + callee_module, + callee_def_func, + callee_needs_gc_heap, + }: InlineHeuristicParams, + ) -> bool { + log::trace!( + "considering inlining:\n\ + \tcaller = ({caller_module:?}, {caller_def_func:?})\n\ + \t\tsize = {caller_size}\n\ + \t\tneeds_gc_heap = {caller_needs_gc_heap}\n\ + \tcallee = ({callee_module:?}, {callee_def_func:?})\n\ + \t\tsize = {callee_size}\n\ + \t\tneeds_gc_heap = {callee_needs_gc_heap}" + ); + + debug_assert!( + tunables.inlining, + "shouldn't even call this method if we aren't configured for inlining" + ); + debug_assert!( + caller_module != callee_module || caller_def_func != callee_def_func, + "we never inline recursion" + ); + + // Consider whether this is an intra-module call. + // + // Inlining within a single core module has most often already been done + // by the toolchain that produced the module, e.g. LLVM, and any extant + // function calls to small callees were presumably annotated with the + // equivalent of `#[inline(never)]` or `#[cold]` but we don't have that + // information anymore. + if caller_module == callee_module { + match tunables.inlining_intra_module { + IntraModuleInlining::Yes => {} + + IntraModuleInlining::WhenUsingGc + if caller_needs_gc_heap || callee_needs_gc_heap => {} + + IntraModuleInlining::WhenUsingGc => { + log::trace!(" --> not inlining: intra-module call that does not use GC"); + return false; + } + + IntraModuleInlining::No => { + log::trace!(" --> not inlining: intra-module call"); + return false; + } + } + } + + // Small callees are often worth inlining regardless of the size of the + // caller. + if callee_size <= tunables.inlining_small_callee_size { + log::trace!( + " --> inlining: callee's size is less than the small-callee size: \ + {callee_size} <= {}", + tunables.inlining_small_callee_size + ); + return true; + } + + // It is often not worth inlining if the sum of the caller and callee + // sizes is too large. + let sum_size = caller_size.saturating_add(callee_size); + if sum_size > tunables.inlining_sum_size_threshold { + log::trace!( + " --> not inlining: the sum of the caller's and callee's sizes is greater than \ + the inlining-sum-size threshold: {callee_size} + {caller_size} > {}", + tunables.inlining_sum_size_threshold + ); + return false; + } + + log::trace!(" --> inlining: did not find a reason we should not"); + true + } } fn compile_required_builtins(engine: &Engine, raw_outputs: &mut Vec) -> Result<()> { @@ -650,14 +1070,19 @@ fn compile_required_builtins(engine: &Engine, raw_outputs: &mut Vec { // A map from kind to `CompileOutput`. - outputs: BTreeMap>, + outputs: BTreeMap>>, } -impl UnlinkedCompileOutputs { +impl UnlinkedCompileOutputs<'_> { /// Flatten all our functions into a single list and remember each of their /// indices within it. fn pre_link(self) -> PreLinkOutput { @@ -762,7 +1187,7 @@ struct PreLinkOutput { needs_gc_heap: bool, /// The flattened list of (symbol name, compiled function) pairs, as they /// will be laid out in the object file. - compiled_funcs: Vec<(String, Box)>, + compiled_funcs: Vec<(String, Box)>, /// The `FunctionIndices` mapping our function keys to indices in that flat /// list. indices: FunctionIndices, @@ -789,7 +1214,7 @@ impl FunctionIndices { types: &ModuleTypesBuilder, mut obj: object::write::Object<'static>, engine: &'a Engine, - compiled_funcs: Vec<(String, Box)>, + compiled_funcs: Vec<(String, Box)>, translations: PrimaryMap>, dwarf_package_bytes: Option<&[u8]>, ) -> Result<(wasmtime_environ::ObjectBuilder<'a>, Artifacts)> { @@ -1017,3 +1442,17 @@ impl Artifacts { self.modules.into_iter().next().unwrap().1 } } + +/// Extend `dest` with `items` and return the range of indices in `dest` where +/// they ended up. +fn extend_with_range(dest: &mut Vec, items: impl IntoIterator) -> Range { + let start = dest.len(); + let start = u32::try_from(start).unwrap(); + + dest.extend(items); + + let end = dest.len(); + let end = u32::try_from(end).unwrap(); + + start..end +} diff --git a/crates/wasmtime/src/compile/call_graph.rs b/crates/wasmtime/src/compile/call_graph.rs new file mode 100644 index 0000000000..4ce62245d4 --- /dev/null +++ b/crates/wasmtime/src/compile/call_graph.rs @@ -0,0 +1,105 @@ +//! Construction of the call-graph, for the purposes of inlining. +//! +//! These call graphs are not necessarily complete or accurate, and Wasmtime's +//! soundness does not rely on those properties. First off, we do not attempt to +//! understand indirect calls, which at their worst must force any call analysis +//! give up and say "the callee could be absolutely any function". More +//! interestingly, these call graphs are only used for scheduling bottom-up +//! inlining, so the worst that inaccurate information can do is cause us to +//! miss inlining opportunities or lose potential parallelism in our +//! schedule. For best results, however, every direct call that is potentially +//! inlinable should be reported when constructing these call graphs. + +use super::*; +use core::{ + fmt::{self, Debug}, + ops::Range, +}; +use wasmtime_environ::{EntityRef, SecondaryMap}; + +/// A call graph reified into a densely packed and quickly accessible +/// representation. +/// +/// In a call graph, nodes are functions, and an edge `f --> g` means that the +/// function `f` calls the function `g`. +pub struct CallGraph +where + Node: EntityRef + Debug, +{ + /// A map from each node to the subslice of `self.edge_elems` that are its + /// edges. + edges: SecondaryMap>, + + /// Densely packed edge elements for `self.edges`. + edge_elems: Vec, +} + +impl Debug for CallGraph +where + Node: EntityRef + Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct Edges<'a, Node: EntityRef + Debug>(&'a CallGraph); + + impl<'a, Node: EntityRef + Debug> Debug for Edges<'a, Node> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map() + .entries(self.0.nodes().map(|n| (n, self.0.edges(n)))) + .finish() + } + } + + f.debug_struct("CallGraph") + .field("edges", &Edges(self)) + .finish() + } +} + +impl CallGraph +where + Node: EntityRef + Debug, +{ + /// Construct a new call graph. + /// + /// `funcs` should be an iterator over all function nodes in this call + /// graph's translation unit. + /// + /// The `get_calls` function should yield (by pushing onto the given `Vec`) + /// all of the callee function nodes that the given caller function node + /// calls. + pub fn new( + funcs: impl IntoIterator, + mut get_calls: impl FnMut(Node, &mut Vec) -> Result<()>, + ) -> Result { + let funcs = funcs.into_iter(); + + let (min, max) = funcs.size_hint(); + let capacity = max.unwrap_or_else(|| 2 * min); + let mut edges = SecondaryMap::with_capacity(capacity); + let mut edge_elems = vec![]; + + let mut calls = vec![]; + for caller in funcs { + debug_assert!(calls.is_empty()); + get_calls(caller, &mut calls)?; + + debug_assert_eq!(edges[caller], Range::default()); + edges[caller] = extend_with_range(&mut edge_elems, calls.drain(..)); + } + + Ok(CallGraph { edges, edge_elems }) + } + + /// Get the function nodes in this call graph. + pub fn nodes(&self) -> impl ExactSizeIterator { + self.edges.keys() + } + + /// Get the callee function nodes that the given caller function node calls. + pub fn edges(&self, node: Node) -> &[Node] { + let Range { start, end } = self.edges[node].clone(); + let start = usize::try_from(start).unwrap(); + let end = usize::try_from(end).unwrap(); + &self.edge_elems[start..end] + } +} diff --git a/crates/wasmtime/src/compile/scc.rs b/crates/wasmtime/src/compile/scc.rs index 5651010229..025f2e3f53 100644 --- a/crates/wasmtime/src/compile/scc.rs +++ b/crates/wasmtime/src/compile/scc.rs @@ -11,16 +11,21 @@ //! //! [Tarjan's algorithm]: https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm -#![expect(dead_code, reason = "used in upcoming PRs")] - -use crate::prelude::*; -use std::ops::Range; -use wasmtime_environ::{EntityRef, EntitySet, PrimaryMap, SecondaryMap}; +use super::*; +use std::{ + collections::BTreeSet, + fmt::{self, Debug}, + hash::Hash, + ops::Range, +}; +use wasmtime_environ::{ + EntityRef, EntitySet, PrimaryMap, SecondaryMap, packed_option::PackedOption, +}; /// A strongly-connected component. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Component(u32); -wasmtime_environ::entity_impl!(Component); +pub struct Scc(u32); +wasmtime_environ::entity_impl!(Scc); /// The set of strongly-connected components for a graph of `Node`s. pub struct StronglyConnectedComponents @@ -29,12 +34,34 @@ where { /// A map from a component to the range of `self.component_nodes` that make /// up that component's nodes. - components: PrimaryMap>, + components: PrimaryMap>, /// The data storage for the values of `self.components`. component_nodes: Vec, } +impl Debug for StronglyConnectedComponents +where + Node: EntityRef + Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct Map<'a, Node: EntityRef + Debug>(&'a StronglyConnectedComponents); + + impl<'a, Node> Debug for Map<'a, Node> + where + Node: EntityRef + Debug, + { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> fmt::Result { + f.debug_map().entries(self.0.iter()).finish() + } + } + + f.debug_struct("StronglyConnectedComponents") + .field("components", &Map(self)) + .finish() + } +} + impl StronglyConnectedComponents where Node: EntityRef + std::fmt::Debug, @@ -50,7 +77,7 @@ where // The resulting components and their nodes. let mut component_nodes = vec![]; - let mut components = PrimaryMap::>::new(); + let mut components = PrimaryMap::>::new(); // The DFS index counter. let mut index = NonMaxU32::default(); @@ -123,21 +150,22 @@ where // from the SCC stack and push them into our result data // structures. if indices[node] == lowlinks[node] { - let start = component_nodes.len(); - let start = u32::try_from(start).unwrap(); - loop { - let v = stack.pop().unwrap(); - let was_on_stack = on_stack.remove(v); - debug_assert!(was_on_stack); - component_nodes.push(v); - if v == node { - break; - } - } - let end = component_nodes.len(); - let end = u32::try_from(end).unwrap(); - debug_assert!(end > start); - components.push(start..end); + let mut done = false; + components.push(extend_with_range( + &mut component_nodes, + std::iter::from_fn(|| { + if done { + return None; + } + let v = stack.pop().unwrap(); + let was_on_stack = on_stack.remove(v); + debug_assert!(was_on_stack); + if v == node { + done = true; + } + Some(v) + }), + )); } } } @@ -165,7 +193,7 @@ where /// /// Iteration happens in reverse-topological order (successors are visited /// before predecessors in the resulting SCC DAG). - pub fn iter(&self) -> impl ExactSizeIterator + '_ { + pub fn iter(&self) -> impl ExactSizeIterator + '_ { self.components .iter() .map(|(component, range)| (component, self.node_range(range.clone()))) @@ -175,7 +203,7 @@ where /// /// Iteration happens in reverse-topological order (successors are visited /// before predecessors in the resulting SCC DAG). - pub fn keys(&self) -> impl ExactSizeIterator { + pub fn keys(&self) -> impl ExactSizeIterator { self.components.keys() } @@ -183,6 +211,7 @@ where /// /// Iteration happens in reverse-topological order (successors are visited /// before predecessors in the resulting SCC DAG). + #[cfg(test)] pub fn values(&self) -> impl ExactSizeIterator + '_ { self.components .values() @@ -190,10 +219,193 @@ where } /// Get the `Node`s that make up the given strongly-connected component. - pub fn nodes(&self, component: Component) -> &[Node] { + pub fn nodes(&self, component: Scc) -> &[Node] { let range = self.components[component].clone(); self.node_range(range) } + + /// Get this set of strongly-connected components' "evaporation". + /// + /// Given an input graph `G`, we can construct a new graph where the new + /// graph's nodes are the strongly-connected components of `G` and there is + /// an edge `scc(a) --> scc(b)` for every edge `a --> b` in the input graph + /// `G` where `scc(a) != scc(b)` and `scc` is the function from a node to + /// its strongly-connected component. This new graph is known as the + /// "condensation" of `G`. + /// + /// In the following diagram, the solid lines are the input graph and the + /// dotted lines show its condensation: + /// + /// ```ignore + /// ............... + /// : : + /// : ,-----. : + /// : | | : + /// : V | : + /// : +---+ | : + /// : | a |---' : + /// : +---+ : + /// : | : + /// :....|........: + /// | : + /// | : + /// | : + /// | V + /// .....|.............. .................... + /// : | : : : + /// : V :......>: : + /// : +---+ +---+ : : +---+ +---+ : + /// : | b |<-->| c |------------>| d |<-->| e | : + /// : +---+ +---+ : : +---+ +---+ : + /// : | : : | : + /// :....|.............: :.............|....: + /// | : : | + /// | : : | + /// | : : | + /// | V : | + /// .....|........................ : | + /// : | : : | + /// : | ,----------------. : : | + /// : | | | :<....: | + /// : V | V : | + /// : +---+ +---+ +---+ : | + /// : | f |<---| g |<---| h |<-------------' + /// : +---+ +---+ +---+ : + /// : : + /// :............................: + /// ``` + /// + /// Note that a graph's condensation is always acyclic, because the + /// strongly-conneted components encapsulate and hide any cycles from the + /// input graph. + /// + /// I am not aware of an existing name for the reverse (or transpose) + /// condensation, where each of the condensation's edges `a --> b` are + /// flipped into `b --> a`, so, at cfallin's brilliant suggestion, I have + /// decided to call it an "evaporation". + /// + /// In the context of a call graph, the condensation allows us to derive a + /// partial dependency ordering for bottom-up inlining: + /// + /// * An edge `scc({a,b,...}) --> scc({c,d,...})` means that the functions + /// `{c,d,...}` should be processed for inlining before functions + /// `{a,b,...}`, since some functions in `{a,b,...}` call some functions + /// in `{c,d,...}` and we might want to inline these calls but only after + /// `{c,d,...}` have already had their calls inlined. + /// + /// * Functions within an SCC are unordered (and we probably don't want to + /// inline between them at all, or only want to do so in a very limited + /// manner, since their members are mutually recursive). + /// + /// A call graph's evaporation, by flipping edges from pointing to an SCC's + /// dependencies to pointing to its dependers, allows us to answer the + /// question "given that I've finished processing calls in `scc({e,f,...})` + /// for inlining, which other SCCs are now potentially ready for + /// processing?". + pub fn evaporation(&self, successors: F) -> Evaporation + where + F: Fn(Node) -> S, + S: Iterator, + { + log::trace!("Creating the evaporation of {self:#?}"); + + let node_to_component: SecondaryMap> = self + .iter() + .flat_map(|(c, nodes)| { + nodes + .iter() + .copied() + .map(move |node| (node, PackedOption::from(Some(c)))) + }) + .collect(); + + // Create a set of all reverse dependencies. This set contains an entry + // `(to, from)` for all `from --> to` edges in the SCC's condensation. + // + // Note that we use a `BTreeSet` so that the results are ordered, all + // edges `a --> *` are contiguous for each component `a`, and we can + // therefore pack them densely in the resulting `Evaporation`. + let mut reverse_edge_set = BTreeSet::<(Scc, Scc)>::new(); + for (from_component, nodes) in self.iter() { + for node in nodes { + for succ in successors(*node) { + let to_component = node_to_component[succ].unwrap(); + if to_component == from_component { + continue; + } + reverse_edge_set.insert((to_component, from_component)); + } + } + } + + // Convert the `reverse_edge_set` into our densely-packed + // representation. + let mut reverse_edges = + SecondaryMap::>::with_capacity(self.components.len()); + let mut reverse_edge_elems = Vec::with_capacity(reverse_edge_set.len()); + for (to_node, from_node) in reverse_edge_set { + let range = extend_with_range(&mut reverse_edge_elems, Some(from_node)); + + if reverse_edges[to_node] == Range::default() { + reverse_edges[to_node] = range; + } else { + debug_assert_eq!(reverse_edges[to_node].end, range.start); + reverse_edges[to_node].end = range.end; + } + } + + let result = Evaporation { + reverse_edges, + reverse_edge_elems, + }; + log::trace!(" -> {result:#?}"); + result + } +} + +/// The "evaporation" of a graph and its strongly-connected components. +/// +/// Created by `StronglyConnectedComponents::evaporation`; see that method's +/// documentation for more details. +pub struct Evaporation { + reverse_edges: SecondaryMap>, + reverse_edge_elems: Vec, +} + +impl Debug for Evaporation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct Map<'a>(&'a Evaporation); + + impl<'a> Debug for Map<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_map() + .entries( + self.0 + .reverse_edges + .keys() + .map(|k| (k, self.0.reverse_edges(k))), + ) + .finish() + } + } + + f.debug_struct("Evaporation") + .field("reverse_edges", &Map(self)) + .finish() + } +} + +impl Evaporation { + /// Get the reverse dependencies of the given strongly-connected component. + /// + /// The result is all of the SCCs whose members have edges in the original + /// graph to one or more of this SCC's members. + pub fn reverse_edges(&self, component: Scc) -> &[Scc] { + let Range { start, end } = self.reverse_edges[component].clone(); + let start = usize::try_from(start).unwrap(); + let end = usize::try_from(end).unwrap(); + &self.reverse_edge_elems[start..end] + } } /// An iterative depth-first traversal. diff --git a/crates/wasmtime/src/compile/stratify.rs b/crates/wasmtime/src/compile/stratify.rs new file mode 100644 index 0000000000..f67469209b --- /dev/null +++ b/crates/wasmtime/src/compile/stratify.rs @@ -0,0 +1,400 @@ +//! Stratification of call graphs for parallel bottom-up inlining. +//! +//! This module takes a call graph and constructs a strata, which is essentially +//! a parallel execution plan. A strata consists of an ordered sequence of +//! layers, and a layer of an unordered set of functions. The `i`th layer must +//! be processed before the `i + 1`th layer, but functions within the same layer +//! may be processed in any order (and in parallel). +//! +//! For example, when given the following tree-like call graph: +//! +//! ```ignore +//! +---+ +---+ +---+ +//! | a |-->| b |-->| c | +//! +---+ +---+ +---+ +//! | | +//! | | +---+ +//! | '---->| d | +//! | +---+ +//! | +//! | +---+ +---+ +//! '---->| e |-->| f | +//! +---+ +---+ +//! | +//! | +---+ +//! '---->| g | +//! +---+ +//! ``` +//! +//! then stratification will produce these layers: +//! +//! ```ignore +//! [ +//! {c, d, f, g}, +//! {b, e}, +//! {a}, +//! ] +//! ``` +//! +//! Our goal in constructing the layers is to maximize potential parallelism at +//! each layer. Logically, we do this by finding the strongly-connected +//! components of the input call graph and peeling off all of the leaves of +//! SCCs' condensation (i.e. the DAG that the SCCs form; see the documentation +//! for the `StronglyConnectedComponents::evaporation` method for +//! details). These leaves become the strata's first layer. The layer's +//! components are removed from the condensation graph, and we repeat the +//! process, so that the condensation's new leaves become the strata's second +//! layer, and etc... until the condensation graph is empty and all components +//! have been processed. In practice we don't actually mutate the condensation +//! graph or remove its nodes but instead count how many unprocessed +//! dependencies each component has, and a component is ready for inclusion in a +//! layer once its unprocessed-dependencies count reaches zero. + +use super::{ + call_graph::CallGraph, + scc::{Scc, StronglyConnectedComponents}, + *, +}; +use std::{fmt::Debug, ops::Range}; +use wasmtime_environ::{EntityRef, SecondaryMap}; + +/// A stratified call graph; essentially a parallel-execution plan for bottom-up +/// inlining. +/// +/// See the module doc comment for more details. +pub struct Strata { + layers: Vec>, + layer_elems: Vec, +} + +impl Debug for Strata { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + struct Layers<'a, Node>(&'a Strata); + + impl<'a, Node: Debug> Debug for Layers<'a, Node> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut f = f.debug_list(); + for layer in self.0.layers() { + f.entry(&layer); + } + f.finish() + } + } + + f.debug_struct("Strata") + .field("layers", &Layers(self)) + .finish() + } +} + +impl Strata { + /// Stratify the given call graph, yielding a `Strata` parallel-execution + /// plan. + pub fn new(nodes: impl IntoIterator, call_graph: &CallGraph) -> Self + where + Node: EntityRef + Debug, + { + log::trace!("Stratifying {call_graph:#?}"); + + let components = + StronglyConnectedComponents::new(nodes, |node| call_graph.edges(node).iter().copied()); + let evaporation = components.evaporation(|node| call_graph.edges(node).iter().copied()); + + // A map from each component to the count of how many call-graph + // dependencies to other components it has that have not been fulfilled + // yet. These counts are decremented as we assign a component's dependencies + // to layers. + let mut unfulfilled_deps_count = SecondaryMap::::with_capacity(components.len()); + for to_component in components.keys() { + for from_component in evaporation.reverse_edges(to_component) { + unfulfilled_deps_count[*from_component] += 1; + } + } + + // Build the strata. + // + // The first layer is formed by searching through all components for those + // that have a zero unfulfilled-deps count. When we finish a layer, we + // iterate over each of component in that layer and decrement the + // unfulfilled-deps count of every other component that depends on the + // newly-assigned-to-a-layer component. Any component that then reaches a + // zero unfulfilled-dep count is added to the next layer. This proceeds to a + // fixed point, similarly to GC tracing and ref-count decrementing. + + let mut layers: Vec> = vec![]; + let mut layer_elems: Vec = Vec::with_capacity(call_graph.nodes().len()); + + let mut current_layer: Vec = components + .keys() + .filter(|scc| unfulfilled_deps_count[*scc] == 0) + .collect(); + debug_assert!( + !current_layer.is_empty() || call_graph.nodes().len() == 0, + "the first layer can only be empty when the call graph itself is empty" + ); + + let mut next_layer = vec![]; + + while !current_layer.is_empty() { + debug_assert!(next_layer.is_empty()); + + for dependee in ¤t_layer { + for depender in evaporation.reverse_edges(*dependee) { + debug_assert!(unfulfilled_deps_count[*depender] > 0); + unfulfilled_deps_count[*depender] -= 1; + if unfulfilled_deps_count[*depender] == 0 { + next_layer.push(*depender); + } + } + } + + layers.push(extend_with_range( + &mut layer_elems, + current_layer + .drain(..) + .flat_map(|scc| components.nodes(scc).iter().copied()), + )); + + std::mem::swap(&mut next_layer, &mut current_layer); + } + + debug_assert!( + unfulfilled_deps_count.values().all(|c| *c == 0), + "after every component is assigned to a layer, all dependencies should be fulfilled" + ); + + let result = Strata { + layers, + layer_elems, + }; + log::trace!(" -> {result:#?}"); + result + } + + /// Iterate over the layers of this `Strata`. + /// + /// The `i`th layer must be processed before the `i + 1`th layer, but the + /// functions within a layer may be processed in any order and in parallel. + pub fn layers(&self) -> impl ExactSizeIterator { + self.layers.iter().map(|range| { + let start = usize::try_from(range.start).unwrap(); + let end = usize::try_from(range.end).unwrap(); + &self.layer_elems[start..end] + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + struct Function(u32); + wasmtime_environ::entity_impl!(Function); + + struct Functions { + calls: SecondaryMap>, + } + + impl Default for Functions { + fn default() -> Self { + let _ = env_logger::try_init(); + Self { + calls: Default::default(), + } + } + } + + impl Functions { + fn define_func(&mut self, f: u32) -> &mut Self { + let f = Function::from_u32(f); + if self.calls.get(f).is_none() { + self.calls[f] = vec![]; + } + self + } + + fn define_call(&mut self, caller: u32, callee: u32) -> &mut Self { + self.define_func(caller); + self.define_func(callee); + let caller = Function::from_u32(caller); + let callee = Function::from_u32(callee); + self.calls[caller].push(callee); + self + } + + fn define_calls( + &mut self, + caller: u32, + callees: impl IntoIterator, + ) -> &mut Self { + for callee in callees { + self.define_call(caller, callee); + } + self + } + + fn stratify(&self) -> Strata { + let call_graph = CallGraph::new(self.calls.keys(), |f, calls| { + calls.extend_from_slice(&self.calls[f]); + Ok(()) + }) + .unwrap(); + Strata::::new(self.calls.keys(), &call_graph) + } + + fn assert_stratification(&self, mut expected: Vec>) { + for layer in &mut expected { + layer.sort(); + } + log::trace!("expected stratification = {expected:?}"); + + let actual = self + .stratify() + .layers() + .map(|layer| { + let mut layer = layer.iter().map(|f| f.as_u32()).collect::>(); + layer.sort(); + layer + }) + .collect::>(); + log::trace!("actual stratification = {actual:?}"); + + assert_eq!(expected.len(), actual.iter().len()); + for (expected, actual) in expected.into_iter().zip(actual) { + log::trace!("expected layer = {expected:?}"); + log::trace!(" actual layer = {expected:?}"); + + assert_eq!(expected.len(), actual.len()); + for (expected, actual) in expected.into_iter().zip(actual) { + assert_eq!(expected, actual); + } + } + } + } + + #[test] + fn test_disconnected_functions() { + // +---+ +---+ +---+ + // | 0 | | 1 | | 2 | + // +---+ +---+ +---+ + Functions::default() + .define_func(0) + .define_func(1) + .define_func(2) + .assert_stratification(vec![vec![0, 1, 2]]); + } + + #[test] + fn test_chained_functions() { + // +---+ +---+ +---+ + // | 0 |-->| 1 |-->| 2 | + // +---+ +---+ +---+ + Functions::default() + .define_call(0, 1) + .define_call(1, 2) + .assert_stratification(vec![vec![2], vec![1], vec![0]]); + } + + #[test] + fn test_cycle() { + // ,---------------. + // V | + // +---+ +---+ +---+ + // | 0 |-->| 1 |-->| 2 | + // +---+ +---+ +---+ + Functions::default() + .define_call(0, 1) + .define_call(1, 2) + .define_call(2, 0) + .assert_stratification(vec![vec![0, 1, 2]]); + } + + #[test] + fn test_tree() { + // +---+ +---+ +---+ + // | 0 |-->| 1 |-->| 2 | + // +---+ +---+ +---+ + // | | + // | | +---+ + // | '---->| 3 | + // | +---+ + // | + // | +---+ +---+ + // '---->| 4 |-->| 5 | + // +---+ +---+ + // | + // | +---+ + // '---->| 6 | + // +---+ + Functions::default() + .define_calls(0, [1, 4]) + .define_calls(1, [2, 3]) + .define_calls(4, [5, 6]) + .assert_stratification(vec![vec![2, 3, 5, 6], vec![1, 4], vec![0]]); + } + + #[test] + fn test_chain_of_cycles() { + // ,-----. + // | | + // V | + // +---+ | + // | 0 |---' + // +---+ + // | + // V + // +---+ +---+ + // | 1 |<-->| 2 | + // +---+ +---+ + // | + // | ,----------------. + // | | | + // V | V + // +---+ +---+ +---+ + // | 3 |<---| 4 |<---| 5 | + // +---+ +---+ +---+ + Functions::default() + .define_calls(0, [0, 1]) + .define_calls(1, [2, 3]) + .define_calls(2, [1]) + .define_calls(3, [5]) + .define_calls(4, [3]) + .define_calls(5, [4]) + .assert_stratification(vec![vec![3, 4, 5], vec![1, 2], vec![0]]); + } + + #[test] + fn test_multiple_edges_to_same_component() { + // +---+ +---+ + // | 0 | | 1 | + // +---+ +---+ + // ^ ^ + // | | + // V V + // +---+ +---+ + // | 2 | | 3 | + // +---+ +---+ + // | | + // `------. ,------' + // | | + // V V + // +---+ + // | 4 | + // +---+ + // ^ + // | + // V + // +---+ + // | 5 | + // +---+ + Functions::default() + .define_calls(0, [2]) + .define_calls(1, [3]) + .define_calls(2, [0, 4]) + .define_calls(3, [1, 4]) + .define_calls(4, [5]) + .define_calls(5, [4]) + .assert_stratification(vec![vec![4, 5], vec![0, 1, 2, 3]]); + } +} diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index 8c33cc4816..068e7f8732 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -2027,6 +2027,25 @@ impl Config { self } + /// Whether to enable function inlining during compilation or not. + /// + /// This may result in faster execution at runtime, but adds additional + /// compilation time. Inlining may also enlarge the size of compiled + /// artifacts (for example, the size of the result of + /// [`Engine::precompile_component`]). + /// + /// Inlining is not supported by all of Wasmtime's compilation strategies; + /// currently, it only Cranelift supports it. This setting will be ignored + /// when using a compilation strategy that does not support inlining, like + /// Winch. + /// + /// Note that inlining is still somewhat experimental at the moment (as of + /// the Wasmtime version 36). + pub fn compiler_inlining(&mut self, inlining: bool) -> &mut Self { + self.tunables.inlining = Some(inlining); + self + } + /// Returns the set of features that the currently selected compiler backend /// does not support at all and may panic on. /// @@ -2497,6 +2516,7 @@ impl Config { } // Apply compiler settings and flags + compiler.set_tunables(tunables.clone())?; for (k, v) in self.compiler_config.settings.iter() { compiler.set(k, v)?; } @@ -2509,7 +2529,6 @@ impl Config { compiler.enable_incremental_compilation(cache_store.clone())?; } - compiler.set_tunables(tunables.clone())?; compiler.wmemcheck(self.compiler_config.wmemcheck); Ok((self, compiler.build()?)) @@ -2603,37 +2622,55 @@ impl Config { /// Configures Wasmtime to not use signals-based trap handlers, for example /// disables `SIGILL` and `SIGSEGV` handler registration on Unix platforms. /// + /// > **Note:** this option has important performance ramifications, be sure + /// > to understand the implications. Wasm programs have been measured to + /// > run up to 2x slower when signals-based traps are disabled. + /// /// Wasmtime will by default leverage signals-based trap handlers (or the /// platform equivalent, for example "vectored exception handlers" on - /// Windows) to make generated code more efficient. For example an - /// out-of-bounds load in WebAssembly will result in a `SIGSEGV` on Unix - /// that is caught by a signal handler in Wasmtime by default. Another - /// example is divide-by-zero is reported by hardware rather than - /// explicitly checked and Wasmtime turns that into a trap. - /// - /// Some environments however may not have easy access to signal handlers. - /// For example embedded scenarios may not support virtual memory. Other + /// Windows) to make generated code more efficient. For example, when + /// Wasmtime can use signals-based traps, it can elide explicit bounds + /// checks for Wasm linear memory accesses, instead relying on virtual + /// memory guard pages to raise a `SIGSEGV` (on Unix) for out-of-bounds + /// accesses, which Wasmtime's runtime then catches and handles. Another + /// example is divide-by-zero: with signals-based traps, Wasmtime can let + /// the hardware raise a trap when the divisor is zero. Without + /// signals-based traps, Wasmtime must explicitly emit additional + /// instructions to check for zero and conditionally branch to a trapping + /// code path. + /// + /// Some environments however may not have access to signal handlers. For + /// example embedded scenarios may not support virtual memory. Other /// environments where Wasmtime is embedded within the surrounding /// environment may require that new signal handlers aren't registered due /// to the global nature of signal handlers. This option exists to disable - /// the signal handler registration when required. + /// the signal handler registration when required for these scenarios. /// - /// When signals-based trap handlers are disabled then generated code will - /// never rely on segfaults or other signals. Generated code will be slower - /// because bounds checks must be explicit along with other operations like - /// integer division which must check for zero. + /// When signals-based trap handlers are disabled, then Wasmtime and its + /// generated code will *never* rely on segfaults or other + /// signals. Generated code will be slower because bounds must be explicitly + /// checked along with other conditions like division by zero. /// - /// When this option is disable it additionally requires that the + /// The following additional factors can also affect Wasmtime's ability to + /// elide explicit bounds checks and leverage signals-based traps: + /// + /// * The [`Config::memory_reservation`] and [`Config::memory_guard_size`] + /// settings + /// * The index type of the linear memory (e.g. 32-bit or 64-bit) + /// * The page size of the linear memory + /// + /// When this option is disabled, the /// `enable_heap_access_spectre_mitigation` and - /// `enable_table_access_spectre_mitigation` Cranelift settings are + /// `enable_table_access_spectre_mitigation` Cranelift settings must also be /// disabled. This means that generated code must have spectre mitigations /// disabled. This is because spectre mitigations rely on faults from /// loading from the null address to implement bounds checks. /// - /// This option defaults to `true` meaning that signals-based trap handlers - /// are enabled by default. + /// This option defaults to `true`: signals-based trap handlers are enabled + /// by default. /// - /// **Note** Disabling this option is not compatible with the Winch compiler. + /// > **Note:** Disabling this option is not compatible with the Winch + /// > compiler. pub fn signals_based_traps(&mut self, enable: bool) -> &mut Self { self.tunables.signals_based_traps = Some(enable); self diff --git a/crates/wasmtime/src/engine.rs b/crates/wasmtime/src/engine.rs index 1870e4b8be..5c49b99857 100644 --- a/crates/wasmtime/src/engine.rs +++ b/crates/wasmtime/src/engine.rs @@ -195,6 +195,40 @@ impl Engine { .collect::, E>>() } + #[cfg(any(feature = "cranelift", feature = "winch"))] + pub(crate) fn run_maybe_parallel_mut< + T: Send, + E: Send, + F: Fn(&mut T) -> Result<(), E> + Send + Sync, + >( + &self, + input: &mut [T], + f: F, + ) -> Result<(), E> { + if self.config().parallel_compilation { + #[cfg(feature = "parallel-compilation")] + { + use rayon::prelude::*; + // If we collect into `Result<(), E>` directly, the returned + // error is not deterministic, because any error could be + // returned early. So we first materialize all results in order + // and then return the first error deterministically, or + // `Ok(_)`. + return input + .into_par_iter() + .map(|a| f(a)) + .collect::>>() + .into_iter() + .collect::>(); + } + } + + // In case the parallel-compilation feature is disabled or the + // parallel_compilation config was turned off dynamically fallback to + // the non-parallel version. + input.into_iter().map(|a| f(a)).collect::>() + } + /// Take a weak reference to this engine. pub fn weak(&self) -> EngineWeak { EngineWeak { diff --git a/crates/wasmtime/src/engine/serialization.rs b/crates/wasmtime/src/engine/serialization.rs index 86dc67e139..feccd9f5dc 100644 --- a/crates/wasmtime/src/engine/serialization.rs +++ b/crates/wasmtime/src/engine/serialization.rs @@ -288,6 +288,11 @@ impl Metadata<'_> { winch_callable, signals_based_traps, memory_init_cow, + inlining, + inlining_intra_module, + inlining_small_callee_size, + inlining_sum_size_threshold, + // This doesn't affect compilation, it's just a runtime setting. memory_reservation_for_growth: _, @@ -355,6 +360,18 @@ impl Metadata<'_> { other.memory_init_cow, "memory initialization with CoW", )?; + Self::check_bool(inlining, other.inlining, "function inlining")?; + Self::check_int( + inlining_small_callee_size, + other.inlining_small_callee_size, + "function inlining small-callee size", + )?; + Self::check_int( + inlining_sum_size_threshold, + other.inlining_sum_size_threshold, + "function inlining sum-size threshold", + )?; + Self::check_intra_module_inlining(inlining_intra_module, other.inlining_intra_module)?; Ok(()) } @@ -441,6 +458,28 @@ impl Metadata<'_> { } } } + + fn check_intra_module_inlining( + module: wasmtime_environ::IntraModuleInlining, + host: wasmtime_environ::IntraModuleInlining, + ) -> Result<()> { + if module == host { + return Ok(()); + } + + let desc = |cfg| match cfg { + wasmtime_environ::IntraModuleInlining::No => "without intra-module inlining", + wasmtime_environ::IntraModuleInlining::Yes => "with intra-module inlining", + wasmtime_environ::IntraModuleInlining::WhenUsingGc => { + "with intra-module inlining only when using GC" + } + }; + + let module = desc(module); + let host = desc(host); + + bail!("module was compiled {module} however the host is configured {host}") + } } #[cfg(test)] diff --git a/crates/wasmtime/src/runtime.rs b/crates/wasmtime/src/runtime.rs index 585130e5e8..43c6eee333 100644 --- a/crates/wasmtime/src/runtime.rs +++ b/crates/wasmtime/src/runtime.rs @@ -25,6 +25,10 @@ // explanation of why truncation shouldn't be happening at runtime. This // situation should be pretty rare though. #![warn(clippy::cast_possible_truncation)] +#![warn( + unsafe_op_in_unsafe_fn, + reason = "opt-in until the crate opts-in as a whole -- #11180" +)] #[macro_use] pub(crate) mod func; diff --git a/crates/wasmtime/src/runtime/code_memory.rs b/crates/wasmtime/src/runtime/code_memory.rs index 7d4bbaae72..1ade0a672d 100644 --- a/crates/wasmtime/src/runtime/code_memory.rs +++ b/crates/wasmtime/src/runtime/code_memory.rs @@ -394,12 +394,14 @@ impl CodeMemory { { let text = self.text(); let unwind_info = &self.mmap[self.unwind.clone()]; - let registration = crate::runtime::vm::UnwindRegistration::new( - text.as_ptr(), - unwind_info.as_ptr(), - unwind_info.len(), - ) - .context("failed to create unwind info registration")?; + let registration = unsafe { + crate::runtime::vm::UnwindRegistration::new( + text.as_ptr(), + unwind_info.as_ptr(), + unwind_info.len(), + ) + .context("failed to create unwind info registration")? + }; self.unwind_registration = Some(registration); return Ok(()); } diff --git a/crates/wasmtime/src/runtime/component/bindgen_examples/_4_imported_resources.rs b/crates/wasmtime/src/runtime/component/bindgen_examples/_4_imported_resources.rs index 5540c19bab..b84579bc2f 100644 --- a/crates/wasmtime/src/runtime/component/bindgen_examples/_4_imported_resources.rs +++ b/crates/wasmtime/src/runtime/component/bindgen_examples/_4_imported_resources.rs @@ -33,7 +33,7 @@ bindgen!({ // Interactions with `ResourceTable` can possibly trap so enable the ability // to return traps from generated functions. - trappable_imports: true, + imports: { default: trappable }, }); /// A sample host-defined type which contains arbitrary host-defined data. diff --git a/crates/wasmtime/src/runtime/component/bindgen_examples/_7_async.rs b/crates/wasmtime/src/runtime/component/bindgen_examples/_7_async.rs index d9e798791f..e5cc07100d 100644 --- a/crates/wasmtime/src/runtime/component/bindgen_examples/_7_async.rs +++ b/crates/wasmtime/src/runtime/component/bindgen_examples/_7_async.rs @@ -25,17 +25,17 @@ bindgen!({ } "#, - async: true, // NEW + // NEW: Make all imports/exports `async` by default, and additionally + // interactions with `ResourceTable` can possibly trap so enable the ability + // to return traps from generated functions with `trappable`. + imports: { default: async | trappable }, + exports: { default: async }, with: { // Specify that our host resource is going to point to the `MyLogger` // which is defined just below this macro. "example:imported-resources/logging/logger": MyLogger, }, - - // Interactions with `ResourceTable` can possibly trap so enable the ability - // to return traps from generated functions. - trappable_imports: true, }); /// A sample host-defined type which contains arbitrary host-defined data. diff --git a/crates/wasmtime/src/runtime/component/component.rs b/crates/wasmtime/src/runtime/component/component.rs index ae1df341fd..00f7acf72d 100644 --- a/crates/wasmtime/src/runtime/component/component.rs +++ b/crates/wasmtime/src/runtime/component/component.rs @@ -17,8 +17,8 @@ use core::ptr::NonNull; use std::path::Path; use wasmtime_environ::TypeTrace; use wasmtime_environ::component::{ - AllCallFunc, CanonicalOptions, CompiledComponentInfo, ComponentArtifacts, ComponentTypes, - CoreDef, Export, ExportIndex, GlobalInitializer, InstantiateModule, NameMapNoIntern, + AllCallFunc, CompiledComponentInfo, ComponentArtifacts, ComponentTypes, CoreDef, Export, + ExportIndex, GlobalInitializer, InstantiateModule, NameMapNoIntern, OptionsIndex, StaticModuleIndex, TrampolineIndex, TypeComponentIndex, TypeFuncIndex, VMComponentOffsets, }; use wasmtime_environ::{FunctionLoc, HostPtr, ObjectKind, PrimaryMap}; @@ -223,7 +223,9 @@ impl Component { /// lives for as long as the module and is nevery externally modified for /// the lifetime of the deserialized module. pub unsafe fn deserialize_raw(engine: &Engine, memory: NonNull<[u8]>) -> Result { - let code = engine.load_code_raw(memory, ObjectKind::Component)?; + // SAFETY: the contract required by `load_code_raw` is the same as this + // function. + let code = unsafe { engine.load_code_raw(memory, ObjectKind::Component)? }; Component::from_parts(engine, code, None) } @@ -834,9 +836,10 @@ impl Component { pub(crate) fn export_lifted_function( &self, export: ExportIndex, - ) -> (TypeFuncIndex, &CoreDef, &CanonicalOptions) { - match &self.env_component().export_items[export] { - Export::LiftedFunction { ty, func, options } => (*ty, func, options), + ) -> (TypeFuncIndex, &CoreDef, OptionsIndex) { + let component = self.env_component(); + match &component.export_items[export] { + Export::LiftedFunction { ty, func, options } => (*ty, func, *options), _ => unreachable!(), } } diff --git a/crates/wasmtime/src/runtime/component/concurrent.rs b/crates/wasmtime/src/runtime/component/concurrent.rs index 2814fc7025..f14aa9e450 100644 --- a/crates/wasmtime/src/runtime/component/concurrent.rs +++ b/crates/wasmtime/src/runtime/component/concurrent.rs @@ -1,5 +1,3 @@ -#![deny(unsafe_op_in_unsafe_fn)] - //! Runtime support for the Component Model Async ABI. //! //! This module and its submodules provide host runtime support for Component @@ -71,7 +69,8 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::fmt; use std::future::Future; use std::marker::PhantomData; -use std::mem::{self, MaybeUninit}; +use std::mem::{self, ManuallyDrop, MaybeUninit}; +use std::ops::DerefMut; use std::pin::{Pin, pin}; use std::ptr::{self, NonNull}; use std::slice; @@ -81,16 +80,18 @@ use std::vec::Vec; use table::{Table, TableDebug, TableError, TableId}; use wasmtime_environ::PrimaryMap; use wasmtime_environ::component::{ - ExportIndex, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, PREPARE_ASYNC_NO_RESULT, - PREPARE_ASYNC_WITH_RESULT, RuntimeComponentInstanceIndex, StringEncoding, - TypeComponentGlobalErrorContextTableIndex, TypeComponentLocalErrorContextTableIndex, - TypeFutureTableIndex, TypeStreamTableIndex, TypeTupleIndex, + CanonicalOptions, CanonicalOptionsDataModel, ExportIndex, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, + OptionsIndex, PREPARE_ASYNC_NO_RESULT, PREPARE_ASYNC_WITH_RESULT, + RuntimeComponentInstanceIndex, StringEncoding, TypeComponentGlobalErrorContextTableIndex, + TypeComponentLocalErrorContextTableIndex, TypeFutureTableIndex, TypeStreamTableIndex, + TypeTupleIndex, }; pub use abort::AbortHandle; pub use futures_and_streams::{ - DropWithStore, DropWithStoreAndValue, ErrorContext, FutureReader, FutureWriter, ReadBuffer, - StreamReader, StreamWriter, VecBuffer, WithAccessor, WithAccessorAndValue, WriteBuffer, + ErrorContext, FutureReader, FutureWriter, GuardedFutureReader, GuardedFutureWriter, + GuardedStreamReader, GuardedStreamWriter, ReadBuffer, StreamReader, StreamWriter, VecBuffer, + WriteBuffer, }; pub(crate) use futures_and_streams::{ ResourcePair, lower_error_context_to_index, lower_future_to_index, lower_stream_to_index, @@ -201,14 +202,14 @@ const START_FLAG_ASYNC_CALLEE: u32 = wasmtime_environ::component::START_FLAG_ASY /// instance to which the current host task belongs. /// /// See [`Accessor::with`] for details. -pub struct Access<'a, T: 'static, D: HasData = HasSelf> { +pub struct Access<'a, T: 'static, D: HasData + ?Sized = HasSelf> { accessor: &'a Accessor, store: StoreContextMut<'a, T>, } impl<'a, T, D> Access<'a, T, D> where - D: HasData, + D: HasData + ?Sized, T: 'static, { /// Get mutable access to the store data. @@ -244,7 +245,7 @@ where impl<'a, T, D> AsContext for Access<'a, T, D> where - D: HasData, + D: HasData + ?Sized, T: 'static, { type Data = T; @@ -256,7 +257,7 @@ where impl<'a, T, D> AsContextMut for Access<'a, T, D> where - D: HasData, + D: HasData + ?Sized, T: 'static, { fn as_context_mut(&mut self) -> StoreContextMut<'_, T> { @@ -325,7 +326,7 @@ where /// feel free to file an issue on the Wasmtime repository. pub struct Accessor> where - D: HasData, + D: HasData + ?Sized, { token: StoreToken, get_data: fn(&mut T) -> D::Data<'_>, @@ -353,7 +354,7 @@ pub trait AsAccessor { /// The `D` in `Accessor`, or the projection out of /// `Self::Data`. - type AccessorData: HasData; + type AccessorData: HasData + ?Sized; /// Returns the accessor that this is referring to. fn as_accessor(&self) -> &Accessor; @@ -368,7 +369,7 @@ impl AsAccessor for &T { } } -impl AsAccessor for Accessor { +impl AsAccessor for Accessor { type Data = T; type AccessorData = D; @@ -427,7 +428,7 @@ impl Accessor { impl Accessor where - D: HasData, + D: HasData + ?Sized, { /// Run the specified closure, passing it mutable access to the store. /// @@ -542,7 +543,7 @@ where // { ... }`). So this seems to be the best we can do for the time being. pub trait AccessorTask: Send + 'static where - D: HasData, + D: HasData + ?Sized, { /// Run the task. fn run(self, accessor: &Accessor) -> impl Future + Send; @@ -673,6 +674,12 @@ impl GuestCall { } } +/// Job to be run on a worker fiber. +enum WorkerItem { + GuestCall(GuestCall), + Function(Mutex Result<()> + Send>>), +} + /// Represents state related to an in-progress poll operation (e.g. `task.poll` /// or `CallbackCode.POLL`). #[derive(Debug)] @@ -701,6 +708,8 @@ enum WorkItem { GuestCall(GuestCall), /// A pending `task.poll` or `CallbackCode.POLL` operation. Poll(PollParams), + /// A job to run on a worker fiber. + WorkerFunction(Mutex Result<()> + Send>>), } impl fmt::Debug for WorkItem { @@ -710,6 +719,7 @@ impl fmt::Debug for WorkItem { Self::ResumeFiber(_) => f.debug_tuple("ResumeFiber").finish(), Self::GuestCall(call) => f.debug_tuple("GuestCall").field(call).finish(), Self::Poll(params) => f.debug_tuple("Poll").field(params).finish(), + Self::WorkerFunction(_) => f.debug_tuple("WorkerFunction").finish(), } } } @@ -1164,6 +1174,10 @@ impl ConcurrentState { self.get_mut(task)?.context[usize::try_from(slot).unwrap()] = val; Ok(()) } + + fn options(&self, options: OptionsIndex) -> &CanonicalOptions { + &self.component.env_component().options[options] + } } impl Instance { @@ -1290,27 +1304,28 @@ impl Instance { struct Dropper<'a, T: 'static, V> { store: StoreContextMut<'a, T>, - value: Option, + value: ManuallyDrop, } impl<'a, T, V> Drop for Dropper<'a, T, V> { fn drop(&mut self) { - let value = self.value.take(); - tls::set(self.store.0.traitobj_mut(), move || drop(value)); + tls::set(self.store.0.traitobj_mut(), || { + // SAFETY: Here we drop the value without moving it for the + // first and only time -- per the contract for `Drop::drop`, + // this code won't run again, and the `value` field will no + // longer be accessible. + unsafe { ManuallyDrop::drop(&mut self.value) } + }); } } let accessor = &Accessor::new(token, Some(self)); let dropper = &mut Dropper { store, - value: Some(fun(accessor)), + value: ManuallyDrop::new(fun(accessor)), }; - // SAFETY: `dropper` is a local, non-escaping variable and we do not - // move its `value` field until it is dropped. - // - // TODO: Could/should we make this safe using some combination of `pin!` - // and `pin_project!`? - let future = unsafe { Pin::new_unchecked(dropper.value.as_mut().unwrap()) }; + // SAFETY: We never move `dropper` nor its `value` field. + let future = unsafe { Pin::new_unchecked(dropper.value.deref_mut()) }; self.poll_until(dropper.store.as_context_mut(), future) .await @@ -1345,7 +1360,7 @@ impl Instance { ) -> AbortHandle where T: 'static, - D: HasData, + D: HasData + ?Sized, { let store = store.as_context_mut(); @@ -1397,8 +1412,22 @@ impl Instance { // immediately if one of them fails. let next = match self.set_tls(store.0, || next.as_mut().poll(cx)) { Poll::Ready(Some(output)) => { - if let Err(e) = output.consume(store.0.traitobj_mut(), self) { - return Poll::Ready(Err(e)); + match output { + HostTaskOutput::Result(Err(e)) => return Poll::Ready(Err(e)), + HostTaskOutput::Result(Ok(())) => {} + HostTaskOutput::Function(fun) => { + // Defer calling this function to a worker fiber + // in case it involves calling a guest realloc + // function as part of a lowering operation. + // + // TODO: This isn't necessary for _all_ + // `HostOutput::Function`s, so we could optimize + // by adding another variant to `HostOutput` to + // distinguish which ones need it and which + // don't. + self.concurrent_state_mut(store.0) + .push_high_priority(WorkItem::WorkerFunction(Mutex::new(fun))) + } } Poll::Ready(true) } @@ -1418,12 +1447,14 @@ impl Instance { let ready = mem::take(&mut state.low_priority); if ready.is_empty() { return match next { - // In this case, one of the futures in - // `ConcurrentState::futures` completed - // successfully, so we return now and continue the - // outer loop in case there is another one ready to - // complete. - Poll::Ready(true) => Poll::Ready(Ok(Either::Right(Vec::new()))), + Poll::Ready(true) => { + // In this case, one of the futures in + // `ConcurrentState::futures` completed + // successfully, so we return now and continue + // the outer loop in case there is another one + // ready to complete. + Poll::Ready(Ok(Either::Right(Vec::new()))) + } Poll::Ready(false) => { // Poll the future we were passed one last time // in case one of `ConcurrentState::futures` had @@ -1528,7 +1559,8 @@ impl Instance { WorkItem::GuestCall(call) => { let state = self.concurrent_state_mut(store); if call.is_ready(state)? { - self.run_on_worker(store, call).await?; + self.run_on_worker(store, WorkerItem::GuestCall(call)) + .await?; } else { let task = state.get_mut(call.task)?; if !task.starting_sent { @@ -1577,6 +1609,9 @@ impl Instance { })); } } + WorkItem::WorkerFunction(fun) => { + self.run_on_worker(store, WorkerItem::Function(fun)).await?; + } } Ok(()) @@ -1622,23 +1657,25 @@ impl Instance { } /// Execute the specified guest call on a worker fiber. - async fn run_on_worker(self, store: &mut StoreOpaque, call: GuestCall) -> Result<()> { + async fn run_on_worker(self, store: &mut StoreOpaque, item: WorkerItem) -> Result<()> { let worker = if let Some(fiber) = self.concurrent_state_mut(store).worker.take() { fiber } else { fiber::make_fiber(store.traitobj_mut(), move |store| { loop { - let call = self.concurrent_state_mut(store).guest_call.take().unwrap(); - self.handle_guest_call(store, call)?; + match self.concurrent_state_mut(store).worker_item.take().unwrap() { + WorkerItem::GuestCall(call) => self.handle_guest_call(store, call)?, + WorkerItem::Function(fun) => fun.into_inner().unwrap()(store, self)?, + } self.suspend(store, SuspendReason::NeedWork)?; } })? }; - let guest_call = &mut self.concurrent_state_mut(store).guest_call; - assert!(guest_call.is_none()); - *guest_call = Some(call); + let worker_item = &mut self.concurrent_state_mut(store).worker_item; + assert!(worker_item.is_none()); + *worker_item = Some(item); self.resume_fiber(store, worker).await } @@ -2375,8 +2412,10 @@ impl Instance { // Use the caller's `GuestTask::sync_call_set` to register interest in // the subtask... + let guest_waitable = Waitable::Guest(guest_task); + let old_set = guest_waitable.common(state)?.set; let set = state.get_mut(caller)?.sync_call_set; - Waitable::Guest(guest_task).join(state, Some(set))?; + guest_waitable.join(state, Some(set))?; // ... and suspend this fiber temporarily while we wait for it to start. // @@ -2431,7 +2470,7 @@ impl Instance { let state = self.concurrent_state_mut(store.0); - Waitable::Guest(guest_task).join(state, None)?; + guest_waitable.join(state, old_set)?; if let Some(storage) = storage { // The caller used a sync-lowered import to call an async-lifted @@ -2719,23 +2758,19 @@ impl Instance { /// Implements the `task.return` intrinsic, lifting the result for the /// current guest task. - /// - /// SAFETY: The `memory` and `storage` pointers must be valid, and `storage` - /// must contain at least `storage_len` items. - pub(crate) unsafe fn task_return( + pub(crate) fn task_return( self, store: &mut dyn VMStore, ty: TypeTupleIndex, - memory: *mut VMMemoryDefinition, - string_encoding: u8, - storage: *mut ValRaw, - storage_len: usize, + options: OptionsIndex, + storage: &[ValRaw], ) -> Result<()> { - // SAFETY: The `wasmtime_cranelift`-generated code that calls this - // method will have ensured that `storage` is a valid pointer containing - // at least `storage_len` items. - let storage = unsafe { std::slice::from_raw_parts(storage, storage_len) }; let state = self.concurrent_state_mut(store); + let CanonicalOptions { + string_encoding, + data_model, + .. + } = *state.options(options); let guest_task = state.guest_task.unwrap(); let lift = state .get_mut(guest_task)? @@ -2744,17 +2779,29 @@ impl Instance { .ok_or_else(|| { anyhow!("`task.return` or `task.cancel` called more than once for current task") })?; + assert!(state.get(guest_task)?.result.is_none()); - if ty != lift.ty - || (!memory.is_null() - && memory != lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut())) - || string_encoding != lift.string_encoding as u8 - { + let invalid = ty != lift.ty + || string_encoding != lift.string_encoding + || match data_model { + CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory { + Some(memory) => { + let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut()); + let actual = self.id().get(store).runtime_memory(memory); + expected != actual + } + // Memory not specified, meaning it didn't need to be + // specified per validation, so not invalid. + None => false, + }, + // Always invalid as this isn't supported. + CanonicalOptionsDataModel::Gc { .. } => true, + }; + + if invalid { bail!("invalid `task.return` signature and/or options for current task"); } - assert!(state.get(guest_task)?.result.is_none()); - log::trace!("task.return for {guest_task:?}"); let result = (lift.lift)(store, self, storage)?; @@ -2853,15 +2900,16 @@ impl Instance { pub(crate) fn waitable_set_wait( self, store: &mut dyn VMStore, - caller_instance: RuntimeComponentInstanceIndex, - async_: bool, - memory: *mut VMMemoryDefinition, + options: OptionsIndex, set: u32, payload: u32, ) -> Result { - let (rep, WaitableState::Set) = self.concurrent_state_mut(store).waitable_tables - [caller_instance] - .get_mut_by_index(set)? + let state = self.concurrent_state_mut(store); + let opts = state.options(options); + let async_ = opts.async_; + let caller_instance = opts.instance; + let (rep, WaitableState::Set) = + state.waitable_tables[caller_instance].get_mut_by_index(set)? else { bail!("invalid waitable-set handle"); }; @@ -2871,9 +2919,9 @@ impl Instance { async_, WaitableCheck::Wait(WaitableCheckParams { set: TableId::new(rep), - memory, - payload, caller_instance, + options, + payload, }), ) } @@ -2882,15 +2930,16 @@ impl Instance { pub(crate) fn waitable_set_poll( self, store: &mut dyn VMStore, - caller_instance: RuntimeComponentInstanceIndex, - async_: bool, - memory: *mut VMMemoryDefinition, + options: OptionsIndex, set: u32, payload: u32, ) -> Result { - let (rep, WaitableState::Set) = self.concurrent_state_mut(store).waitable_tables - [caller_instance] - .get_mut_by_index(set)? + let state = self.concurrent_state_mut(store); + let opts = state.options(options); + let async_ = opts.async_; + let caller_instance = opts.instance; + let (rep, WaitableState::Set) = + state.waitable_tables[caller_instance].get_mut_by_index(set)? else { bail!("invalid waitable-set handle"); }; @@ -2900,9 +2949,9 @@ impl Instance { async_, WaitableCheck::Poll(WaitableCheckParams { set: TableId::new(rep), - memory, - payload, caller_instance, + options, + payload, }), ) } @@ -3001,16 +3050,7 @@ impl Instance { } }; let store = store.store_opaque_mut(); - let options = unsafe { - Options::new( - store.id(), - NonNull::new(params.memory), - None, - StringEncoding::Utf8, - true, - None, - ) - }; + let options = Options::new_index(store, self, params.options); let ptr = func::validate_inbounds::<(u32, u32)>( options.memory_mut(store), &ValRaw::u32(params.payload), @@ -3129,10 +3169,14 @@ impl Instance { if async_ { return Ok(BLOCKED); } else { + let waitable = Waitable::Guest(guest_task); + let old_set = waitable.common(concurrent_state)?.set; let set = concurrent_state.get_mut(caller)?.sync_call_set; - Waitable::Guest(guest_task).join(concurrent_state, Some(set))?; + waitable.join(concurrent_state, Some(set))?; self.suspend(store, SuspendReason::Waiting { set, task: caller })?; + + waitable.join(self.concurrent_state_mut(store), old_set)?; } } } @@ -3235,54 +3279,50 @@ pub trait VMComponentAsyncStore { ) -> Result; /// The `future.write` intrinsic. - unsafe fn future_write( + fn future_write( &mut self, instance: Instance, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: u8, - async_: bool, ty: TypeFutureTableIndex, + options: OptionsIndex, future: u32, address: u32, ) -> Result; /// The `future.read` intrinsic. - unsafe fn future_read( + fn future_read( &mut self, instance: Instance, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: u8, - async_: bool, ty: TypeFutureTableIndex, + options: OptionsIndex, future: u32, address: u32, ) -> Result; + /// The `future.drop-writable` intrinsic. + fn future_drop_writable( + &mut self, + instance: Instance, + ty: TypeFutureTableIndex, + writer: u32, + ) -> Result<()>; + /// The `stream.write` intrinsic. - unsafe fn stream_write( + fn stream_write( &mut self, instance: Instance, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: u8, - async_: bool, ty: TypeStreamTableIndex, + options: OptionsIndex, stream: u32, address: u32, count: u32, ) -> Result; /// The `stream.read` intrinsic. - unsafe fn stream_read( + fn stream_read( &mut self, instance: Instance, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: u8, - async_: bool, ty: TypeStreamTableIndex, + options: OptionsIndex, stream: u32, address: u32, count: u32, @@ -3290,13 +3330,11 @@ pub trait VMComponentAsyncStore { /// The "fast-path" implementation of the `stream.write` intrinsic for /// "flat" (i.e. memcpy-able) payloads. - unsafe fn flat_stream_write( + fn flat_stream_write( &mut self, instance: Instance, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - async_: bool, ty: TypeStreamTableIndex, + options: OptionsIndex, payload_size: u32, payload_align: u32, stream: u32, @@ -3306,13 +3344,11 @@ pub trait VMComponentAsyncStore { /// The "fast-path" implementation of the `stream.read` intrinsic for "flat" /// (i.e. memcpy-able) payloads. - unsafe fn flat_stream_read( + fn flat_stream_read( &mut self, instance: Instance, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - async_: bool, ty: TypeStreamTableIndex, + options: OptionsIndex, payload_size: u32, payload_align: u32, stream: u32, @@ -3320,14 +3356,20 @@ pub trait VMComponentAsyncStore { count: u32, ) -> Result; + /// The `stream.drop-writable` intrinsic. + fn stream_drop_writable( + &mut self, + instance: Instance, + ty: TypeStreamTableIndex, + writer: u32, + ) -> Result<()>; + /// The `error-context.debug-message` intrinsic. - unsafe fn error_context_debug_message( + fn error_context_debug_message( &mut self, instance: Instance, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: u8, ty: TypeComponentLocalErrorContextTableIndex, + options: OptionsIndex, err_ctx_handle: u32, debug_msg_address: u32, ) -> Result<()>; @@ -3434,222 +3476,179 @@ impl VMComponentAsyncStore for StoreInner { } } - unsafe fn future_write( + fn future_write( &mut self, instance: Instance, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: u8, - async_: bool, ty: TypeFutureTableIndex, + options: OptionsIndex, future: u32, address: u32, ) -> Result { - // SAFETY: Per the trait-level documentation for - // `VMComponentAsyncStore`, all raw pointers passed to this function - // must be valid. - unsafe { - instance - .guest_write( - StoreContextMut(self), - memory, - realloc, - string_encoding, - async_, - TableIndex::Future(ty), - None, - future, - address, - 1, - ) - .map(|result| result.encode()) - } + instance + .guest_write( + StoreContextMut(self), + TableIndex::Future(ty), + options, + None, + future, + address, + 1, + ) + .map(|result| result.encode()) } - unsafe fn future_read( + fn future_read( &mut self, instance: Instance, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: u8, - async_: bool, ty: TypeFutureTableIndex, + options: OptionsIndex, future: u32, address: u32, ) -> Result { - // SAFETY: See corresponding comment in `Self::future_write`. - unsafe { - instance - .guest_read( - StoreContextMut(self), - memory, - realloc, - string_encoding, - async_, - TableIndex::Future(ty), - None, - future, - address, - 1, - ) - .map(|result| result.encode()) - } + instance + .guest_read( + StoreContextMut(self), + TableIndex::Future(ty), + options, + None, + future, + address, + 1, + ) + .map(|result| result.encode()) } - unsafe fn stream_write( + fn stream_write( &mut self, instance: Instance, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: u8, - async_: bool, ty: TypeStreamTableIndex, + options: OptionsIndex, stream: u32, address: u32, count: u32, ) -> Result { - // SAFETY: See corresponding comment in `Self::future_write`. - unsafe { - instance - .guest_write( - StoreContextMut(self), - memory, - realloc, - string_encoding, - async_, - TableIndex::Stream(ty), - None, - stream, - address, - count, - ) - .map(|result| result.encode()) - } + instance + .guest_write( + StoreContextMut(self), + TableIndex::Stream(ty), + options, + None, + stream, + address, + count, + ) + .map(|result| result.encode()) } - unsafe fn stream_read( + fn stream_read( &mut self, instance: Instance, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: u8, - async_: bool, ty: TypeStreamTableIndex, + options: OptionsIndex, stream: u32, address: u32, count: u32, ) -> Result { - // SAFETY: See corresponding comment in `Self::future_write`. - unsafe { - instance - .guest_read( - StoreContextMut(self), - memory, - realloc, - string_encoding, - async_, - TableIndex::Stream(ty), - None, - stream, - address, - count, - ) - .map(|result| result.encode()) - } + instance + .guest_read( + StoreContextMut(self), + TableIndex::Stream(ty), + options, + None, + stream, + address, + count, + ) + .map(|result| result.encode()) } - unsafe fn flat_stream_write( + fn future_drop_writable( + &mut self, + instance: Instance, + ty: TypeFutureTableIndex, + writer: u32, + ) -> Result<()> { + instance.guest_drop_writable(StoreContextMut(self), TableIndex::Future(ty), writer) + } + + fn flat_stream_write( &mut self, instance: Instance, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - async_: bool, ty: TypeStreamTableIndex, + options: OptionsIndex, payload_size: u32, payload_align: u32, stream: u32, address: u32, count: u32, ) -> Result { - // SAFETY: See corresponding comment in `Self::future_write`. - unsafe { - instance - .guest_write( - StoreContextMut(self), - memory, - realloc, - StringEncoding::Utf8 as u8, - async_, - TableIndex::Stream(ty), - Some(FlatAbi { - size: payload_size, - align: payload_align, - }), - stream, - address, - count, - ) - .map(|result| result.encode()) - } + instance + .guest_write( + StoreContextMut(self), + TableIndex::Stream(ty), + options, + Some(FlatAbi { + size: payload_size, + align: payload_align, + }), + stream, + address, + count, + ) + .map(|result| result.encode()) } - unsafe fn flat_stream_read( + fn flat_stream_read( &mut self, instance: Instance, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - async_: bool, ty: TypeStreamTableIndex, + options: OptionsIndex, payload_size: u32, payload_align: u32, stream: u32, address: u32, count: u32, ) -> Result { - // SAFETY: See corresponding comment in `Self::future_write`. - unsafe { - instance - .guest_read( - StoreContextMut(self), - memory, - realloc, - StringEncoding::Utf8 as u8, - async_, - TableIndex::Stream(ty), - Some(FlatAbi { - size: payload_size, - align: payload_align, - }), - stream, - address, - count, - ) - .map(|result| result.encode()) - } + instance + .guest_read( + StoreContextMut(self), + TableIndex::Stream(ty), + options, + Some(FlatAbi { + size: payload_size, + align: payload_align, + }), + stream, + address, + count, + ) + .map(|result| result.encode()) } - unsafe fn error_context_debug_message( + fn stream_drop_writable( + &mut self, + instance: Instance, + ty: TypeStreamTableIndex, + writer: u32, + ) -> Result<()> { + instance.guest_drop_writable(StoreContextMut(self), TableIndex::Stream(ty), writer) + } + + fn error_context_debug_message( &mut self, instance: Instance, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: u8, ty: TypeComponentLocalErrorContextTableIndex, + options: OptionsIndex, err_ctx_handle: u32, debug_msg_address: u32, ) -> Result<()> { - // SAFETY: See corresponding comment in `Self::future_write`. - unsafe { - instance.error_context_debug_message( - StoreContextMut(self), - memory, - realloc, - string_encoding, - ty, - err_ctx_handle, - debug_msg_address, - ) - } + instance.error_context_debug_message( + StoreContextMut(self), + ty, + options, + err_ctx_handle, + debug_msg_address, + ) } } @@ -3952,6 +3951,8 @@ impl Waitable { /// remove it from any set it may currently belong to (when `set` is /// `None`). fn join(&self, state: &mut ConcurrentState, set: Option>) -> Result<()> { + log::trace!("waitable {self:?} join set {set:?}",); + let old = mem::replace(&mut self.common(state)?.set, set); if let Some(old) = old { @@ -4225,7 +4226,7 @@ pub struct ConcurrentState { /// This helps us avoid creating a new fiber for each `GuestCall` work item. worker: Option>, /// A place to stash the work item for which we're resuming a worker fiber. - guest_call: Option, + worker_item: Option, /// (Sub)Component specific error context tracking /// @@ -4287,7 +4288,7 @@ impl ConcurrentState { low_priority: Vec::new(), suspend_reason: None, worker: None, - guest_call: None, + worker_item: None, error_context_tables, global_error_context_ref_counts: BTreeMap::new(), component: component.clone(), @@ -4315,9 +4316,9 @@ impl ConcurrentState { fibers: &mut Vec>, futures: &mut Vec>, ) { - for entry in mem::take(&mut self.table) { - if let Ok(set) = entry.downcast::() { - for mode in set.waiting.into_values() { + for entry in self.table.iter_mut() { + if let Some(set) = entry.downcast_mut::() { + for mode in mem::take(&mut set.waiting).into_values() { if let WaitMode::Fiber(fiber) = mode { fibers.push(fiber); } @@ -4433,9 +4434,9 @@ fn unpack_callback_code(code: u32) -> (u32, u32) { /// `waitable-set.poll`. struct WaitableCheckParams { set: TableId, - memory: *mut VMMemoryDefinition, - payload: u32, caller_instance: RuntimeComponentInstanceIndex, + options: OptionsIndex, + payload: u32, } /// Helper enum for passing parameters to `ComponentInstance::waitable_check`. diff --git a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs index b06691a799..0433bbb2fa 100644 --- a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs +++ b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs @@ -3,15 +3,13 @@ use super::{ Event, GlobalErrorContextRefCount, LocalErrorContextRefCount, StateTable, Waitable, WaitableCommon, WaitableState, }; -use crate::component::concurrent::ConcurrentState; +use crate::component::concurrent::{ConcurrentState, WorkItem}; use crate::component::func::{self, LiftContext, LowerContext, Options}; use crate::component::matching::InstanceType; use crate::component::values::{ErrorContextAny, FutureAny, StreamAny}; -use crate::component::{ - Accessor, AsAccessor, HasData, HasSelf, Instance, Lower, Val, WasmList, WasmStr, -}; +use crate::component::{AsAccessor, Instance, Lower, Val, WasmList, WasmStr}; use crate::store::{StoreOpaque, StoreToken}; -use crate::vm::{VMFuncRef, VMMemoryDefinition, VMStore}; +use crate::vm::VMStore; use crate::{AsContextMut, StoreContextMut, ValRaw}; use anyhow::{Context, Result, anyhow, bail}; use buffers::Extender; @@ -23,14 +21,12 @@ use std::future; use std::iter; use std::marker::PhantomData; use std::mem::{self, MaybeUninit}; -use std::ops::{Deref, DerefMut}; -use std::ptr::NonNull; use std::string::{String, ToString}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::task::{Poll, Waker}; use std::vec::Vec; use wasmtime_environ::component::{ - CanonicalAbiInfo, ComponentTypes, InterfaceType, RuntimeComponentInstanceIndex, StringEncoding, + CanonicalAbiInfo, ComponentTypes, InterfaceType, OptionsIndex, RuntimeComponentInstanceIndex, TypeComponentGlobalErrorContextTableIndex, TypeComponentLocalErrorContextTableIndex, TypeFutureTableIndex, TypeStreamTableIndex, }; @@ -198,7 +194,12 @@ fn accept_reader, U: 'static> let types = instance.id().get(store.0).component().types().clone(); let count = buffer.remaining().len().min(count); - let lower = &mut LowerContext::new(store.as_context_mut(), options, &types, instance); + let lower = &mut if T::MAY_REQUIRE_REALLOC { + LowerContext::new + } else { + LowerContext::new_without_realloc + }(store.as_context_mut(), options, &types, instance); + if address % usize::try_from(T::ALIGN32)? != 0 { bail!("read pointer not aligned"); } @@ -405,184 +406,24 @@ pub(super) struct FlatAbi { pub(super) align: u32, } -/// Trait representing objects (such as streams, futures, or structs containing -/// them) which require access to the store in order to be disposed of properly. -pub trait DropWithStore: Sized { - /// Dispose of `self` using the specified store. - fn drop(self, store: impl AsContextMut) -> Result<()>; - - /// Dispose of `self` using the specified accessor. - fn drop_with(self, accessor: impl AsAccessor) -> Result<()> { - accessor.as_accessor().with(move |store| self.drop(store)) - } -} - -/// Trait representing objects (such as the writable end of a future or a struct -/// containing one) which require access to the store _and_ a value to write in -/// order to be disposed of properly. -pub trait DropWithStoreAndValue: Sized { - /// Dispose of `self` using the specified store, writing the specified value. - fn drop(self, store: impl AsContextMut, value: T) -> Result<()>; - - /// Dispose of `self` using the specified accessor and value. - fn drop_with(self, accessor: impl AsAccessor, value: T) -> Result<()> { - accessor - .as_accessor() - .with(move |store| self.drop(store, value)) - } -} - -/// RAII wrapper for `DropWithStore` implementations. -/// -/// This may be used to automatically dispose of the wrapped object when it goes -/// out of scope. -pub struct WithAccessor<'a, T: DropWithStore, U: 'static, D: HasData = HasSelf> { - accessor: &'a Accessor, - inner: Option, -} - -impl<'a, T: DropWithStore, U, D: HasData> WithAccessor<'a, T, U, D> { - /// Create a new instance wrapping the specified `inner` object. - pub fn new(accessor: &'a Accessor, inner: T) -> Self { - Self { - accessor, - inner: Some(inner), - } - } - - /// Deconstruct `self`, returning the inner object. - pub fn into_inner(mut self) -> T { - // TODO: Could consider using `unwrap_unchecked` here for performance. - self.inner.take().unwrap() - } -} - -impl<'a, T: DropWithStore, U, D: HasData> Deref for WithAccessor<'a, T, U, D> { - type Target = T; - - fn deref(&self) -> &T { - // TODO: Could consider using `unwrap_unchecked` here for performance. - self.inner.as_ref().unwrap() - } -} - -impl<'a, T: DropWithStore, U, D: HasData> DerefMut for WithAccessor<'a, T, U, D> { - fn deref_mut(&mut self) -> &mut T { - // TODO: Could consider using `unwrap_unchecked` here for performance. - self.inner.as_mut().unwrap() - } -} - -impl<'a, T: DropWithStore, U, D: HasData> Drop for WithAccessor<'a, T, U, D> { - fn drop(&mut self) { - if let Some(inner) = self.inner.take() { - _ = inner.drop_with(self.accessor); - } - } -} - -/// RAII wrapper for `DropWithStoreAndValue` implementations. -/// -/// This may be used to automatically dispose of the wrapped object when it goes -/// out of scope, passing the specified value. -pub struct WithAccessorAndValue<'a, V, T, U, D = HasSelf> -where - U: 'static, - D: HasData, - V: func::Lower + Send + Sync + 'static, - T: DropWithStoreAndValue, -{ - accessor: &'a Accessor, - inner_and_value: Option<(T, V)>, -} - -impl< - 'a, - V: func::Lower + Send + Sync + 'static, - T: DropWithStoreAndValue, - U: 'static, - D: HasData, -> WithAccessorAndValue<'a, V, T, U, D> -{ - /// Create a new instance wrapping the specified `inner` object and value. - pub fn new(accessor: &'a Accessor, inner: T, value: V) -> Self { - Self { - accessor, - inner_and_value: Some((inner, value)), - } - } - - /// Deconstruct `self`, returning the inner object. - pub fn into_inner(mut self) -> T { - // TODO: Could consider using `unwrap_unchecked` here for performance. - self.inner_and_value.take().unwrap().0 - } -} - -impl< - 'a, - V: func::Lower + Send + Sync + 'static, - T: DropWithStoreAndValue, - U: 'static, - D: HasData, -> Deref for WithAccessorAndValue<'a, V, T, U, D> -{ - type Target = T; - - fn deref(&self) -> &T { - // TODO: Could consider using `unwrap_unchecked` here for performance. - &self.inner_and_value.as_ref().unwrap().0 - } -} - -impl< - 'a, - V: func::Lower + Send + Sync + 'static, - T: DropWithStoreAndValue, - U: 'static, - D: HasData, -> DerefMut for WithAccessorAndValue<'a, V, T, U, D> -{ - fn deref_mut(&mut self) -> &mut T { - // TODO: Could consider using `unwrap_unchecked` here for performance. - &mut self.inner_and_value.as_mut().unwrap().0 - } -} - -impl< - 'a, - V: func::Lower + Send + Sync + 'static, - T: DropWithStoreAndValue, - U: 'static, - D: HasData, -> Drop for WithAccessorAndValue<'a, V, T, U, D> -{ - fn drop(&mut self) { - if let Some((inner, value)) = self.inner_and_value.take() { - _ = inner.drop_with(self.accessor, value); - } - } -} - /// Represents the writable end of a Component Model `future`. /// -/// Note that `FutureWriter` instances must be disposed of using either -/// `FutureWriter::write` or `DropWithStoreAndValue::drop`; otherwise the -/// in-store representation will leak and the reader end will hang indefinitely. -/// Consider using [`WithAccessorAndValue`] to ensure that disposal happens -/// automatically. +/// Note that `FutureWriter` instances must be disposed of using either `write` +/// or `close`; otherwise the in-store representation will leak and the reader +/// end will hang indefinitely. Consider using [`GuardedFutureWriter`] to +/// ensure that disposal happens automatically. pub struct FutureWriter { + default: fn() -> T, id: TableId, instance: Instance, - _phantom: PhantomData, } impl FutureWriter { - fn new(id: TableId, instance: Instance) -> Self { + fn new(default: fn() -> T, id: TableId, instance: Instance) -> Self { Self { + default, id, instance, - _phantom: PhantomData, } } @@ -597,6 +438,15 @@ impl FutureWriter { /// Panics if the store that the [`Accessor`] is derived from does not own /// this future. pub async fn write(self, accessor: impl AsAccessor, value: T) -> bool + where + T: func::Lower + Send + Sync + 'static, + { + self.guard(accessor).write(value).await + } + + /// Mut-ref signature instead of by-value signature for + /// `GuardedFutureWriter` to more easily call. + async fn write_(&mut self, accessor: impl AsAccessor, value: T) -> bool where T: func::Lower + Send + Sync + 'static, { @@ -607,8 +457,6 @@ impl FutureWriter { .host_write_async(accessor, self.id, Some(value), TransmitKind::Future) .await; - _ = accessor.with(|store| self.just_drop(store)); - match result { Ok(HostResult { dropped, .. }) => !dropped, Err(_) => todo!("guarantee buffer recovery if `host_write` fails"), @@ -628,37 +476,133 @@ impl FutureWriter { watch_reader(accessor, self.instance, self.id).await } - fn just_drop(self, mut store: impl AsContextMut) -> Result<()> { - self.instance.host_drop_writer( - store.as_context_mut().0.traitobj_mut(), - self.id, - TransmitKind::Future, - ) + /// Close this `FutureWriter`, writing the default value. + /// + /// # Panics + /// + /// Panics if the store that the [`Accessor`] is derived from does not own + /// this future. Usage of this future after calling `close` will also cause + /// a panic. + pub fn close(&mut self, mut store: impl AsContextMut) + where + T: func::Lower + Send + Sync + 'static, + { + let id = mem::replace(&mut self.id, TableId::new(0)); + let default = self.default; + self.instance + .host_drop_writer(store.as_context_mut(), id, Some(&move || Ok(default()))) + .unwrap(); + } + + /// Convenience method around [`Self::close`]. + pub fn close_with(&mut self, accessor: impl AsAccessor) + where + T: func::Lower + Send + Sync + 'static, + { + accessor.as_accessor().with(|access| self.close(access)) + } + + /// Returns a [`GuardedFutureWriter`] which will auto-close this future on + /// drop and clean it up from the store. + /// + /// Note that the `accessor` provided must own this future and is + /// additionally transferred to the `GuardedFutureWriter` return value. + pub fn guard(self, accessor: A) -> GuardedFutureWriter + where + T: func::Lower + Send + Sync + 'static, + A: AsAccessor, + { + GuardedFutureWriter::new(accessor, self) } } -impl DropWithStoreAndValue for FutureWriter { - fn drop(self, mut store: impl AsContextMut, value: T) -> Result<()> { - let result = self.instance.host_write( - store.as_context_mut(), - self.id, - Some(value), - TransmitKind::Future, - ); +/// A [`FutureWriter`] paired with an [`Accessor`]. +/// +/// This is an RAII wrapper around [`FutureWriter`] that ensures it is closed +/// when dropped. This can be created through [`GuardedFutureWriter::new`] or +/// [`FutureWriter::guard`]. +pub struct GuardedFutureWriter +where + T: func::Lower + Send + Sync + 'static, + A: AsAccessor, +{ + // This field is `None` to implement the conversion from this guard back to + // `FutureWriter`. When `None` is seen in the destructor it will cause the + // destructor to do nothing. + writer: Option>, + accessor: A, +} - _ = self.just_drop(store); +impl GuardedFutureWriter +where + T: func::Lower + Send + Sync + 'static, + A: AsAccessor, +{ + /// Create a new `GuardedFutureWriter` with the specified `accessor` and + /// `writer`. + pub fn new(accessor: A, writer: FutureWriter) -> Self { + Self { + writer: Some(writer), + accessor, + } + } - result.map(drop) + /// Wrapper for [`FutureWriter::write`]. + pub async fn write(mut self, value: T) -> bool + where + T: func::Lower + Send + Sync + 'static, + { + self.writer + .as_mut() + .unwrap() + .write_(&self.accessor, value) + .await + } + + /// Wrapper for [`FutureWriter::watch_reader`] + pub async fn watch_reader(&mut self) { + self.writer + .as_mut() + .unwrap() + .watch_reader(&self.accessor) + .await + } + + /// Extracts the underlying [`FutureWriter`] from this guard, returning it + /// back. + pub fn into_future(self) -> FutureWriter { + self.into() + } +} + +impl From> for FutureWriter +where + T: func::Lower + Send + Sync + 'static, + A: AsAccessor, +{ + fn from(mut guard: GuardedFutureWriter) -> Self { + guard.writer.take().unwrap() + } +} + +impl Drop for GuardedFutureWriter +where + T: func::Lower + Send + Sync + 'static, + A: AsAccessor, +{ + fn drop(&mut self) { + if let Some(writer) = &mut self.writer { + writer.close_with(&self.accessor) + } } } /// Represents the readable end of a Component Model `future`. /// -/// Note that `FutureReader` instances must be disposed of using either -/// `FutureReader::read` or `DropWithStore::drop`; otherwise the in-store -/// representation will leak and the writer end will hang indefinitely. -/// Consider using [`WithAccessor`] to ensure that disposal happens -/// automatically. +/// Note that `FutureReader` instances must be disposed of using either `read` +/// or `close`; otherwise the in-store representation will leak and the writer +/// end will hang indefinitely. Consider using [`GuardedFutureReader`] to +/// ensure that disposal happens automatically. pub struct FutureReader { instance: Instance, id: TableId, @@ -687,6 +631,13 @@ impl FutureReader { /// Panics if the store that the [`Accessor`] is derived from does not own /// this future. pub async fn read(self, accessor: impl AsAccessor) -> Option + where + T: func::Lift + Send + 'static, + { + self.guard(accessor).read().await + } + + async fn read_(&mut self, accessor: impl AsAccessor) -> Option where T: func::Lift + Send + 'static, { @@ -697,8 +648,6 @@ impl FutureReader { .host_read_async(accessor, self.id, None, TransmitKind::Future) .await; - _ = accessor.with(|store| self.drop(store)); - if let Ok(HostResult { mut buffer, dropped: false, @@ -776,15 +725,41 @@ impl FutureReader { _ => func::bad_type_info(), } } -} -impl DropWithStore for FutureReader { - fn drop(self, mut store: impl AsContextMut) -> Result<()> { - self.instance.host_drop_reader( - store.as_context_mut().0.traitobj_mut(), - self.id, - TransmitKind::Future, - ) + /// Close this `FutureReader`, writing the default value. + /// + /// # Panics + /// + /// Panics if the store that the [`Accessor`] is derived from does not own + /// this future. Usage of this future after calling `close` will also cause + /// a panic. + pub fn close(&mut self, mut store: impl AsContextMut) { + // `self` should never be used again, but leave an invalid handle there just in case. + let id = mem::replace(&mut self.id, TableId::new(0)); + self.instance + .host_drop_reader( + store.as_context_mut().0.traitobj_mut(), + id, + TransmitKind::Future, + ) + .unwrap(); + } + + /// Convenience method around [`Self::close`]. + pub fn close_with(&mut self, accessor: impl AsAccessor) { + accessor.as_accessor().with(|access| self.close(access)) + } + + /// Returns a [`GuardedFutureReader`] which will auto-close this future on + /// drop and clean it up from the store. + /// + /// Note that the `accessor` provided must own this future and is + /// additionally transferred to the `GuardedFutureReader` return value. + pub fn guard(self, accessor: A) -> GuardedFutureReader + where + A: AsAccessor, + { + GuardedFutureReader::new(accessor, self) } } @@ -887,12 +862,84 @@ unsafe impl func::Lift for FutureReader { } } +/// A [`FutureReader`] paired with an [`Accessor`]. +/// +/// This is an RAII wrapper around [`FutureReader`] that ensures it is closed +/// when dropped. This can be created through [`GuardedFutureReader::new`] or +/// [`FutureReader::guard`]. +pub struct GuardedFutureReader +where + A: AsAccessor, +{ + // This field is `None` to implement the conversion from this guard back to + // `FutureReader`. When `None` is seen in the destructor it will cause the + // destructor to do nothing. + reader: Option>, + accessor: A, +} + +impl GuardedFutureReader +where + A: AsAccessor, +{ + /// Create a new `GuardedFutureReader` with the specified `accessor` and `reader`. + pub fn new(accessor: A, reader: FutureReader) -> Self { + Self { + reader: Some(reader), + accessor, + } + } + + /// Wrapper for [`FutureReader::read`]. + pub async fn read(mut self) -> Option + where + T: func::Lift + Send + 'static, + { + self.reader.as_mut().unwrap().read_(&self.accessor).await + } + + /// Wrapper for [`FutureReader::watch_writer`]. + pub async fn watch_writer(&mut self) { + self.reader + .as_mut() + .unwrap() + .watch_writer(&self.accessor) + .await + } + + /// Extracts the underlying [`FutureReader`] from this guard, returning it + /// back. + pub fn into_future(self) -> FutureReader { + self.into() + } +} + +impl From> for FutureReader +where + A: AsAccessor, +{ + fn from(mut guard: GuardedFutureReader) -> Self { + guard.reader.take().unwrap() + } +} + +impl Drop for GuardedFutureReader +where + A: AsAccessor, +{ + fn drop(&mut self) { + if let Some(reader) = &mut self.reader { + reader.close_with(&self.accessor) + } + } +} + /// Represents the writable end of a Component Model `stream`. /// -/// Note that `StreamWriter` instances must be disposed of using -/// `DropWithStore::drop`; otherwise the in-store representation will leak and -/// the reader end will hang indefinitely. Consider using [`WithAccessor`] to -/// ensure that disposal happens automatically. +/// Note that `StreamWriter` instances must be disposed of using `close`; +/// otherwise the in-store representation will leak and the reader end will hang +/// indefinitely. Consider using [`GuardedStreamWriter`] to ensure that +/// disposal happens automatically. pub struct StreamWriter { instance: Instance, id: TableId, @@ -993,24 +1040,141 @@ impl StreamWriter { pub async fn watch_reader(&mut self, accessor: impl AsAccessor) { watch_reader(accessor, self.instance, self.id).await } + + /// Close this `StreamWriter`, writing the default value. + /// + /// # Panics + /// + /// Panics if the store that the [`Accessor`] is derived from does not own + /// this future. Usage of this future after calling `close` will also cause + /// a panic. + pub fn close(&mut self, mut store: impl AsContextMut) { + // `self` should never be used again, but leave an invalid handle there just in case. + let id = mem::replace(&mut self.id, TableId::new(0)); + self.instance + .host_drop_writer(store.as_context_mut(), id, None::<&dyn Fn() -> Result<()>>) + .unwrap() + } + + /// Convenience method around [`Self::close`]. + pub fn close_with(&mut self, accessor: impl AsAccessor) { + accessor.as_accessor().with(|access| self.close(access)) + } + + /// Returns a [`GuardedStreamWriter`] which will auto-close this stream on + /// drop and clean it up from the store. + /// + /// Note that the `accessor` provided must own this future and is + /// additionally transferred to the `GuardedStreamWriter` return value. + pub fn guard(self, accessor: A) -> GuardedStreamWriter + where + A: AsAccessor, + { + GuardedStreamWriter::new(accessor, self) + } } -impl DropWithStore for StreamWriter { - fn drop(self, mut store: impl AsContextMut) -> Result<()> { - self.instance.host_drop_writer( - store.as_context_mut().0.traitobj_mut(), - self.id, - TransmitKind::Stream, - ) +/// A [`StreamWriter`] paired with an [`Accessor`]. +/// +/// This is an RAII wrapper around [`StreamWriter`] that ensures it is closed +/// when dropped. This can be created through [`GuardedStreamWriter::new`] or +/// [`StreamWriter::guard`]. +pub struct GuardedStreamWriter +where + A: AsAccessor, +{ + // This field is `None` to implement the conversion from this guard back to + // `StreamWriter`. When `None` is seen in the destructor it will cause the + // destructor to do nothing. + writer: Option>, + accessor: A, +} + +impl GuardedStreamWriter +where + A: AsAccessor, +{ + /// Create a new `GuardedStreamWriter` with the specified `accessor` and `writer`. + pub fn new(accessor: A, writer: StreamWriter) -> Self { + Self { + writer: Some(writer), + accessor, + } + } + + /// Wrapper for [`StreamWriter::is_closed`]. + pub fn is_closed(&self) -> bool { + self.writer.as_ref().unwrap().is_closed() + } + + /// Wrapper for [`StreamWriter::write`]. + pub async fn write(&mut self, buffer: B) -> B + where + T: func::Lower + 'static, + B: WriteBuffer, + { + self.writer + .as_mut() + .unwrap() + .write(&self.accessor, buffer) + .await + } + + /// Wrapper for [`StreamWriter::write_all`]. + pub async fn write_all(&mut self, buffer: B) -> B + where + T: func::Lower + 'static, + B: WriteBuffer, + { + self.writer + .as_mut() + .unwrap() + .write_all(&self.accessor, buffer) + .await + } + + /// Wrapper for [`StreamWriter::watch_reader`]. + pub async fn watch_reader(&mut self) { + self.writer + .as_mut() + .unwrap() + .watch_reader(&self.accessor) + .await + } + + /// Extracts the underlying [`StreamWriter`] from this guard, returning it + /// back. + pub fn into_stream(self) -> StreamWriter { + self.into() + } +} + +impl From> for StreamWriter +where + A: AsAccessor, +{ + fn from(mut guard: GuardedStreamWriter) -> Self { + guard.writer.take().unwrap() + } +} + +impl Drop for GuardedStreamWriter +where + A: AsAccessor, +{ + fn drop(&mut self) { + if let Some(writer) = &mut self.writer { + writer.close_with(&self.accessor) + } } } /// Represents the readable end of a Component Model `stream`. /// -/// Note that `StreamReader` instances must be disposed of using -/// `DropWithStore::drop`; otherwise the in-store representation will leak and -/// the writer end will hang indefinitely. Consider using [`WithAccessor`] to -/// ensure that disposal happens automatically. +/// Note that `StreamReader` instances must be disposed of using `close`; +/// otherwise the in-store representation will leak and the writer end will hang +/// indefinitely. Consider using [`GuardedStreamReader`] to ensure that +/// disposal happens automatically. pub struct StreamReader { instance: Instance, id: TableId, @@ -1135,15 +1299,41 @@ impl StreamReader { _ => func::bad_type_info(), } } -} -impl DropWithStore for StreamReader { - fn drop(self, mut store: impl AsContextMut) -> Result<()> { - self.instance.host_drop_reader( - store.as_context_mut().0.traitobj_mut(), - self.id, - TransmitKind::Stream, - ) + /// Close this `StreamReader`, writing the default value. + /// + /// # Panics + /// + /// Panics if the store that the [`Accessor`] is derived from does not own + /// this future. Usage of this future after calling `close` will also cause + /// a panic. + pub fn close(&mut self, mut store: impl AsContextMut) { + // `self` should never be used again, but leave an invalid handle there just in case. + let id = mem::replace(&mut self.id, TableId::new(0)); + self.instance + .host_drop_reader( + store.as_context_mut().0.traitobj_mut(), + id, + TransmitKind::Stream, + ) + .unwrap() + } + + /// Convenience method around [`Self::close`]. + pub fn close_with(&mut self, accessor: impl AsAccessor) { + accessor.as_accessor().with(|access| self.close(access)) + } + + /// Returns a [`GuardedStreamReader`] which will auto-close this stream on + /// drop and clean it up from the store. + /// + /// Note that the `accessor` provided must own this future and is + /// additionally transferred to the `GuardedStreamReader` return value. + pub fn guard(self, accessor: A) -> GuardedStreamReader + where + A: AsAccessor, + { + GuardedStreamReader::new(accessor, self) } } @@ -1246,6 +1436,89 @@ unsafe impl func::Lift for StreamReader { } } +/// A [`StreamReader`] paired with an [`Accessor`]. +/// +/// This is an RAII wrapper around [`StreamReader`] that ensures it is closed +/// when dropped. This can be created through [`GuardedStreamReader::new`] or +/// [`StreamReader::guard`]. +pub struct GuardedStreamReader +where + A: AsAccessor, +{ + // This field is `None` to implement the conversion from this guard back to + // `StreamReader`. When `None` is seen in the destructor it will cause the + // destructor to do nothing. + reader: Option>, + accessor: A, +} + +impl GuardedStreamReader +where + A: AsAccessor, +{ + /// Create a new `GuardedStreamReader` with the specified `accessor` and + /// `reader`. + pub fn new(accessor: A, reader: StreamReader) -> Self { + Self { + reader: Some(reader), + accessor, + } + } + + /// Wrapper for `StreamReader::is_closed` + pub fn is_closed(&self) -> bool { + self.reader.as_ref().unwrap().is_closed() + } + + /// Wrapper for `StreamReader::read`. + pub async fn read(&mut self, buffer: B) -> B + where + T: func::Lift + 'static, + B: ReadBuffer + Send + 'static, + { + self.reader + .as_mut() + .unwrap() + .read(&self.accessor, buffer) + .await + } + + /// Wrapper for `StreamReader::watch_writer`. + pub async fn watch_writer(&mut self) { + self.reader + .as_mut() + .unwrap() + .watch_writer(&self.accessor) + .await + } + + /// Extracts the underlying [`StreamReader`] from this guard, returning it + /// back. + pub fn into_stream(self) -> StreamReader { + self.into() + } +} + +impl From> for StreamReader +where + A: AsAccessor, +{ + fn from(mut guard: GuardedStreamReader) -> Self { + guard.reader.take().unwrap() + } +} + +impl Drop for GuardedStreamReader +where + A: AsAccessor, +{ + fn drop(&mut self) { + if let Some(reader) = &mut self.reader { + reader.close_with(&self.accessor) + } + } +} + /// Represents a Component Model `error-context`. pub struct ErrorContext { rep: u32, @@ -1550,16 +1823,21 @@ enum Reader<'a> { impl Instance { /// Create a new Component Model `future` as pair of writable and readable ends, /// the latter of which may be passed to guest code. + /// + /// `default` is a callback to be used if the writable end of the future is + /// closed without having written a value. You may supply e.g. `|| + /// unreachable!()` if you're sure that won't happen. pub fn future( self, mut store: impl AsContextMut, + default: fn() -> T, ) -> Result<(FutureWriter, FutureReader)> { let (write, read) = self .concurrent_state_mut(store.as_context_mut().0) .new_transmit()?; Ok(( - FutureWriter::new(write, self), + FutureWriter::new(default, write, self), FutureReader::new(read, self), )) } @@ -1587,6 +1865,7 @@ impl Instance { id: TableId, mut buffer: B, kind: TransmitKind, + post_write: PostWrite, ) -> Result, oneshot::Receiver>>> { let transmit_id = self.concurrent_state_mut(store.0).get(id)?.state; let transmit = self @@ -1601,6 +1880,10 @@ impl Instance { ReadState::Open }; + if matches!(post_write, PostWrite::Drop) && !matches!(transmit.read, ReadState::Open) { + transmit.write = WriteState::Dropped; + } + Ok(match mem::replace(&mut transmit.read, new_state) { ReadState::Open => { assert!(matches!(&transmit.write, WriteState::Open)); @@ -1619,7 +1902,7 @@ impl Instance { _ = tx.send(result); Ok(code) }), - post_write: PostWrite::Continue, + post_write, }; self.concurrent_state_mut(store.0) .get_mut(transmit_id)? @@ -1642,34 +1925,58 @@ impl Instance { } let read_handle = transmit.read_handle; - let (result, code) = accept_reader::( - store.as_context_mut(), - self, - Reader::Guest { - options: &options, - ty, - address, - count, - }, - buffer, - kind, - )?; - - self.concurrent_state_mut(store.0).set_event( - read_handle.rep(), - match ty { - TableIndex::Future(ty) => Event::FutureRead { - code, - pending: Some((ty, handle)), + let accept = move |mut store: StoreContextMut| { + let (result, code) = accept_reader::( + store.as_context_mut(), + self, + Reader::Guest { + options: &options, + ty, + address, + count, }, - TableIndex::Stream(ty) => Event::StreamRead { - code, - pending: Some((ty, handle)), + buffer, + kind, + )?; + + self.concurrent_state_mut(store.0).set_event( + read_handle.rep(), + match ty { + TableIndex::Future(ty) => Event::FutureRead { + code, + pending: Some((ty, handle)), + }, + TableIndex::Stream(ty) => Event::StreamRead { + code, + pending: Some((ty, handle)), + }, }, - }, - )?; + )?; - Ok(result) + anyhow::Ok(result) + }; + + if T::MAY_REQUIRE_REALLOC { + // For payloads which may require a realloc call, use a + // oneshot::channel and background task. This is necessary + // because calling the guest while there are host embedder + // frames on the stack is unsound. + let (tx, rx) = oneshot::channel(); + let token = StoreToken::new(store.as_context_mut()); + self.concurrent_state_mut(store.0).push_high_priority( + WorkItem::WorkerFunction(Mutex::new(Box::new(move |store, _| { + _ = tx.send(accept(token.as_context_mut(store))?); + Ok(()) + }))), + ); + Err(rx) + } else { + // Optimize flat payloads (i.e. those which do not require + // calling the guest's realloc function) by lowering + // directly instead of using a oneshot::channel and + // background task. + Ok(accept(store)?) + } } ReadState::HostReady { accept } => { @@ -1704,10 +2011,15 @@ impl Instance { buffer: B, kind: TransmitKind, ) -> Result> { - match accessor - .as_accessor() - .with(move |mut access| self.host_write(access.as_context_mut(), id, buffer, kind))? - { + match accessor.as_accessor().with(move |mut access| { + self.host_write( + access.as_context_mut(), + id, + buffer, + kind, + PostWrite::Continue, + ) + })? { Ok(result) => Ok(result), Err(rx) => Ok(rx.await?), } @@ -1941,15 +2253,15 @@ impl Instance { } /// Drop the write end of a stream or future read from the host. - fn host_drop_writer( + fn host_drop_writer( self, - store: &mut dyn VMStore, + mut store: StoreContextMut, id: TableId, - kind: TransmitKind, + default: Option<&dyn Fn() -> Result>, ) -> Result<()> { - let transmit_id = self.concurrent_state_mut(store).get(id)?.state; + let transmit_id = self.concurrent_state_mut(store.0).get(id)?.state; let transmit = self - .concurrent_state_mut(store) + .concurrent_state_mut(store.0) .get_mut(transmit_id) .with_context(|| format!("error closing writer {transmit_id:?}"))?; log::trace!( @@ -1964,25 +2276,35 @@ impl Instance { // Existing queued transmits must be updated with information for the impending writer closure match &mut transmit.write { - WriteState::GuestReady { post_write, .. } => { - *post_write = PostWrite::Drop; + WriteState::GuestReady { .. } => { + unreachable!("can't call `host_drop_writer` on a guest-owned writer"); } WriteState::HostReady { post_write, .. } => { *post_write = PostWrite::Drop; } v @ WriteState::Open => { - if let (TransmitKind::Future, false) = ( - kind, + if let (Some(default), false) = ( + default, transmit.done || matches!(transmit.read, ReadState::Dropped), ) { - bail!("cannot drop future write end without first writing a value") + // This is a future, and we haven't written a value yet -- + // write the default value. + _ = self.host_write( + store.as_context_mut(), + id, + Some(default()?), + TransmitKind::Future, + PostWrite::Drop, + )?; + } else { + *v = WriteState::Dropped; } - - *v = WriteState::Dropped; } WriteState::Dropped => unreachable!("write state is already dropped"), } + let transmit = self.concurrent_state_mut(store.0).get_mut(transmit_id)?; + // If the existing read state is dropped, then there's nothing to read // and we can keep it that way. // @@ -1998,12 +2320,12 @@ impl Instance { // Swap in the new read state match mem::replace(&mut transmit.read, new_state) { - // If the guest was ready to read, then we cannot drop the reader (or writer) + // If the guest was ready to read, then we cannot drop the reader (or writer); // we must deliver the event, and update the state associated with the handle to // represent that a read must be performed ReadState::GuestReady { ty, handle, .. } => { // Ensure the final read of the guest is queued, with appropriate closure indicator - self.concurrent_state_mut(store).update_event( + self.concurrent_state_mut(store.0).update_event( read_handle.rep(), match ty { TableIndex::Future(ty) => Event::FutureRead { @@ -2026,14 +2348,14 @@ impl Instance { // If the read state is open, then there are no registered readers of the stream/future ReadState::Open => { - self.concurrent_state_mut(store).update_event( + self.concurrent_state_mut(store.0).update_event( read_handle.rep(), - match kind { - TransmitKind::Future => Event::FutureRead { + match default { + Some(_) => Event::FutureRead { code: ReturnCode::Dropped(0), pending: None, }, - TransmitKind::Stream => Event::StreamRead { + None => Event::StreamRead { code: ReturnCode::Dropped(0), pending: None, }, @@ -2045,7 +2367,7 @@ impl Instance { // (both writer and reader have been dropped) ReadState::Dropped => { log::trace!("host_drop_writer delete {transmit_id:?}"); - self.concurrent_state_mut(store) + self.concurrent_state_mut(store.0) .delete_transmit(transmit_id)?; } } @@ -2053,14 +2375,14 @@ impl Instance { } /// Drop the writable end of the specified stream or future from the guest. - fn guest_drop_writable( + pub(super) fn guest_drop_writable( self, - store: &mut dyn VMStore, + store: StoreContextMut, ty: TableIndex, writer: u32, ) -> Result<()> { let (transmit_rep, state) = self - .concurrent_state_mut(store) + .concurrent_state_mut(store.0) .state_table(ty) .remove_by_index(writer) .context("failed to find writer")?; @@ -2081,27 +2403,20 @@ impl Instance { let id = TableId::::new(transmit_rep); log::trace!("guest_drop_writable: drop writer {id:?}"); - self.host_drop_writer(store, id, kind) - } - - /// Implements the `future.drop-writable` intrinsic. - pub(crate) fn future_drop_writable( - self, - store: &mut dyn VMStore, - ty: TypeFutureTableIndex, - writer: u32, - ) -> Result<()> { - self.guest_drop_writable(store, TableIndex::Future(ty), writer) - } - - /// Implements the `stream.drop-writable` intrinsic. - pub(crate) fn stream_drop_writable( - self, - store: &mut dyn VMStore, - ty: TypeStreamTableIndex, - writer: u32, - ) -> Result<()> { - self.guest_drop_writable(store, TableIndex::Stream(ty), writer) + match kind { + TransmitKind::Stream => { + self.host_drop_writer(store, id, None::<&dyn Fn() -> Result<()>>) + } + TransmitKind::Future => self.host_drop_writer( + store, + id, + Some(&|| { + Err::<(), _>(anyhow!( + "cannot drop future write end without first writing a value" + )) + }), + ), + } } /// Copy `count` items from `read_address` to `write_address` for the @@ -2244,40 +2559,22 @@ impl Instance { } /// Write to the specified stream or future from the guest. - /// - /// SAFETY: `memory` and `realloc` must be valid pointers to their - /// respective guest entities. - pub(super) unsafe fn guest_write( + pub(super) fn guest_write( self, mut store: StoreContextMut, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: u8, - async_: bool, ty: TableIndex, + options: OptionsIndex, flat_abi: Option, handle: u32, address: u32, count: u32, ) -> Result { - if !async_ { - bail!("synchronous stream and future writes not yet supported"); - } - let address = usize::try_from(address).unwrap(); let count = usize::try_from(count).unwrap(); - // SAFETY: Per this function's contract, `memory` and `realloc` are - // valid. - let options = unsafe { - Options::new( - store.0.store_opaque().id(), - NonNull::new(memory), - NonNull::new(realloc), - StringEncoding::from_u8(string_encoding).unwrap(), - true, - None, - ) - }; + let options = Options::new_index(store.0, self, options); + if !options.async_() { + bail!("synchronous stream and future writes not yet supported"); + } let concurrent_state = self.concurrent_state_mut(store.0); let (rep, state) = concurrent_state.get_mut_by_index(ty, handle)?; let StreamFutureState::Write { done } = *state else { @@ -2479,39 +2776,21 @@ impl Instance { } /// Read from the specified stream or future from the guest. - /// - /// SAFETY: `memory` and `realloc` must be valid pointers to their - /// respective guest entities. - pub(super) unsafe fn guest_read( + pub(super) fn guest_read( self, mut store: StoreContextMut, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: u8, - async_: bool, ty: TableIndex, + options: OptionsIndex, flat_abi: Option, handle: u32, address: u32, count: u32, ) -> Result { - if !async_ { + let address = usize::try_from(address).unwrap(); + let options = Options::new_index(store.0, self, options); + if !options.async_() { bail!("synchronous stream and future reads not yet supported"); } - - let address = usize::try_from(address).unwrap(); - // SAFETY: Per this function's contract, `memory` and `realloc` must be - // valid. - let options = unsafe { - Options::new( - store.0.store_opaque().id(), - NonNull::new(memory), - NonNull::new(realloc), - StringEncoding::from_u8(string_encoding).unwrap(), - true, - None, - ) - }; let concurrent_state = self.concurrent_state_mut(store.0); let (rep, state) = concurrent_state.get_mut_by_index(ty, handle)?; let StreamFutureState::Read { done } = *state else { @@ -2736,33 +3015,15 @@ impl Instance { } /// Create a new error context for the given component. - /// - /// SAFETY: `memory` and `realloc` must be valid pointers to their - /// respective guest entities. - pub(crate) unsafe fn error_context_new( + pub(crate) fn error_context_new( self, store: &mut StoreOpaque, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: u8, ty: TypeComponentLocalErrorContextTableIndex, + options: OptionsIndex, debug_msg_address: u32, debug_msg_len: u32, ) -> Result { - // SAFETY: Per this function's contract, `memory` and `realloc` must be - // valid. - let options = unsafe { - Options::new( - store.id(), - NonNull::new(memory), - NonNull::new(realloc), - StringEncoding::from_u8(string_encoding).ok_or_else(|| { - anyhow::anyhow!("failed to convert u8 string encoding [{string_encoding}]") - })?, - false, - None, - ) - }; + let options = Options::new_index(store, self, options); let lift_ctx = &mut LiftContext::new(store, &options, self); // Read string from guest memory let address = usize::try_from(debug_msg_address)?; @@ -2808,21 +3069,15 @@ impl Instance { } /// Retrieve the debug message from the specified error context. - /// - /// SAFETY: `memory` and `realloc` must be valid pointers to their - /// respective guest entities. - pub(super) unsafe fn error_context_debug_message( + pub(super) fn error_context_debug_message( self, store: StoreContextMut, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: u8, ty: TypeComponentLocalErrorContextTableIndex, + options: OptionsIndex, err_ctx_handle: u32, debug_msg_address: u32, ) -> Result<()> { // Retrieve the error context and internal debug message - let id = store.0.store_opaque().id(); let state = self.concurrent_state_mut(store.0); let (state_table_id_rep, _) = state .error_context_tables @@ -2835,20 +3090,7 @@ impl Instance { state.get_mut(TableId::::new(state_table_id_rep))?; let debug_msg = debug_msg.clone(); - // SAFETY: Per this function's contract, `memory` and `realloc` are - // valid. - let options = unsafe { - Options::new( - id, - NonNull::new(memory), - NonNull::new(realloc), - StringEncoding::from_u8(string_encoding).ok_or_else(|| { - anyhow::anyhow!("failed to convert u8 string encoding [{string_encoding}]") - })?, - false, - None, - ) - }; + let options = Options::new_index(store.0, self, options); let types = self.id().get(store.0).component().types().clone(); let lower_cx = &mut LowerContext::new(store, &options, &types, self); let debug_msg_address = usize::try_from(debug_msg_address)?; diff --git a/crates/wasmtime/src/runtime/component/concurrent/table.rs b/crates/wasmtime/src/runtime/component/concurrent/table.rs index d46a0dfe70..382eb07e6a 100644 --- a/crates/wasmtime/src/runtime/component/concurrent/table.rs +++ b/crates/wasmtime/src/runtime/component/concurrent/table.rs @@ -13,7 +13,7 @@ use std::collections::BTreeSet; use std::fmt; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; -use std::vec::{self, Vec}; +use std::vec::Vec; pub struct TableId { rep: u32, @@ -356,32 +356,12 @@ impl Table { } Ok(e) } -} - -pub struct TableIterator(vec::IntoIter); - -impl Iterator for TableIterator { - type Item = Box; - fn next(&mut self) -> Option { - loop { - if let Some(entry) = self.0.next() { - if let Entry::Occupied { entry } = entry { - break Some(entry.entry); - } - } else { - break None; - } - } - } -} - -impl IntoIterator for Table { - type Item = Box; - type IntoIter = TableIterator; - - fn into_iter(self) -> TableIterator { - TableIterator(self.entries.into_iter()) + pub fn iter_mut(&mut self) -> impl Iterator { + self.entries.iter_mut().filter_map(|entry| match entry { + Entry::Occupied { entry } => Some(&mut *entry.entry), + Entry::Free { .. } => None, + }) } } diff --git a/crates/wasmtime/src/runtime/component/func.rs b/crates/wasmtime/src/runtime/component/func.rs index 15f32a56e9..ad97995acf 100644 --- a/crates/wasmtime/src/runtime/component/func.rs +++ b/crates/wasmtime/src/runtime/component/func.rs @@ -11,16 +11,12 @@ use crate::{AsContext, AsContextMut, StoreContextMut, ValRaw}; use core::mem::{self, MaybeUninit}; use core::ptr::NonNull; use wasmtime_environ::component::{ - CanonicalOptions, CanonicalOptionsDataModel, ExportIndex, InterfaceType, MAX_FLAT_PARAMS, - MAX_FLAT_RESULTS, TypeFuncIndex, TypeTuple, + CanonicalOptions, ExportIndex, InterfaceType, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, TypeFuncIndex, + TypeTuple, }; #[cfg(feature = "component-model-async")] use crate::component::concurrent::{self, AsAccessor, PreparedCall}; -#[cfg(feature = "component-model-async")] -use core::future::Future; -#[cfg(feature = "component-model-async")] -use core::pin::Pin; mod host; mod options; @@ -280,36 +276,51 @@ impl Func { results: &mut [Val], ) -> Result<()> { let mut store = store.as_context_mut(); - assert!( - store.0.async_support(), - "cannot use `call_async` without enabling async support in the config" - ); + #[cfg(feature = "component-model-async")] { - let future = - self.call_concurrent_dynamic(store.as_context_mut(), params.to_vec(), false); - let run_results = self - .instance - .run_concurrent(store, async |_| future.await) - .await??; - assert_eq!(run_results.len(), results.len()); - for (result, slot) in run_results.into_iter().zip(results) { - *slot = result; - } - Ok(()) + self.instance + .run_concurrent(&mut store, async |store| { + self.call_concurrent_dynamic(store, params, results, false) + .await + }) + .await? } #[cfg(not(feature = "component-model-async"))] { + assert!( + store.0.async_support(), + "cannot use `call_async` without enabling async support in the config" + ); store .on_fiber(|store| self.call_impl(store, params, results)) .await? } } - fn check_param_count(&self, store: StoreContextMut, count: usize) -> Result<()> { + fn check_params_results( + &self, + store: StoreContextMut, + params: &[Val], + results: &mut [Val], + ) -> Result<()> { let param_tys = self.params(&store); - if param_tys.len() != count { - bail!("expected {} argument(s), got {count}", param_tys.len()); + if param_tys.len() != params.len() { + bail!( + "expected {} argument(s), got {}", + param_tys.len(), + params.len(), + ); + } + + let result_tys = self.results(&store); + + if result_tys.len() != results.len() { + bail!( + "expected {} result(s), got {}", + result_tys.len(), + results.len(), + ); } Ok(()) @@ -332,40 +343,43 @@ impl Func { pub async fn call_concurrent( self, accessor: impl AsAccessor, - params: Vec, - ) -> Result> { - let accessor = accessor.as_accessor(); - let result = accessor.with(|mut access| { - let store = access.as_context_mut(); - assert!( - store.0.async_support(), - "cannot use `call_concurrent` when async support is not enabled on the config" - ); - - self.call_concurrent_dynamic(store, params, true) - }); - result.await + params: &[Val], + results: &mut [Val], + ) -> Result<()> { + self.call_concurrent_dynamic(accessor.as_accessor(), params, results, true) + .await } /// Internal helper function for `call_async` and `call_concurrent`. #[cfg(feature = "component-model-async")] - fn call_concurrent_dynamic<'a, T: Send + 'static>( + async fn call_concurrent_dynamic( self, - mut store: StoreContextMut<'a, T>, - params: Vec, + store: impl AsAccessor, + params: &[Val], + results: &mut [Val], call_post_return_automatically: bool, - ) -> Pin>> + Send + 'static>> { - let result = (|| { - self.check_param_count(store.as_context_mut(), params.len())?; + ) -> Result<()> { + let store = store.as_accessor(); + let result = store.with(|mut store| { + assert!( + store.as_context_mut().0.async_support(), + "cannot use `call_concurrent` when async support is not enabled on the config" + ); + self.check_params_results(store.as_context_mut(), params, results)?; let prepared = self.prepare_call_dynamic( store.as_context_mut(), - params, + params.to_vec(), call_post_return_automatically, )?; - concurrent::queue_call(store, prepared) - })(); + concurrent::queue_call(store.as_context_mut(), prepared) + })?; - Box::pin(async move { result?.await }) + let run_results = result.await?; + assert_eq!(run_results.len(), results.len()); + for (result, slot) in run_results.into_iter().zip(results) { + *slot = result; + } + Ok(()) } /// Calls `concurrent::prepare_call` with monomorphized functions for @@ -412,17 +426,7 @@ impl Func { ) -> Result<()> { let mut store = store.as_context_mut(); - self.check_param_count(store.as_context_mut(), params.len())?; - - let result_tys = self.results(&store); - - if result_tys.len() != results.len() { - bail!( - "expected {} result(s), got {}", - result_tys.len(), - results.len() - ); - } + self.check_params_results(store.as_context_mut(), params, results)?; if self.abi_async(store.0) { unreachable!( @@ -484,14 +488,17 @@ impl Func { pub(crate) fn post_return_core_func(&self, store: &StoreOpaque) -> Option> { let instance = self.instance.id().get(store); - let (_ty, _def, options) = instance.component().export_lifted_function(self.index); - options.post_return.map(|i| instance.runtime_post_return(i)) + let component = instance.component(); + let (_ty, _def, options) = component.export_lifted_function(self.index); + let post_return = component.env_component().options[options].post_return; + post_return.map(|i| instance.runtime_post_return(i)) } pub(crate) fn abi_async(&self, store: &StoreOpaque) -> bool { let instance = self.instance.id().get(store); - let (_ty, _def, options) = instance.component().export_lifted_function(self.index); - options.async_ + let component = instance.component(); + let (_ty, _def, options) = component.export_lifted_function(self.index); + component.env_component().options[options].async_ } pub(crate) fn abi_info<'a>( @@ -499,28 +506,16 @@ impl Func { store: &'a StoreOpaque, ) -> (Options, InstanceFlags, TypeFuncIndex, &'a CanonicalOptions) { let vminstance = self.instance.id().get(store); - let (ty, _def, raw_options) = vminstance.component().export_lifted_function(self.index); - let mem_opts = match raw_options.data_model { - CanonicalOptionsDataModel::Gc {} => todo!("CM+GC"), - CanonicalOptionsDataModel::LinearMemory(opts) => opts, - }; - let memory = mem_opts - .memory - .map(|i| NonNull::new(vminstance.runtime_memory(i)).unwrap()); - let realloc = mem_opts.realloc.map(|i| vminstance.runtime_realloc(i)); - let flags = vminstance.instance_flags(raw_options.instance); - let callback = raw_options.callback.map(|i| vminstance.runtime_callback(i)); - let options = unsafe { - Options::new( - store.id(), - memory, - realloc, - raw_options.string_encoding, - raw_options.async_, - callback, - ) - }; - (options, flags, ty, raw_options) + let component = vminstance.component(); + let (ty, _def, options_index) = component.export_lifted_function(self.index); + let raw_options = &component.env_component().options[options_index]; + let options = Options::new_index(store, self.instance, options_index); + ( + options, + vminstance.instance_flags(raw_options.instance), + ty, + raw_options, + ) } /// Invokes the underlying wasm function, lowering arguments and lifting the @@ -696,9 +691,11 @@ impl Func { let index = self.index; let vminstance = self.instance.id().get(store.0); - let (_ty, _def, options) = vminstance.component().export_lifted_function(index); + let component = vminstance.component(); + let (_ty, _def, options) = component.export_lifted_function(index); let post_return = self.post_return_core_func(store.0); - let mut flags = vminstance.instance_flags(options.instance); + let mut flags = + vminstance.instance_flags(component.env_component().options[options].instance); let mut instance = self.instance.id().get_mut(store.0); let post_return_arg = instance.as_mut().post_return_arg_take(index); diff --git a/crates/wasmtime/src/runtime/component/func/host.rs b/crates/wasmtime/src/runtime/component/func/host.rs index d52364c0e0..d858a71c13 100644 --- a/crates/wasmtime/src/runtime/component/func/host.rs +++ b/crates/wasmtime/src/runtime/component/func/host.rs @@ -6,11 +6,9 @@ use crate::component::storage::slice_to_storage_mut; use crate::component::{ComponentNamedList, ComponentType, Instance, Lift, Lower, Val}; use crate::prelude::*; use crate::runtime::vm::component::{ - ComponentInstance, InstanceFlags, VMComponentContext, VMLowering, VMLoweringCallee, -}; -use crate::runtime::vm::{ - SendSyncPtr, VMFuncRef, VMGlobalDefinition, VMMemoryDefinition, VMOpaqueContext, VMStore, + ComponentInstance, VMComponentContext, VMLowering, VMLoweringCallee, }; +use crate::runtime::vm::{SendSyncPtr, VMOpaqueContext, VMStore}; use crate::{AsContextMut, CallHook, StoreContextMut, ValRaw}; use alloc::sync::Arc; use core::any::Any; @@ -20,7 +18,7 @@ use core::pin::Pin; use core::ptr::NonNull; use wasmtime_environ::component::{ CanonicalAbiInfo, ComponentTypes, InterfaceType, MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS, - MAX_FLAT_RESULTS, RuntimeComponentInstanceIndex, StringEncoding, TypeFuncIndex, TypeTuple, + MAX_FLAT_RESULTS, OptionsIndex, TypeFuncIndex, TypeTuple, }; pub struct HostFunc { @@ -92,12 +90,7 @@ impl HostFunc { cx: NonNull, data: NonNull, ty: u32, - caller_instance: u32, - flags: NonNull, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: u8, - async_: u8, + options: u32, storage: NonNull>, storage_len: usize, ) -> bool @@ -114,12 +107,7 @@ impl HostFunc { store, instance, TypeFuncIndex::from_u32(ty), - RuntimeComponentInstanceIndex::from_u32(caller_instance), - InstanceFlags::from_raw(flags), - memory, - realloc, - StringEncoding::from_u8(string_encoding).unwrap(), - async_ != 0, + OptionsIndex::from_u32(options), NonNull::slice_from_raw_parts(storage, storage_len).as_mut(), move |store, instance, args| (*data.as_ptr())(store, instance, args), ) @@ -241,12 +229,7 @@ unsafe fn call_host( mut store: StoreContextMut<'_, T>, instance: Instance, ty: TypeFuncIndex, - caller_instance: RuntimeComponentInstanceIndex, - mut flags: InstanceFlags, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: StringEncoding, - async_: bool, + options_idx: OptionsIndex, storage: &mut [MaybeUninit], closure: F, ) -> Result<()> @@ -255,23 +238,21 @@ where Params: Lift, Return: Lower + 'static, { - let options = Options::new( - store.0.store_opaque().id(), - NonNull::new(memory), - NonNull::new(realloc), - string_encoding, - async_, - None, - ); + let options = Options::new_index(store.0, instance, options_idx); + let vminstance = instance.id().get(store.0); + let opts = &vminstance.component().env_component().options[options_idx]; + let async_ = opts.async_; + let caller_instance = opts.instance; + let mut flags = vminstance.instance_flags(caller_instance); // Perform a dynamic check that this instance can indeed be left. Exiting // the component is disallowed, for example, when the `realloc` function // calls a canonical import. - if !flags.may_leave() { + if unsafe { !flags.may_leave() } { bail!("cannot leave component instance"); } - let types = instance.id().get(store.0).component().types().clone(); + let types = vminstance.component().types().clone(); let ty = &types[ty]; let param_tys = InterfaceType::Tuple(ty.params); let result_tys = InterfaceType::Tuple(ty.results); @@ -279,7 +260,7 @@ where if async_ { #[cfg(feature = "component-model-async")] { - let mut storage = Storage::<'_, Params, u32>::new_async::(storage); + let mut storage = unsafe { Storage::<'_, Params, u32>::new_async::(storage) }; // Lift the parameters, either from flat storage or from linear // memory. @@ -309,10 +290,14 @@ where let mut lower_result = { let types = types.clone(); move |store: StoreContextMut, instance: Instance, ret: Return| { - flags.set_may_leave(false); + unsafe { + flags.set_may_leave(false); + } let mut lower = LowerContext::new(store, &options, &types, instance); ret.linear_lower_to_memory(&mut lower, result_tys, retptr)?; - flags.set_may_leave(true); + unsafe { + flags.set_may_leave(true); + } lower.exit_call()?; Ok(()) } @@ -349,7 +334,7 @@ where ); } } else { - let mut storage = Storage::<'_, Params, Return>::new_sync(storage); + let mut storage = unsafe { Storage::<'_, Params, Return>::new_sync(storage) }; let mut lift = LiftContext::new(store.0.store_opaque_mut(), &options, instance); lift.enter_call(); let params = storage.lift_params(&mut lift, param_tys)?; @@ -362,10 +347,14 @@ where } }; - flags.set_may_leave(false); + unsafe { + flags.set_may_leave(false); + } let mut lower = LowerContext::new(store, &options, &types, instance); storage.lower_results(&mut lower, result_tys, ret)?; - flags.set_may_leave(true); + unsafe { + flags.set_may_leave(true); + } lower.exit_call()?; } @@ -691,29 +680,26 @@ unsafe fn call_host_and_handle_result( where T: 'static, { - let cx = VMComponentContext::from_opaque(cx); - ComponentInstance::from_vmctx(cx, |store, instance| { - let mut store = store.unchecked_context_mut(); - - crate::runtime::vm::catch_unwind_and_record_trap(|| { - store.0.call_hook(CallHook::CallingHost)?; - let res = func(store.as_context_mut(), instance); - store.0.call_hook(CallHook::ReturningFromHost)?; - res + let cx = unsafe { VMComponentContext::from_opaque(cx) }; + unsafe { + ComponentInstance::from_vmctx(cx, |store, instance| { + let mut store = store.unchecked_context_mut(); + + crate::runtime::vm::catch_unwind_and_record_trap(|| { + store.0.call_hook(CallHook::CallingHost)?; + let res = func(store.as_context_mut(), instance); + store.0.call_hook(CallHook::ReturningFromHost)?; + res + }) }) - }) + } } unsafe fn call_host_dynamic( mut store: StoreContextMut<'_, T>, instance: Instance, ty: TypeFuncIndex, - caller_instance: RuntimeComponentInstanceIndex, - mut flags: InstanceFlags, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: StringEncoding, - async_: bool, + options_idx: OptionsIndex, storage: &mut [MaybeUninit], closure: F, ) -> Result<()> @@ -729,19 +715,17 @@ where + 'static, T: 'static, { - let options = Options::new( - store.0.store_opaque().id(), - NonNull::new(memory), - NonNull::new(realloc), - string_encoding, - async_, - None, - ); + let options = Options::new_index(store.0, instance, options_idx); + let vminstance = instance.id().get(store.0); + let opts = &vminstance.component().env_component().options[options_idx]; + let async_ = opts.async_; + let caller_instance = opts.instance; + let mut flags = vminstance.instance_flags(caller_instance); // Perform a dynamic check that this instance can indeed be left. Exiting // the component is disallowed, for example, when the `realloc` function // calls a canonical import. - if !flags.may_leave() { + if unsafe { !flags.may_leave() } { bail!("cannot leave component instance"); } @@ -759,14 +743,16 @@ where MAX_FLAT_PARAMS }; - let ret_index = dynamic_params_load( - &mut lift, - &types, - storage, - param_tys, - &mut params_and_results, - max_flat, - )?; + let ret_index = unsafe { + dynamic_params_load( + &mut lift, + &types, + storage, + param_tys, + &mut params_and_results, + max_flat, + )? + }; let result_start = params_and_results.len(); for _ in 0..result_tys.types.len() { params_and_results.push(Val::Bool(false)); @@ -778,7 +764,7 @@ where let retptr = if result_tys.types.len() == 0 { 0 } else { - let retptr = storage[ret_index].assume_init(); + let retptr = unsafe { storage[ret_index].assume_init() }; let mut lower = LowerContext::new(store.as_context_mut(), &options, &types, instance); validate_inbounds_dynamic(&result_tys.abi, lower.as_slice_mut(), &retptr)? @@ -799,7 +785,9 @@ where let result_vals = &result_vals[result_start..]; assert_eq!(result_vals.len(), result_tys.types.len()); - flags.set_may_leave(false); + unsafe { + flags.set_may_leave(false); + } let mut lower = LowerContext::new(store, &options, &types, instance); let mut ptr = retptr; @@ -808,7 +796,9 @@ where val.store(&mut lower, *ty, offset)?; } - flags.set_may_leave(true); + unsafe { + flags.set_may_leave(true); + } lower.exit_call()?; @@ -842,7 +832,9 @@ where instance.poll_and_block(store.0.traitobj_mut(), future, caller_instance)?; let result_vals = &result_vals[result_start..]; - flags.set_may_leave(false); + unsafe { + flags.set_may_leave(false); + } let mut cx = LowerContext::new(store, &options, &types, instance); if let Some(cnt) = result_tys.abi.flat_count(MAX_FLAT_RESULTS) { @@ -852,7 +844,7 @@ where } assert!(dst.next().is_none()); } else { - let ret_ptr = storage[ret_index].assume_init_ref(); + let ret_ptr = unsafe { storage[ret_index].assume_init_ref() }; let mut ptr = validate_inbounds_dynamic(&result_tys.abi, cx.as_slice_mut(), ret_ptr)?; for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) { let offset = types.canonical_abi(ty).next_field32_size(&mut ptr); @@ -860,7 +852,9 @@ where } } - flags.set_may_leave(true); + unsafe { + flags.set_may_leave(true); + } cx.exit_call()?; } @@ -932,12 +926,7 @@ extern "C" fn dynamic_entrypoint( cx: NonNull, data: NonNull, ty: u32, - caller_instance: u32, - flags: NonNull, - memory: *mut VMMemoryDefinition, - realloc: *mut VMFuncRef, - string_encoding: u8, - async_: u8, + options: u32, storage: NonNull>, storage_len: usize, ) -> bool @@ -960,12 +949,7 @@ where store, instance, TypeFuncIndex::from_u32(ty), - RuntimeComponentInstanceIndex::from_u32(caller_instance), - InstanceFlags::from_raw(flags), - memory, - realloc, - StringEncoding::from_u8(string_encoding).unwrap(), - async_ != 0, + OptionsIndex::from_u32(options), NonNull::slice_from_raw_parts(storage, storage_len).as_mut(), move |store, instance, params, results| { (*data.as_ptr())(store, instance, params, results) diff --git a/crates/wasmtime/src/runtime/component/func/options.rs b/crates/wasmtime/src/runtime/component/func/options.rs index 8759b8b50c..eaa6b27cd7 100644 --- a/crates/wasmtime/src/runtime/component/func/options.rs +++ b/crates/wasmtime/src/runtime/component/func/options.rs @@ -11,7 +11,10 @@ use crate::{FuncType, StoreContextMut}; use alloc::sync::Arc; use core::pin::Pin; use core::ptr::NonNull; -use wasmtime_environ::component::{ComponentTypes, StringEncoding, TypeResourceTableIndex}; +use wasmtime_environ::component::{ + CanonicalOptions, CanonicalOptionsDataModel, ComponentTypes, OptionsIndex, StringEncoding, + TypeResourceTableIndex, +}; /// Runtime representation of canonical ABI options in the component model. /// @@ -61,24 +64,33 @@ unsafe impl Sync for Options {} impl Options { // FIXME(#4311): prevent a ctor where the memory is memory64 - /// Creates a new set of options with the specified components. + /// Creates a new [`Options`] from the given [`OptionsIndex`] belonging to + /// the specified [`Instance`] /// - /// # Unsafety + /// # Panics /// - /// This is unsafety as there is no way to statically verify the validity of - /// the arguments. For example pointers must be valid pointers, the - /// `StoreId` must be valid for the pointers, etc. - pub unsafe fn new( - store_id: StoreId, - memory: Option>, - realloc: Option>, - string_encoding: StringEncoding, - async_: bool, - callback: Option>, - ) -> Options { + /// Panics if `instance` is not owned by `store` or if `index` is not valid + /// for `instance`'s component. + pub fn new_index(store: &StoreOpaque, instance: Instance, index: OptionsIndex) -> Options { + let instance = instance.id().get(store); + let CanonicalOptions { + string_encoding, + async_, + callback, + ref data_model, + .. + } = instance.component().env_component().options[index]; + let (memory, realloc) = match data_model { + CanonicalOptionsDataModel::Gc { .. } => (None, None), + CanonicalOptionsDataModel::LinearMemory(o) => (o.memory, o.realloc), + }; + let memory = memory.map(|i| NonNull::new(instance.runtime_memory(i)).unwrap()); + let realloc = realloc.map(|i| instance.runtime_realloc(i)); + let callback = callback.map(|i| instance.runtime_callback(i)); let _ = callback; + Options { - store_id, + store_id: store.id(), memory, realloc, string_encoding, @@ -218,6 +230,9 @@ pub struct LowerContext<'a, T: 'static> { /// Index of the component instance that's being lowered into. instance: Instance, + + /// Whether to allow `options.realloc` to be used when lowering. + allow_realloc: bool, } #[doc(hidden)] @@ -228,12 +243,43 @@ impl<'a, T: 'static> LowerContext<'a, T> { options: &'a Options, types: &'a ComponentTypes, instance: Instance, + ) -> LowerContext<'a, T> { + #[cfg(all(debug_assertions, feature = "component-model-async"))] + if store.engine().config().async_support { + // Assert that we're running on a fiber, which is necessary in + // case we call the guest's realloc function. + store.0.with_blocking(|_, _| {}); + } + LowerContext { + store, + options, + types, + instance, + allow_realloc: true, + } + } + + /// Like `new`, except disallows use of `options.realloc`. + /// + /// The returned object will panic if its `realloc` method is called. + /// + /// This is meant for use when lowering "flat" values (i.e. values which + /// require no allocations) into already-allocated memory or into stack + /// slots, in which case the lowering may safely be done outside of a fiber + /// since there is no need to make any guest calls. + #[cfg(feature = "component-model-async")] + pub(crate) fn new_without_realloc( + store: StoreContextMut<'a, T>, + options: &'a Options, + types: &'a ComponentTypes, + instance: Instance, ) -> LowerContext<'a, T> { LowerContext { store, options, types, instance, + allow_realloc: false, } } @@ -271,6 +317,8 @@ impl<'a, T: 'static> LowerContext<'a, T> { old_align: u32, new_size: usize, ) -> Result { + assert!(self.allow_realloc); + let realloc_func_ty = Arc::clone(self.instance().component().realloc_func_ty()); self.options .realloc( diff --git a/crates/wasmtime/src/runtime/component/func/typed.rs b/crates/wasmtime/src/runtime/component/func/typed.rs index 7483f325f6..3e669ab278 100644 --- a/crates/wasmtime/src/runtime/component/func/typed.rs +++ b/crates/wasmtime/src/runtime/component/func/typed.rs @@ -687,6 +687,16 @@ pub unsafe trait ComponentType: Send + Sync { #[doc(hidden)] const IS_RUST_UNIT_TYPE: bool = false; + /// Whether this type might require a call to the guest's realloc function + /// to allocate linear memory when lowering (e.g. a non-empty `string`). + /// + /// If this is `false`, Wasmtime may optimize lowering by using + /// `LowerContext::new_without_realloc` and lowering values outside of any + /// fiber. That will panic if the lowering process ends up needing realloc + /// after all, so `true` is a conservative default. + #[doc(hidden)] + const MAY_REQUIRE_REALLOC: bool = true; + /// Returns the number of core wasm abi values will be used to represent /// this type in its lowered form. /// @@ -1016,6 +1026,8 @@ macro_rules! integers { const ABI: CanonicalAbiInfo = CanonicalAbiInfo::$abi; + const MAY_REQUIRE_REALLOC: bool = false; + fn typecheck(ty: &InterfaceType, _types: &InstanceType<'_>) -> Result<()> { match ty { InterfaceType::$ty => Ok(()), @@ -2404,8 +2416,8 @@ pub unsafe fn lower_payload( let typed = typed_payload(payload); lower(typed)?; - let typed_len = storage_as_slice(typed).len(); - let payload = storage_as_slice_mut(payload); + let typed_len = unsafe { storage_as_slice(typed).len() }; + let payload = unsafe { storage_as_slice_mut(payload) }; for slot in payload[typed_len..].iter_mut() { slot.write(ValRaw::u64(0)); } diff --git a/crates/wasmtime/src/runtime/component/mod.rs b/crates/wasmtime/src/runtime/component/mod.rs index 3af50f9376..9219fd8513 100644 --- a/crates/wasmtime/src/runtime/component/mod.rs +++ b/crates/wasmtime/src/runtime/component/mod.rs @@ -119,9 +119,10 @@ mod values; pub use self::component::{Component, ComponentExportIndex}; #[cfg(feature = "component-model-async")] pub use self::concurrent::{ - AbortHandle, Access, Accessor, AccessorTask, AsAccessor, DropWithStore, DropWithStoreAndValue, - ErrorContext, FutureReader, FutureWriter, ReadBuffer, StreamReader, StreamWriter, - VMComponentAsyncStore, VecBuffer, WithAccessor, WithAccessorAndValue, WriteBuffer, + AbortHandle, Access, Accessor, AccessorTask, AsAccessor, ErrorContext, FutureReader, + FutureWriter, GuardedFutureReader, GuardedFutureWriter, GuardedStreamReader, + GuardedStreamWriter, ReadBuffer, StreamReader, StreamWriter, VMComponentAsyncStore, VecBuffer, + WriteBuffer, }; pub use self::func::{ ComponentNamedList, ComponentType, Func, Lift, Lower, TypedFunc, WasmList, WasmStr, @@ -162,8 +163,6 @@ pub mod __internal { pub use core::cell::RefCell; pub use core::future::Future; pub use core::mem::transmute; - #[cfg(feature = "async")] - pub use trait_variant::make as trait_variant_make; pub use wasmtime_environ; pub use wasmtime_environ::component::{CanonicalAbiInfo, ComponentTypes, InterfaceType}; } @@ -260,60 +259,113 @@ pub(crate) use self::store::ComponentStoreData; /// } /// ", /// -/// // Add calls to `tracing::span!` before each import or export is called -/// // to log most arguments and return values. By default values -/// // containing lists are excluded; enable `verbose_tracing` to include -/// // them. +/// // Further configuration of imported functions. This can be used to add +/// // functionality per-function or by default for all imports. Note that +/// // exports are also supported via the `exports` key below. /// // -/// // This option defaults to `false`. -/// tracing: true, -/// -/// // Include all arguments and return values in the tracing output, -/// // including values containing lists, which may be very large. +/// // Functions in this list are specified as their interface first then +/// // the raw wasm name of the function. Interface versions can be +/// // optionally omitted and prefixes are also supported to configure +/// // entire interfaces at once for example. Only the first matching item +/// // in this list is used to configure a function. /// // -/// // This option defaults to `false`. -/// verbose_tracing: false, -/// -/// // Imports will be async functions and exports -/// // are also invoked as async functions. Requires `Config::async_support` -/// // to be `true`. +/// // Configuration for a function is a set of flags which can be added +/// // per-function. Each flag's meaning is documented below and the final +/// // set of flags for a function are calculated by the first matching +/// // rule below unioned with the default flags inferred from the WIT +/// // signature itself (unless below configures the `ignore_wit` flag). /// // -/// // Note that this is only async for the host as the guest will still -/// // appear as if it's invoking blocking functions. +/// // Specifically the defaults for a normal WIT function are empty, +/// // meaning all flags below are disabled. For a WIT `async` function the +/// // `async | store` flags are enabled by default, but all others are +/// // still disabled. /// // -/// // This option defaults to `false`. -/// async: true, +/// // Note that unused keys in this map are a compile-time error. All +/// // keys are required to be used and consulted. +/// imports: { +/// // The `async` flag is used to indicate that a Rust-level `async` +/// // function is used on the host. This means that the host is allowed +/// // to do async I/O. Note though that to WebAssembly itself the +/// // function will still be blocking. This requires +/// // `Config::async_support` to be `true` as well. +/// "wasi:io/poll/poll": async, +/// +/// // The `store` flag means that the host function will have access +/// // to the store during its execution. By default host functions take +/// // `&mut self` which only has access to the data in question +/// // implementing the generated traits from `bindgen!`. This +/// // configuration means that in addition to `Self` the entire store +/// // will be accessible if necessary. +/// // +/// // Functions that have access to a `store` are generated in a +/// // `HostWithStore` trait. Functions without a `store` are generated +/// // in a `Host` trait. +/// // +/// // > Note: this is not yet implemented for non-async functions. This +/// // > will result in bindgen errors right now and is intended to be +/// // > implemented in the near future. +/// "wasi:clocks/monotonic-clock/now": store, +/// +/// // This is an example of combining flags where the `async` and +/// // `store` flags are combined. This means that the generated +/// // host function is both `async` and additionally has access to +/// // the `store`. Note though that this configuration is not necessary +/// // as the WIT function is itself already marked as `async`. That +/// // means that this is the default already applied meaning that +/// // specifying it here would be redundant. +/// // +/// // "wasi:clocks/monotonic-clock/[async]wait-until": async | store, /// -/// // Alternative mode of async configuration where this still implies -/// // async instantiation happens, for example, but more control is -/// // provided over which imports are async and which aren't. -/// // -/// // Note that in this mode all exports are still async. -/// async: { -/// // All imports are async except for functions with these names -/// except_imports: ["foo", "bar"], +/// // The `tracing` flag indicates that `tracing!` will be used to log +/// // entries and exits into this host API. This can assist with +/// // debugging or just generally be used to provide logs for the host. +/// // +/// // By default values are traced unless they contain lists, but +/// // tracing of lists can be enabled with `verbose_tracing` below. +/// "my:local/api/foo": tracing, +/// +/// // The `verbose_tracing` flag indicates that when combined with +/// // `tracing` the values of parameters/results are added to logs. +/// // This may include lists which may be very large. +/// "my:local/api/other-function": tracing | verbose_tracing, /// -/// // All imports are synchronous except for functions with these names +/// // The `trappable` flag indicates that this import is allowed to +/// // generate a trap. +/// // +/// // Imports that may trap have their return types wrapped in +/// // `wasmtime::Result` where the `Err` variant indicates that a +/// // trap will be raised in the guest. /// // -/// // Note that this key cannot be specified with `except_imports`, -/// // only one or the other is accepted. -/// only_imports: ["foo", "bar"], +/// // By default imports cannot trap and the return value is the return +/// // value from the WIT bindings itself. +/// // +/// // Note that the `trappable` configuration can be combined with the +/// // `trappable_error_type` configuration below to avoid having a +/// // host function return `wasmtime::Result>` +/// // for example and instead return `Result`. +/// "my:local/api/fallible": trappable, +/// +/// // The `ignore_wit` flag discards the WIT-level defaults of a +/// // function. For example this `async` WIT function will be ignored +/// // and a synchronous function will be generated on the host. +/// "my:local/api/[async]wait": ignore_wit, +/// +/// // The `exact` flag ensures that the filter, here "f", only matches +/// // functions exactly. For example "f" here would only refer to +/// // `import f: func()` in a world. Without this flag then "f" +/// // would also configure any package `f:*/*/*` for example. +/// "f": exact, +/// +/// // This is used to configure the defaults of all functions if no +/// // other key above matches a function. Note that if specific +/// // functions mentioned above want these flags too then the flags +/// // must be added there too because only one matching rule in this +/// // map is used per-function. +/// default: async | trappable, /// }, /// -/// // This option is used to indicate whether imports can trap. -/// // -/// // Imports that may trap have their return types wrapped in -/// // `wasmtime::Result` where the `Err` variant indicates that a -/// // trap will be raised in the guest. -/// // -/// // By default imports cannot trap and the return value is the return -/// // value from the WIT bindings itself. This value can be set to `true` -/// // to indicate that any import can trap. This value can also be set to -/// // an array-of-strings to indicate that only a set list of imports -/// // can trap. -/// trappable_imports: false, // no imports can trap (default) -/// // trappable_imports: true, // all imports can trap -/// // trappable_imports: ["foo", "bar"], // only these can trap +/// // Same as `imports` above, but applies to exported functions. +/// exports: { /* ... */ }, /// /// // This can be used to translate WIT return values of the form /// // `result` into `Result` in Rust. diff --git a/crates/wasmtime/src/runtime/component/storage.rs b/crates/wasmtime/src/runtime/component/storage.rs index d471c75e72..0a6ba7d938 100644 --- a/crates/wasmtime/src/runtime/component/storage.rs +++ b/crates/wasmtime/src/runtime/component/storage.rs @@ -12,20 +12,24 @@ fn assert_raw_slice_compat() { pub unsafe fn storage_as_slice(storage: &T) -> &[ValRaw] { assert_raw_slice_compat::(); - slice::from_raw_parts( - (storage as *const T).cast(), - mem::size_of_val(storage) / mem::size_of::(), - ) + unsafe { + slice::from_raw_parts( + (storage as *const T).cast(), + mem::size_of_val(storage) / mem::size_of::(), + ) + } } /// Same as `storage_as_slice`, but mutable. pub unsafe fn storage_as_slice_mut(storage: &mut MaybeUninit) -> &mut [MaybeUninit] { assert_raw_slice_compat::(); - slice::from_raw_parts_mut( - (storage as *mut MaybeUninit).cast(), - mem::size_of_val(storage) / mem::size_of::(), - ) + unsafe { + slice::from_raw_parts_mut( + (storage as *mut MaybeUninit).cast(), + mem::size_of_val(storage) / mem::size_of::(), + ) + } } /// Same as `storage_as_slice`, but in reverse and mutable. @@ -44,7 +48,7 @@ pub unsafe fn slice_to_storage_mut(slice: &mut [MaybeUninit]) -> &mut mem::size_of_val(slice) ); - &mut *slice.as_mut_ptr().cast() + unsafe { &mut *slice.as_mut_ptr().cast() } } /// Same as `storage_as_slice`, but in reverse @@ -61,5 +65,5 @@ pub unsafe fn slice_to_storage(slice: &[ValRaw]) -> &T { mem::size_of_val(slice) ); - &*slice.as_ptr().cast() + unsafe { &*slice.as_ptr().cast() } } diff --git a/crates/wasmtime/src/runtime/externals/table.rs b/crates/wasmtime/src/runtime/externals/table.rs index 33b7ca83b0..395451a9db 100644 --- a/crates/wasmtime/src/runtime/externals/table.rs +++ b/crates/wasmtime/src/runtime/externals/table.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use crate::runtime::vm::{self as runtime, GcStore}; use crate::store::{AutoAssertNoGc, StoreInstanceId, StoreOpaque}; use crate::trampoline::generate_table_export; -use crate::{AnyRef, AsContext, AsContextMut, ExternRef, Func, HeapType, Ref, TableType}; +use crate::{AnyRef, AsContext, AsContextMut, ExternRef, Func, HeapType, Ref, TableType, Trap}; use core::iter; use wasmtime_environ::DefinedTableIndex; @@ -344,26 +344,93 @@ impl Table { destination table's element type", )?; - let (dst_table, _) = dst_table.wasmtime_table(store, iter::empty()); - // FIXME(#11179) shouldn't need to subvert the borrow checker - let dst_table: *mut _ = dst_table; - let src_range = src_index..(src_index.checked_add(len).unwrap_or(u64::MAX)); - let (src_table, _) = src_table.wasmtime_table(store, src_range); - // FIXME(#11179) shouldn't need to subvert the borrow checker - let src_table: *mut _ = src_table; + // SAFETY: the the two tables have the same type, as type-checked above. unsafe { - runtime::Table::copy( - store.optional_gc_store_mut(), - dst_table, - src_table, - dst_index, - src_index, - len, - )?; + Self::copy_raw(store, dst_table, dst_index, src_table, src_index, len)?; } Ok(()) } + /// Copies the elements of `src_table` to `dst_table`. + /// + /// # Panics + /// + /// Panics if the either table doesn't belong to `store`. + /// + /// # Safety + /// + /// Requires that the two tables have previously been type-checked to have + /// the same type. + pub(crate) unsafe fn copy_raw( + store: &mut StoreOpaque, + dst_table: &Table, + dst_index: u64, + src_table: &Table, + src_index: u64, + len: u64, + ) -> Result<(), Trap> { + // Handle lazy initialization of the source table first before doing + // anything else. + let src_range = src_index..(src_index.checked_add(len).unwrap_or(u64::MAX)); + src_table.wasmtime_table(store, src_range); + + // validate `dst_table` belongs to `store`. + dst_table.wasmtime_table(store, iter::empty()); + + // Figure out which of the three cases we're in: + // + // 1. Cross-instance table copy. + // 2. Intra-instance table copy. + // 3. Intra-table copy. + // + // We handle each of them slightly differently. + let src_instance = src_table.instance.instance(); + let dst_instance = dst_table.instance.instance(); + match ( + src_instance == dst_instance, + src_table.index == dst_table.index, + ) { + // 1. Cross-instance table copy: split the mutable store borrow into + // two mutable instance borrows, get each instance's defined table, + // and do the copy. + (false, _) => { + // SAFETY: accessing two instances mutably at the same time + // requires only accessing defined entities on each instance + // which is done below with `get_defined_*` methods. + let (gc_store, [src_instance, dst_instance]) = unsafe { + store.optional_gc_store_and_instances_mut([src_instance, dst_instance]) + }; + src_instance.get_defined_table(src_table.index).copy_to( + dst_instance.get_defined_table(dst_table.index), + gc_store, + dst_index, + src_index, + len, + ) + } + + // 2. Intra-instance, distinct-tables copy: split the mutable + // instance borrow into two distinct mutable table borrows and do + // the copy. + (true, false) => { + let (gc_store, instance) = store.optional_gc_store_and_instance_mut(src_instance); + let [(_, src_table), (_, dst_table)] = instance + .tables_mut() + .get_disjoint_mut([src_table.index, dst_table.index]) + .unwrap(); + src_table.copy_to(dst_table, gc_store, dst_index, src_index, len) + } + + // 3. Intra-table copy: get the table and copy within it! + (true, true) => { + let (gc_store, instance) = store.optional_gc_store_and_instance_mut(src_instance); + instance + .get_defined_table(src_table.index) + .copy_within(gc_store, dst_index, src_index, len) + } + } + } + /// Fill `table[dst..(dst + len)]` with the given value. /// /// # Errors diff --git a/crates/wasmtime/src/runtime/fiber.rs b/crates/wasmtime/src/runtime/fiber.rs index 6f814a3250..a7f805d384 100644 --- a/crates/wasmtime/src/runtime/fiber.rs +++ b/crates/wasmtime/src/runtime/fiber.rs @@ -1,5 +1,3 @@ -#![deny(unsafe_op_in_unsafe_fn)] - use crate::prelude::*; use crate::store::{Executor, StoreId, StoreInner, StoreOpaque}; use crate::vm::mpk::{self, ProtectionMask}; diff --git a/crates/wasmtime/src/runtime/func.rs b/crates/wasmtime/src/runtime/func.rs index d740406df8..101a94cdef 100644 --- a/crates/wasmtime/src/runtime/func.rs +++ b/crates/wasmtime/src/runtime/func.rs @@ -418,8 +418,14 @@ impl Func { ) -> Self { assert!(ty.comes_from_same_engine(store.as_context().engine())); let store = store.as_context_mut().0; - let host = HostFunc::new_unchecked(store.engine(), ty, func); - host.into_func(store) + + // SAFETY: the contract required by `new_unchecked` is the same as the + // contract required by this function itself. + let host = unsafe { HostFunc::new_unchecked(store.engine(), ty, func) }; + + // SAFETY: the `T` used by `func` matches the `T` of the store we're + // inserting into via this function's type signature. + unsafe { host.into_func(store) } } /// Creates a new host-defined WebAssembly function which, when called, @@ -534,7 +540,11 @@ impl Func { /// The safety of this function requires that `func_ref` is a valid function /// pointer owned by `store`. pub(crate) unsafe fn from_vm_func_ref(store: StoreId, func_ref: NonNull) -> Func { - debug_assert!(func_ref.as_ref().type_index != VMSharedTypeIndex::default()); + // SAFETY: given the contract of this function it's safe to read the + // `type_index` field. + unsafe { + debug_assert!(func_ref.as_ref().type_index != VMSharedTypeIndex::default()); + } Func { store, unsafe_func_ref: func_ref.into(), @@ -794,12 +804,11 @@ impl Func { T: 'static, { let store = store.as_context_mut().0; - // part of this unsafety is about matching the `T` to a `Store`, - // which is done through the `AsContextMut` bound above. - unsafe { - let host = HostFunc::wrap(store.engine(), func); - host.into_func(store) - } + let host = HostFunc::wrap(store.engine(), func); + + // SAFETY: The `T` the closure takes is the same as the `T` of the store + // we're inserting into via the type signature above. + unsafe { host.into_func(store) } } #[cfg(feature = "async")] @@ -811,12 +820,11 @@ impl Func { T: 'static, { let store = store.as_context_mut().0; - // part of this unsafety is about matching the `T` to a `Store`, - // which is done through the `AsContextMut` bound above. - unsafe { - let host = HostFunc::wrap_inner(store.engine(), func); - host.into_func(store) - } + let host = HostFunc::wrap_inner(store.engine(), func); + + // SAFETY: The `T` the closure takes is the same as the `T` of the store + // we're inserting into via the type signature above. + unsafe { host.into_func(store) } } /// Same as [`Func::wrap`], except the closure asynchronously produces the @@ -999,7 +1007,10 @@ impl Func { let mut store = store.as_context_mut(); let func_ref = self.vm_func_ref(store.0); let params_and_returns = NonNull::new(params_and_returns).unwrap_or(NonNull::from(&mut [])); - Self::call_unchecked_raw(&mut store, func_ref, params_and_returns) + + // SAFETY: the safety of this function call is the same as the contract + // of this function. + unsafe { Self::call_unchecked_raw(&mut store, func_ref, params_and_returns) } } pub(crate) unsafe fn call_unchecked_raw( @@ -1007,7 +1018,9 @@ impl Func { func_ref: NonNull, params_and_returns: NonNull<[ValRaw]>, ) -> Result<()> { - invoke_wasm_and_catch_traps(store, |caller, vm| { + // SAFETY: the safety of this function call is the same as the contract + // of this function. + invoke_wasm_and_catch_traps(store, |caller, vm| unsafe { VMFuncRef::array_call(func_ref, vm, caller, params_and_returns) }) } @@ -1015,7 +1028,8 @@ impl Func { /// Converts the raw representation of a `funcref` into an `Option` /// /// This is intended to be used in conjunction with [`Func::new_unchecked`], - /// [`Func::call_unchecked`], and [`ValRaw`] with its `funcref` field. + /// [`Func::call_unchecked`], and [`ValRaw`] with its `funcref` field. This + /// is the dual of [`Func::to_raw`]. /// /// # Unsafety /// @@ -1023,14 +1037,21 @@ impl Func { /// caller must guarantee that `raw` is owned by the `store` provided and is /// valid within the `store`. pub unsafe fn from_raw(mut store: impl AsContextMut, raw: *mut c_void) -> Option { - Self::_from_raw(store.as_context_mut().0, raw) + // SAFETY: this function's own contract is that `raw` is owned by store + // to make this safe. + unsafe { Self::_from_raw(store.as_context_mut().0, raw) } } + /// Same as `from_raw`, but with the internal `StoreOpaque` type. pub(crate) unsafe fn _from_raw(store: &mut StoreOpaque, raw: *mut c_void) -> Option { - Some(Func::from_vm_func_ref( - store.id(), - NonNull::new(raw.cast())?, - )) + // SAFETY: this function's own contract is that `raw` is owned by store + // to make this safe. + unsafe { + Some(Func::from_vm_func_ref( + store.id(), + NonNull::new(raw.cast())?, + )) + } } /// Extracts the raw value of this `Func`, which is owned by `store`. @@ -1038,12 +1059,12 @@ impl Func { /// This function returns a value that's suitable for writing into the /// `funcref` field of the [`ValRaw`] structure. /// - /// # Unsafety + /// # Safety /// - /// The returned value is only valid for as long as the store is alive and - /// this function is properly rooted within it. Additionally this function - /// should not be liberally used since it's a very low-level knob. - pub unsafe fn to_raw(&self, mut store: impl AsContextMut) -> *mut c_void { + /// The returned value is only valid for as long as the store is alive. + /// This value is safe to pass to [`Func::from_raw`] so long as the same + /// `store` is provided. + pub fn to_raw(&self, mut store: impl AsContextMut) -> *mut c_void { self.vm_func_ref(store.as_context_mut().0).as_ptr().cast() } @@ -1155,9 +1176,7 @@ impl Func { debug_assert!(values_vec.is_empty()); values_vec.resize_with(values_vec_size, || ValRaw::v128(0)); for (arg, slot) in params.iter().cloned().zip(&mut values_vec) { - unsafe { - *slot = arg.to_raw(&mut *store)?; - } + *slot = arg.to_raw(&mut *store)?; } unsafe { @@ -1255,9 +1274,7 @@ impl Func { for (i, (ret, ty)) in results.iter().zip(ty.results()).enumerate() { ret.ensure_matches_ty(caller.store.0, &ty) .context("function attempted to return an incompatible value")?; - unsafe { - values_vec[i] = ret.to_raw(&mut caller.store)?; - } + values_vec[i] = ret.to_raw(&mut caller.store)?; } // Restore our `val_vec` back into the store so it's usable for the next @@ -1716,7 +1733,9 @@ where ptr: &mut [MaybeUninit], ) -> Result<()> { debug_assert!(ptr.len() > 0); - ::store(self, store, ptr.get_unchecked_mut(0)) + // SAFETY: the contract of this function/trait combo is such that `ptr` + // is valid to store this type's value, thus this lookup should be safe. + unsafe { ::store(self, store, ptr.get_unchecked_mut(0)) } } fn may_gc() -> bool { @@ -1754,7 +1773,9 @@ where store: &mut AutoAssertNoGc<'_>, ptr: &mut [MaybeUninit], ) -> Result<()> { - self.and_then(|val| val.store(store, ptr)) + // SAFETY: the safety of calling this function is the same as calling + // the inner `store`. + unsafe { self.and_then(|val| val.store(store, ptr)) } } fn may_gc() -> bool { @@ -1799,9 +1820,14 @@ macro_rules! impl_wasm_host_results { let mut _cur = 0; $( debug_assert!(_cur < _ptr.len()); - let val = _ptr.get_unchecked_mut(_cur); - _cur += 1; - WasmTy::store($t, _store, val)?; + // SAFETY: `store`'s unsafe contract is that `_ptr` is + // appropriately sized and additionally safe to call `store` + // for sub-types. + unsafe { + let val = _ptr.get_unchecked_mut(_cur); + _cur += 1; + WasmTy::store($t, _store, val)?; + } )* Ok(()) } @@ -1958,9 +1984,13 @@ macro_rules! impl_wasm_ty_list { let mut _cur = 0; ($({ debug_assert!(_cur < _values.len()); - let ptr = _values.get_unchecked(_cur).assume_init_ref(); - _cur += 1; - $args::load(_store, ptr) + // SAFETY: this function's own contract means that `_values` + // is appropriately sized/typed for the internal loads. + unsafe { + let ptr = _values.get_unchecked(_cur).assume_init_ref(); + _cur += 1; + $args::load(_store, ptr) + } },)*) } @@ -2009,36 +2039,48 @@ impl Caller<'_, T> { self.caller } + /// Executes `f` with an appropriate `Caller`. + /// + /// This is the entrypoint for host functions in core wasm and converts from + /// `VMContext` to `Caller` + /// + /// # Safety + /// + /// This requires that `caller` is safe to wrap up as a `Caller`, + /// effectively meaning that we just entered the host from wasm. + /// Additionally this `Caller`'s `T` parameter must match the actual `T` in + /// the store of the vmctx of `caller`. unsafe fn with(caller: NonNull, f: F) -> R where - // The closure must be valid for any `Caller` it is given; it doesn't - // get to choose the `Caller`'s lifetime. - F: for<'a> FnOnce(Caller<'a, T>) -> R, - // And the return value must not borrow from the caller/store. - R: 'static, + F: FnOnce(Caller<'_, T>) -> R, { - crate::runtime::vm::InstanceAndStore::from_vmctx(caller, |pair| { - let (instance, store) = pair.unpack_mut(); - let mut store = store.unchecked_context_mut::(); - let caller = Instance::from_wasmtime(instance.id(), store.0); + // SAFETY: it's a contract of this function itself that `from_vmctx` is + // safe to call. Additionally it's a contract of this function itself + // that the `T` of `Caller` matches the store. + unsafe { + crate::runtime::vm::InstanceAndStore::from_vmctx(caller, |pair| { + let (instance, store) = pair.unpack_mut(); + let mut store = store.unchecked_context_mut::(); + let caller = Instance::from_wasmtime(instance.id(), store.0); - let (gc_lifo_scope, ret) = { - let gc_lifo_scope = store.0.gc_roots().enter_lifo_scope(); + let (gc_lifo_scope, ret) = { + let gc_lifo_scope = store.0.gc_roots().enter_lifo_scope(); - let ret = f(Caller { - store: store.as_context_mut(), - caller, - }); + let ret = f(Caller { + store: store.as_context_mut(), + caller, + }); - (gc_lifo_scope, ret) - }; + (gc_lifo_scope, ret) + }; - // Safe to recreate a mutable borrow of the store because `ret` - // cannot be borrowing from the store. - store.0.exit_gc_lifo_scope(gc_lifo_scope); + // Safe to recreate a mutable borrow of the store because `ret` + // cannot be borrowing from the store. + store.0.exit_gc_lifo_scope(gc_lifo_scope); - ret - }) + ret + }) + } } fn sub_caller(&mut self) -> Caller<'_, T> { @@ -2271,6 +2313,16 @@ impl HostContext { ctx.into() } + /// Raw entry trampoline for wasm for typed functions. + /// + /// # Safety + /// + /// The `callee_vmctx`, `caller_vmctx`, and `args` values must basically be + /// "all valid" in the sense that they're from the same store, appropriately + /// sized, appropriate to dereference, etc. This requires that `T` matches + /// the type of the store that the vmctx values point to. The `F` parameter + /// must match the state in `callee_vmctx`. The `P` and `R` type parameters + /// must accurately describe the params/results store in `args`. unsafe extern "C" fn array_call_trampoline( callee_vmctx: NonNull, caller_vmctx: NonNull, @@ -2292,14 +2344,24 @@ impl HostContext { let run = move |mut caller: Caller<'_, T>| { let mut args = NonNull::slice_from_raw_parts(args.cast::>(), args_len); - let vmctx = VMArrayCallHostFuncContext::from_opaque(callee_vmctx); - let state = vmctx.as_ref().host_state(); + // SAFETY: it's a safety contract of this function itself that + // `callee_vmctx` is safe to read. + let state = unsafe { + let vmctx = VMArrayCallHostFuncContext::from_opaque(callee_vmctx); + vmctx.as_ref().host_state() + }; // Double-check ourselves in debug mode, but we control // the `Any` here so an unsafe downcast should also // work. - debug_assert!(state.is::>()); - let state = &*(state as *const _ as *const HostFuncState); + // + // SAFETY: all typed host functions use `HostFuncState` as their + // state so this should be safe to effectively do an unchecked + // downcast. + let state = unsafe { + debug_assert!(state.is::>()); + &*(state as *const _ as *const HostFuncState) + }; let func = &state.func; let ret = 'ret: { @@ -2312,7 +2374,10 @@ impl HostContext { } else { unsafe { AutoAssertNoGc::disabled(caller.store.0) } }; - let params = P::load(&mut store, args.as_mut()); + // SAFETY: this function requires `args` to be valid and the + // `WasmTyList` trait means that everything should be correctly + // ascribed/typed, making this valid to load from. + let params = unsafe { P::load(&mut store, args.as_mut()) }; let _ = &mut store; drop(store); @@ -2332,14 +2397,22 @@ impl HostContext { } else { unsafe { AutoAssertNoGc::disabled(caller.store.0) } }; - let ret = ret.store(&mut store, args.as_mut())?; + // SAFETY: this function requires that `args` is safe for this + // type signature, and the guarantees of `WasmRet` means that + // everything should be typed appropriately. + let ret = unsafe { ret.store(&mut store, args.as_mut())? }; Ok(ret) } }; // With nothing else on the stack move `run` into this // closure and then run it as part of `Caller::with`. - crate::runtime::vm::catch_unwind_and_record_trap(move || Caller::with(caller_vmctx, run)) + // + // SAFETY: this is an entrypoint of wasm which requires correct type + // ascription of `T` itself, meaning that this should be safe to call. + crate::runtime::vm::catch_unwind_and_record_trap(move || unsafe { + Caller::with(caller_vmctx, run) + }) } } @@ -2397,6 +2470,12 @@ impl HostFunc { /// /// Panics if the given function type is not associated with the given /// engine. + /// + /// # Safety + /// + /// The `func` provided must operate according to the `ty` provided to + /// ensure it's reading the correctly-typed parameters and writing the + /// correctly-typed results. pub unsafe fn new_unchecked( engine: &Engine, ty: FuncType, @@ -2406,7 +2485,11 @@ impl HostFunc { T: 'static, { assert!(ty.comes_from_same_engine(engine)); - let func = move |caller_vmctx, values: &mut [ValRaw]| { + // SAFETY: This is only only called in the raw entrypoint of wasm + // meaning that `caller_vmctx` is appropriate to read, and additionally + // the later usage of `{,in}to_func` will connect `T` to an actual + // store's `T` to ensure it's the same. + let func = move |caller_vmctx, values: &mut [ValRaw]| unsafe { Caller::::with(caller_vmctx, |mut caller| { caller.store.0.call_hook(CallHook::CallingHost)?; let result = func(caller.sub_caller(), values)?; @@ -2464,7 +2547,9 @@ impl HostFunc { self.validate_store(store); let (funcrefs, modules) = store.func_refs_and_modules(); let funcref = funcrefs.push_arc_host(self.clone(), modules); - Func::from_vm_func_ref(store.id(), funcref) + // SAFETY: this funcref was just pushed within the store, so it's safe + // to say this store owns it. + unsafe { Func::from_vm_func_ref(store.id(), funcref) } } /// Inserts this `HostFunc` into a `Store`, returning the `Func` pointing to @@ -2487,7 +2572,7 @@ impl HostFunc { /// `StoreOpaque::rooted_host_funcs`. /// /// Similarly, the caller must arrange for `rooted_func_ref` to be rooted in - /// the same store. + /// the same store and additionally be a valid pointer. pub unsafe fn to_func_store_rooted( self: &Arc, store: &mut StoreOpaque, @@ -2497,12 +2582,22 @@ impl HostFunc { match rooted_func_ref { Some(funcref) => { - debug_assert!(funcref.as_ref().wasm_call.is_some()); - Func::from_vm_func_ref(store.id(), funcref) + // SAFETY: it's a contract of this function itself that + // `funcref` is safe to read. + unsafe { + debug_assert!(funcref.as_ref().wasm_call.is_some()); + } + // SAFETY: it's a contract of this function that `funcref` is + // owned by `store`. + unsafe { Func::from_vm_func_ref(store.id(), funcref) } } None => { debug_assert!(self.func_ref().wasm_call.is_some()); - Func::from_vm_func_ref(store.id(), self.func_ref().into()) + + // SAFETY: it's an unsafe contract of this function that we are + // rooted within the store to say that the store owns a copy of + // this funcref. + unsafe { Func::from_vm_func_ref(store.id(), self.func_ref().into()) } } } } @@ -2512,7 +2607,9 @@ impl HostFunc { self.validate_store(store); let (funcrefs, modules) = store.func_refs_and_modules(); let funcref = funcrefs.push_box_host(Box::new(self), modules); - Func::from_vm_func_ref(store.id(), funcref) + // SAFETY: this funcref was just pushed within `store`, so it's safe to + // say it's owned by the store's id. + unsafe { Func::from_vm_func_ref(store.id(), funcref) } } fn validate_store(&self, store: &mut StoreOpaque) { diff --git a/crates/wasmtime/src/runtime/func/typed.rs b/crates/wasmtime/src/runtime/func/typed.rs index 7e3c0f2fc4..7ea40f9826 100644 --- a/crates/wasmtime/src/runtime/func/typed.rs +++ b/crates/wasmtime/src/runtime/func/typed.rs @@ -55,7 +55,7 @@ where /// an error then the returned `TypedFunc` is memory unsafe to invoke. pub unsafe fn new_unchecked(store: impl AsContext, func: Func) -> TypedFunc { let store = store.as_context().0; - Self::_new_unchecked(store, func) + unsafe { Self::_new_unchecked(store, func) } } pub(crate) unsafe fn _new_unchecked( @@ -150,7 +150,8 @@ where /// /// # Safety /// - /// `func` must be of the given type. + /// `func` must be of the given type, and it additionally must be a valid + /// store-owned pointer within the `store` provided. pub(crate) unsafe fn call_raw( store: &mut StoreContextMut<'_, T>, ty: &FuncType, @@ -159,8 +160,13 @@ where ) -> Result { // double-check that params/results match for this function's type in // debug mode. - if cfg!(debug_assertions) { - Self::debug_typecheck(store.0, func.as_ref().type_index); + // + // SAFETY: this function requires that `ptr` is a valid function + // pointer. + unsafe { + if cfg!(debug_assertions) { + Self::debug_typecheck(store.0, func.as_ref().type_index); + } } // Validate that all runtime values flowing into this store indeed @@ -178,7 +184,11 @@ where { let mut store = AutoAssertNoGc::new(store.0); - params.store(&mut store, ty, &mut storage.params)?; + // SAFETY: it's safe to use a union field here as the field itself + // is `MaybeUninit<_>` meaning nothing is accidentally considered + // initialized. + let dst: &mut MaybeUninit<_> = unsafe { &mut storage.params }; + params.store(&mut store, ty, dst)?; } // Try to capture only a single variable (a tuple) in the closure below. @@ -195,14 +205,22 @@ where let storage = storage.cast::(); let storage = core::ptr::slice_from_raw_parts_mut(storage, storage_len); let storage = NonNull::new(storage).unwrap(); - VMFuncRef::array_call(*func_ref, vm, caller, storage) + + // SAFETY: this function's own contract is that `func_ref` is safe + // to call and additionally that the params/results are correctly + // ascribed for this function call to be safe. + unsafe { VMFuncRef::array_call(*func_ref, vm, caller, storage) } }); let (_, storage) = captures; result?; let mut store = AutoAssertNoGc::new(store.0); - Ok(Results::load(&mut store, &storage.results)) + // SAFETY: this function is itself unsafe to ensure that the result type + // ascription is correct for `Results` and matches the actual function. + // Additionally given the correct type ascription all of the `results` + // accessed here should be validly initialized. + unsafe { Ok(Results::load(&mut store, &storage.results)) } } /// Purely a debug-mode assertion, not actually used in release builds. @@ -543,7 +561,10 @@ unsafe impl WasmTy for Func { #[inline] unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { let p = NonNull::new(ptr.get_funcref()).unwrap().cast(); - Func::from_vm_func_ref(store.id(), p) + + // SAFETY: it's an unsafe contract of `load` that it's only provided + // valid wasm values owned by `store`. + unsafe { Func::from_vm_func_ref(store.id(), p) } } } @@ -595,7 +616,10 @@ unsafe impl WasmTy for Option { #[inline] unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { let ptr = NonNull::new(ptr.get_funcref())?.cast(); - Some(Func::from_vm_func_ref(store.id(), ptr)) + + // SAFETY: it's an unsafe contract of `load` that it's only provided + // valid wasm values owned by `store`. + unsafe { Some(Func::from_vm_func_ref(store.id(), ptr)) } } } @@ -747,7 +771,9 @@ pub unsafe trait WasmResults: WasmParams { // Forwards from a bare type `T` to the 1-tuple type `(T,)` unsafe impl WasmResults for T { unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self { - <(T,) as WasmResults>::load(store, abi).0 + // SAFETY: the one-element tuple and single-type impls behave the same + // way. + unsafe { <(T,) as WasmResults>::load(store, abi).0 } } } @@ -757,7 +783,12 @@ macro_rules! impl_wasm_results { unsafe impl<$($t: WasmTy,)*> WasmResults for ($($t,)*) { unsafe fn load(_store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self { let [$($t,)*] = abi; - ($($t::load(_store, $t),)*) + + ( + // SAFETY: this is forwarding the unsafe contract of the outer + // function to the inner functions here. + $(unsafe { $t::load(_store, $t) },)* + ) } } }; diff --git a/crates/wasmtime/src/runtime/gc/disabled/anyref.rs b/crates/wasmtime/src/runtime/gc/disabled/anyref.rs index c491434960..e93951c2ce 100644 --- a/crates/wasmtime/src/runtime/gc/disabled/anyref.rs +++ b/crates/wasmtime/src/runtime/gc/disabled/anyref.rs @@ -61,17 +61,17 @@ impl AnyRef { unreachable!() } - pub unsafe fn from_raw(_store: impl AsContextMut, raw: u32) -> Option> { + pub fn from_raw(_store: impl AsContextMut, raw: u32) -> Option> { assert_eq!(raw, 0); None } - pub unsafe fn _from_raw(_store: &mut AutoAssertNoGc<'_>, raw: u32) -> Option> { + pub fn _from_raw(_store: &mut AutoAssertNoGc<'_>, raw: u32) -> Option> { assert_eq!(raw, 0); None } - pub unsafe fn to_raw(&self, _store: impl AsContextMut) -> Result { + pub fn to_raw(&self, _store: impl AsContextMut) -> Result { match *self {} } diff --git a/crates/wasmtime/src/runtime/gc/disabled/exnref.rs b/crates/wasmtime/src/runtime/gc/disabled/exnref.rs index 1107f460ae..47e48587ca 100644 --- a/crates/wasmtime/src/runtime/gc/disabled/exnref.rs +++ b/crates/wasmtime/src/runtime/gc/disabled/exnref.rs @@ -16,7 +16,7 @@ pub enum ExnRef {} impl GcRefImpl for ExnRef {} impl ExnRef { - pub unsafe fn from_raw(_store: impl AsContextMut, _raw: u32) -> Option> { + pub fn from_raw(_store: impl AsContextMut, _raw: u32) -> Option> { None } @@ -24,11 +24,11 @@ impl ExnRef { None } - pub unsafe fn to_raw(&self, _store: impl AsContextMut) -> Result { + pub fn to_raw(&self, _store: impl AsContextMut) -> Result { Ok(0) } - pub(crate) unsafe fn _to_raw(&self, _store: &mut AutoAssertNoGc<'_>) -> Result { + pub(crate) fn _to_raw(&self, _store: &mut AutoAssertNoGc<'_>) -> Result { Ok(0) } diff --git a/crates/wasmtime/src/runtime/gc/disabled/externref.rs b/crates/wasmtime/src/runtime/gc/disabled/externref.rs index e1d6af631a..8a4e7a76ac 100644 --- a/crates/wasmtime/src/runtime/gc/disabled/externref.rs +++ b/crates/wasmtime/src/runtime/gc/disabled/externref.rs @@ -38,17 +38,17 @@ impl ExternRef { match *self {} } - pub unsafe fn from_raw(_store: impl AsContextMut, raw: u32) -> Option> { + pub fn from_raw(_store: impl AsContextMut, raw: u32) -> Option> { assert_eq!(raw, 0); None } - pub unsafe fn _from_raw(_store: &mut AutoAssertNoGc<'_>, raw: u32) -> Option> { + pub fn _from_raw(_store: &mut AutoAssertNoGc<'_>, raw: u32) -> Option> { assert_eq!(raw, 0); None } - pub unsafe fn to_raw(&self, _store: impl AsContextMut) -> Result { + pub fn to_raw(&self, _store: impl AsContextMut) -> Result { match *self {} } } diff --git a/crates/wasmtime/src/runtime/gc/enabled/anyref.rs b/crates/wasmtime/src/runtime/gc/enabled/anyref.rs index 755592eba3..906efaca43 100644 --- a/crates/wasmtime/src/runtime/gc/enabled/anyref.rs +++ b/crates/wasmtime/src/runtime/gc/enabled/anyref.rs @@ -249,27 +249,30 @@ impl AnyRef { /// This function assumes that `raw` is an `anyref` value which is currently /// rooted within the [`Store`]. /// - /// # Unsafety + /// # Correctness /// - /// This function is particularly `unsafe` because `raw` not only must be a - /// valid `anyref` value produced prior by [`AnyRef::to_raw`] but it must - /// also be correctly rooted within the store. When arguments are provided - /// to a callback with [`Func::new_unchecked`], for example, or returned via - /// [`Func::call_unchecked`], if a GC is performed within the store then - /// floating `anyref` values are not rooted and will be GC'd, meaning that - /// this function will no longer be safe to call with the values cleaned up. - /// This function must be invoked *before* possible GC operations can happen - /// (such as calling Wasm). + /// This function is tricky to get right because `raw` not only must be a + /// valid `anyref` value produced prior by [`AnyRef::to_raw`] but it + /// must also be correctly rooted within the store. When arguments are + /// provided to a callback with [`Func::new_unchecked`], for example, or + /// returned via [`Func::call_unchecked`], if a GC is performed within the + /// store then floating `anyref` values are not rooted and will be GC'd, + /// meaning that this function will no longer be correct to call with the + /// values cleaned up. This function must be invoked *before* possible GC + /// operations can happen (such as calling Wasm). /// - /// When in doubt try to not use this. Instead use the safe Rust APIs of - /// [`TypedFunc`] and friends. + /// + /// When in doubt try to not use this. Instead use the Rust APIs of + /// [`TypedFunc`] and friends. Note though that this function is not + /// `unsafe` as any value can be passed in. Incorrect values can result in + /// runtime panics, however, so care must still be taken with this method. /// /// [`Func::call_unchecked`]: crate::Func::call_unchecked /// [`Func::new_unchecked`]: crate::Func::new_unchecked /// [`Store`]: crate::Store /// [`TypedFunc`]: crate::TypedFunc /// [`ValRaw`]: crate::ValRaw - pub unsafe fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option> { + pub fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option> { let mut store = AutoAssertNoGc::new(store.as_context_mut().0); Self::_from_raw(&mut store, raw) } @@ -320,19 +323,19 @@ impl AnyRef { /// /// Returns an error if this `anyref` has been unrooted. /// - /// # Unsafety + /// # Correctness /// - /// Produces a raw value which is only safe to pass into a store if a GC + /// Produces a raw value which is only valid to pass into a store if a GC /// doesn't happen between when the value is produce and when it's passed /// into the store. /// /// [`ValRaw`]: crate::ValRaw - pub unsafe fn to_raw(&self, mut store: impl AsContextMut) -> Result { + pub fn to_raw(&self, mut store: impl AsContextMut) -> Result { let mut store = AutoAssertNoGc::new(store.as_context_mut().0); self._to_raw(&mut store) } - pub(crate) unsafe fn _to_raw(&self, store: &mut AutoAssertNoGc<'_>) -> Result { + pub(crate) fn _to_raw(&self, store: &mut AutoAssertNoGc<'_>) -> Result { let gc_ref = self.inner.try_clone_gc_ref(store)?; let raw = if gc_ref.is_i31() { gc_ref.as_raw_non_zero_u32() diff --git a/crates/wasmtime/src/runtime/gc/enabled/exnref.rs b/crates/wasmtime/src/runtime/gc/enabled/exnref.rs index 3a146c4479..ed7a65fb51 100644 --- a/crates/wasmtime/src/runtime/gc/enabled/exnref.rs +++ b/crates/wasmtime/src/runtime/gc/enabled/exnref.rs @@ -136,27 +136,29 @@ impl ExnRef { /// This function assumes that `raw` is an `exnref` value which is currently /// rooted within the [`Store`]. /// - /// # Unsafety + /// # Correctness /// - /// This function is particularly `unsafe` because `raw` not only must be a + /// This function is tricky to get right because `raw` not only must be a /// valid `exnref` value produced prior by [`ExnRef::to_raw`] but it must /// also be correctly rooted within the store. When arguments are provided /// to a callback with [`Func::new_unchecked`], for example, or returned via /// [`Func::call_unchecked`], if a GC is performed within the store then /// floating `exnref` values are not rooted and will be GC'd, meaning that - /// this function will no longer be safe to call with the values cleaned up. - /// This function must be invoked *before* possible GC operations can happen - /// (such as calling Wasm). + /// this function will no longer be correct to call with the values cleaned + /// up. This function must be invoked *before* possible GC operations can + /// happen (such as calling Wasm). /// - /// When in doubt try to not use this. Instead use the safe Rust APIs of - /// [`TypedFunc`] and friends. + /// When in doubt try to not use this. Instead use the Rust APIs of + /// [`TypedFunc`] and friends. Note though that this function is not + /// `unsafe` as any value can be passed in. Incorrect values can result in + /// runtime panics, however, so care must still be taken with this method. /// /// [`Func::call_unchecked`]: crate::Func::call_unchecked /// [`Func::new_unchecked`]: crate::Func::new_unchecked /// [`Store`]: crate::Store /// [`TypedFunc`]: crate::TypedFunc /// [`ValRaw`]: crate::ValRaw - pub unsafe fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option> { + pub fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option> { let mut store = AutoAssertNoGc::new(store.as_context_mut().0); Self::_from_raw(&mut store, raw) } @@ -402,19 +404,19 @@ impl ExnRef { /// /// Returns an error if this `exnref` has been unrooted. /// - /// # Unsafety + /// # Correctness /// - /// Produces a raw value which is only safe to pass into a store if a GC + /// Produces a raw value which is only valid to pass into a store if a GC /// doesn't happen between when the value is produce and when it's passed /// into the store. /// /// [`ValRaw`]: crate::ValRaw - pub unsafe fn to_raw(&self, mut store: impl AsContextMut) -> Result { + pub fn to_raw(&self, mut store: impl AsContextMut) -> Result { let mut store = AutoAssertNoGc::new(store.as_context_mut().0); self._to_raw(&mut store) } - pub(crate) unsafe fn _to_raw(&self, store: &mut AutoAssertNoGc<'_>) -> Result { + pub(crate) fn _to_raw(&self, store: &mut AutoAssertNoGc<'_>) -> Result { let gc_ref = self.inner.try_clone_gc_ref(store)?; let raw = if gc_ref.is_i31() { gc_ref.as_raw_non_zero_u32() diff --git a/crates/wasmtime/src/runtime/gc/enabled/externref.rs b/crates/wasmtime/src/runtime/gc/enabled/externref.rs index fb8e097f91..90698f65f0 100644 --- a/crates/wasmtime/src/runtime/gc/enabled/externref.rs +++ b/crates/wasmtime/src/runtime/gc/enabled/externref.rs @@ -548,27 +548,30 @@ impl ExternRef { /// This function assumes that `raw` is an externref value which is /// currently rooted within the [`Store`]. /// - /// # Unsafety + /// # Correctness /// - /// This function is particularly `unsafe` because `raw` not only must be a - /// valid externref value produced prior by `to_raw` but it must also be - /// correctly rooted within the store. When arguments are provided to a - /// callback with [`Func::new_unchecked`], for example, or returned via - /// [`Func::call_unchecked`], if a GC is performed within the store then - /// floating externref values are not rooted and will be GC'd, meaning that - /// this function will no longer be safe to call with the values cleaned up. - /// This function must be invoked *before* possible GC operations can happen - /// (such as calling wasm). + /// This function is tricky to get right because `raw` not only must be a + /// valid `externref` value produced prior by [`ExternRef::to_raw`] but it + /// must also be correctly rooted within the store. When arguments are + /// provided to a callback with [`Func::new_unchecked`], for example, or + /// returned via [`Func::call_unchecked`], if a GC is performed within the + /// store then floating `externref` values are not rooted and will be GC'd, + /// meaning that this function will no longer be correct to call with the + /// values cleaned up. This function must be invoked *before* possible GC + /// operations can happen (such as calling Wasm). /// - /// When in doubt try to not use this. Instead use the safe Rust APIs of - /// [`TypedFunc`] and friends. + /// + /// When in doubt try to not use this. Instead use the Rust APIs of + /// [`TypedFunc`] and friends. Note though that this function is not + /// `unsafe` as any value can be passed in. Incorrect values can result in + /// runtime panics, however, so care must still be taken with this method. /// /// [`Func::call_unchecked`]: crate::Func::call_unchecked /// [`Func::new_unchecked`]: crate::Func::new_unchecked /// [`Store`]: crate::Store /// [`TypedFunc`]: crate::TypedFunc /// [`ValRaw`]: crate::ValRaw - pub unsafe fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option> { + pub fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option> { let mut store = AutoAssertNoGc::new(store.as_context_mut().0); Self::_from_raw(&mut store, raw) } @@ -585,14 +588,14 @@ impl ExternRef { /// /// Returns an error if this `externref` has been unrooted. /// - /// # Unsafety + /// # Correctness /// - /// Produces a raw value which is only safe to pass into a store if a GC + /// Produces a raw value which is only valid to pass into a store if a GC /// doesn't happen between when the value is produce and when it's passed /// into the store. /// /// [`ValRaw`]: crate::ValRaw - pub unsafe fn to_raw(&self, mut store: impl AsContextMut) -> Result { + pub fn to_raw(&self, mut store: impl AsContextMut) -> Result { let mut store = AutoAssertNoGc::new(store.as_context_mut().0); self._to_raw(&mut store) } diff --git a/crates/wasmtime/src/runtime/instance.rs b/crates/wasmtime/src/runtime/instance.rs index bda64698aa..fef680a35a 100644 --- a/crates/wasmtime/src/runtime/instance.rs +++ b/crates/wasmtime/src/runtime/instance.rs @@ -197,7 +197,10 @@ impl Instance { !store.0.async_support(), "must use async instantiation when async support is enabled", ); - Self::new_started_impl(store, module, imports) + + // SAFETY: the safety contract of `new_started_impl` is the same as this + // function. + unsafe { Self::new_started_impl(store, module, imports) } } /// Internal function to create an instance and run the start function. @@ -209,7 +212,9 @@ impl Instance { module: &Module, imports: Imports<'_>, ) -> Result { - let (instance, start) = Instance::new_raw(store.0, module, imports)?; + // SAFETY: the safety contract of `new_raw` is the same as this + // function. + let (instance, start) = unsafe { Instance::new_raw(store.0, module, imports)? }; if let Some(start) = start { instance.start_raw(store, start)?; } @@ -234,7 +239,11 @@ impl Instance { ); store - .on_fiber(|store| Self::new_started_impl(store, module, imports)) + .on_fiber(|store| { + // SAFETY: the unsafe contract of `new_started_impl` is the same + // as this function. + unsafe { Self::new_started_impl(store, module, imports) } + }) .await? } @@ -277,11 +286,16 @@ impl Instance { // The first thing we do is issue an instance allocation request // to the instance allocator. This, on success, will give us an // instance handle. - let id = store.allocate_instance( - AllocateInstanceKind::Module(module_id), - &ModuleRuntimeInfo::Module(module.clone()), - imports, - )?; + // + // SAFETY: this module, by construction, was already validated within + // the store. + let id = unsafe { + store.allocate_instance( + AllocateInstanceKind::Module(module_id), + &ModuleRuntimeInfo::Module(module.clone()), + imports, + )? + }; // Additionally, before we start doing fallible instantiation, we // do one more step which is to insert an `InstanceData` @@ -412,6 +426,7 @@ impl Instance { /// the value, if found. /// /// Returns `None` if there was no export with a matching entity index. + /// /// # Panics /// /// Panics if `store` does not own this instance. diff --git a/crates/wasmtime/src/runtime/linker.rs b/crates/wasmtime/src/runtime/linker.rs index 6ca250c038..f473a032b8 100644 --- a/crates/wasmtime/src/runtime/linker.rs +++ b/crates/wasmtime/src/runtime/linker.rs @@ -416,6 +416,10 @@ impl Linker { /// /// Panics if the given function type is not associated with the same engine /// as this linker. + /// + /// # Safety + /// + /// See [`Func::new_unchecked`] for more safety information. pub unsafe fn func_new_unchecked( &mut self, module: &str, @@ -427,7 +431,8 @@ impl Linker { T: 'static, { assert!(ty.comes_from_same_engine(self.engine())); - let func = HostFunc::new_unchecked(&self.engine, ty, func); + // SAFETY: the contract of this function is the same as `new_unchecked`. + let func = unsafe { HostFunc::new_unchecked(&self.engine, ty, func) }; let key = self.import_key(module, Some(name)); self.insert(key, Definition::HostFunc(Arc::new(func)))?; Ok(self) @@ -1374,13 +1379,20 @@ impl Definition { } } + /// Inserts this definition into the `store` provided. + /// + /// # Safety + /// /// Note the unsafety here is due to calling `HostFunc::to_func`. The /// requirement here is that the `T` that was originally used to create the /// `HostFunc` matches the `T` on the store. pub(crate) unsafe fn to_extern(&self, store: &mut StoreOpaque) -> Extern { match self { Definition::Extern(e, _) => e.clone(), - Definition::HostFunc(func) => func.to_func(store).into(), + // SAFETY: the contract of this function is the same as what's + // required of `to_func`, that `T` of the store matches the `T` of + // this original definition. + Definition::HostFunc(func) => unsafe { func.to_func(store).into() }, } } diff --git a/crates/wasmtime/src/runtime/module.rs b/crates/wasmtime/src/runtime/module.rs index 03ff73f676..8ce0161502 100644 --- a/crates/wasmtime/src/runtime/module.rs +++ b/crates/wasmtime/src/runtime/module.rs @@ -416,7 +416,9 @@ impl Module { /// lives for as long as the module and is nevery externally modified for /// the lifetime of the deserialized module. pub unsafe fn deserialize_raw(engine: &Engine, memory: NonNull<[u8]>) -> Result { - let code = engine.load_code_raw(memory, ObjectKind::Module)?; + // SAFETY: the contract required by `load_code_raw` is the same as this + // function. + let code = unsafe { engine.load_code_raw(memory, ObjectKind::Module)? }; Module::from_parts(engine, code, None) } @@ -446,8 +448,12 @@ impl Module { #[cfg(feature = "std")] pub unsafe fn deserialize_file(engine: &Engine, path: impl AsRef) -> Result { let file = open_file_for_mmap(path.as_ref())?; - Self::deserialize_open_file(engine, file) - .with_context(|| format!("failed deserialization for: {}", path.as_ref().display())) + // SAFETY: the contract of `deserialize_open_file` is the samea s this + // function. + unsafe { + Self::deserialize_open_file(engine, file) + .with_context(|| format!("failed deserialization for: {}", path.as_ref().display())) + } } /// Same as [`deserialize_file`], except that it takes an open `File` diff --git a/crates/wasmtime/src/runtime/store.rs b/crates/wasmtime/src/runtime/store.rs index 952caaafa9..33e4b6dae5 100644 --- a/crates/wasmtime/src/runtime/store.rs +++ b/crates/wasmtime/src/runtime/store.rs @@ -1309,6 +1309,32 @@ impl StoreOpaque { self.instances[id].handle.get_mut() } + /// Access multiple instances specified via `ids`. + /// + /// # Panics + /// + /// This method will panic if any indices in `ids` overlap. + /// + /// # Safety + /// + /// This method is not safe if the returned instances are used to traverse + /// "laterally" between other instances. For example accessing imported + /// items in an instance may traverse laterally to a sibling instance thus + /// aliasing a returned value here. The caller must ensure that only defined + /// items within the instances themselves are accessed. + #[inline] + pub unsafe fn optional_gc_store_and_instances_mut( + &mut self, + ids: [InstanceId; N], + ) -> (Option<&mut GcStore>, [Pin<&mut vm::Instance>; N]) { + let instances = self + .instances + .get_disjoint_mut(ids) + .unwrap() + .map(|h| h.handle.get_mut()); + (self.gc_store.as_mut(), instances) + } + /// Pair of `Self::optional_gc_store_mut` and `Self::instance_mut` pub fn optional_gc_store_and_instance_mut( &mut self, @@ -1439,12 +1465,10 @@ impl StoreOpaque { let mem_ty = engine.tunables().gc_heap_memory_type(); let tunables = engine.tunables(); - // SAFETY: We validated the GC heap's memory type during engine creation. - let (mem_alloc_index, mem) = unsafe { + let (mem_alloc_index, mem) = engine .allocator() - .allocate_memory(&mut request, &mem_ty, tunables, None)? - }; + .allocate_memory(&mut request, &mem_ty, tunables, None)?; // Then, allocate the actual GC heap, passing in that memory // storage. @@ -2040,10 +2064,8 @@ at https://bytecodealliance.org/security. /// /// # Safety /// - /// The request's associated module, memories, tables, and vmctx must have - /// already have been validated by `validate_module` for the allocator - /// configured. This is typically done during module construction for - /// example. + /// The `imports` provided must be correctly sized/typed for the module + /// being allocated. pub(crate) unsafe fn allocate_instance( &mut self, kind: AllocateInstanceKind<'_>, @@ -2056,16 +2078,20 @@ at https://bytecodealliance.org/security. AllocateInstanceKind::Module(_) => self.engine().allocator(), AllocateInstanceKind::Dummy { allocator } => allocator, }; - let handle = allocator.allocate_module(InstanceAllocationRequest { - id, - runtime_info, - imports, - store: StorePtr::new(self.traitobj()), - #[cfg(feature = "wmemcheck")] - wmemcheck: self.engine().config().wmemcheck, - pkey: self.get_pkey(), - tunables: self.engine().tunables(), - })?; + // SAFETY: this function's own contract is the same as + // `allocate_module`, namely the imports provided are valid. + let handle = unsafe { + allocator.allocate_module(InstanceAllocationRequest { + id, + runtime_info, + imports, + store: StorePtr::new(self.traitobj()), + #[cfg(feature = "wmemcheck")] + wmemcheck: self.engine().config().wmemcheck, + pkey: self.get_pkey(), + tunables: self.engine().tunables(), + })? + }; let actual = match kind { AllocateInstanceKind::Module(module_id) => { @@ -2278,7 +2304,7 @@ unsafe impl vm::VMStore for StoreInner { root: Option, bytes_needed: Option, ) -> Result> { - self.inner.maybe_async_gc(root, bytes_needed) + unsafe { self.inner.maybe_async_gc(root, bytes_needed) } } #[cfg(not(feature = "gc"))] diff --git a/crates/wasmtime/src/runtime/store/func_refs.rs b/crates/wasmtime/src/runtime/store/func_refs.rs index 8f3b12d3c6..8809c715cd 100644 --- a/crates/wasmtime/src/runtime/store/func_refs.rs +++ b/crates/wasmtime/src/runtime/store/func_refs.rs @@ -102,6 +102,7 @@ impl FuncRefs { /// /// You may only access the return value on the same thread as this /// `FuncRefs` and only while the store holding this `FuncRefs` exists. + /// Additionally the `vmctx` field of `func_ref` must be valid to read. pub unsafe fn push( &mut self, func_ref: VMFuncRef, @@ -109,7 +110,9 @@ impl FuncRefs { ) -> NonNull { debug_assert!(func_ref.wasm_call.is_none()); let func_ref = self.bump.alloc(func_ref); - let has_hole = !try_fill(func_ref, modules); + // SAFETY: it's a contract of this function itself that `func_ref` has a + // valid vmctx field to read. + let has_hole = unsafe { !try_fill(func_ref, modules) }; let unpatched = SendSyncPtr::from(func_ref); if has_hole { self.with_holes.push(unpatched); @@ -157,25 +160,27 @@ impl FuncRefs { /// /// You may only access the return value on the same thread as this /// `FuncRefs` and only while the store holding this `FuncRefs` exists. - pub unsafe fn push_arc_host( + pub fn push_arc_host( &mut self, func: Arc, modules: &ModuleRegistry, ) -> NonNull { debug_assert!(func.func_ref().wasm_call.is_none()); - let ret = self.push(func.func_ref().clone(), modules); + // SAFETY: the vmctx field in the funcref of `HostFunc` is safe to read. + let ret = unsafe { self.push(func.func_ref().clone(), modules) }; self.storage.push(Storage::ArcHost { func }); ret } /// Same as `push_arc_host`, but for owned host functions. - pub unsafe fn push_box_host( + pub fn push_box_host( &mut self, func: Box, modules: &ModuleRegistry, ) -> NonNull { debug_assert!(func.func_ref().wasm_call.is_none()); - let ret = self.push(func.func_ref().clone(), modules); + // SAFETY: the vmctx field in the funcref of `HostFunc` is safe to read. + let ret = unsafe { self.push(func.func_ref().clone(), modules) }; self.storage.push(Storage::BoxHost { func }); ret } diff --git a/crates/wasmtime/src/runtime/store/gc.rs b/crates/wasmtime/src/runtime/store/gc.rs index 9a68729739..43eaf6e590 100644 --- a/crates/wasmtime/src/runtime/store/gc.rs +++ b/crates/wasmtime/src/runtime/store/gc.rs @@ -194,7 +194,11 @@ impl StoreOpaque { Err(e) => match e.downcast::>() { Ok(oom) => { let (value, oom) = oom.take_inner(); - self.maybe_async_gc(None, Some(oom.bytes_needed()))?; + // SAFETY: it's the caller's responsibility to ensure that + // this is on a fiber stack if necessary. + unsafe { + self.maybe_async_gc(None, Some(oom.bytes_needed()))?; + } alloc_func(self, value) } Err(e) => Err(e), diff --git a/crates/wasmtime/src/runtime/trampoline/func.rs b/crates/wasmtime/src/runtime/trampoline/func.rs index 296bc1070f..4b6a47a491 100644 --- a/crates/wasmtime/src/runtime/trampoline/func.rs +++ b/crates/wasmtime/src/runtime/trampoline/func.rs @@ -20,6 +20,11 @@ struct TrampolineState { /// data and function together. /// /// Also shepherds panics and traps across Wasm. +/// +/// # Safety +/// +/// Requires that all parameters are valid from a wasm function call and +/// additionally that `vmctx` is backed by `VMArrayCallHostFuncContext`. unsafe extern "C" fn array_call_shim( vmctx: NonNull, caller_vmctx: NonNull, @@ -32,15 +37,27 @@ where // Be sure to catch Rust panics to manually shepherd them across the wasm // boundary, and then otherwise delegate as normal. crate::runtime::vm::catch_unwind_and_record_trap(|| { - let vmctx = VMArrayCallHostFuncContext::from_opaque(vmctx); + // SAFETY: this function itself requires that the `vmctx` is valid to + // use here. + let state = unsafe { + let vmctx = VMArrayCallHostFuncContext::from_opaque(vmctx); + vmctx.as_ref().host_state() + }; + // Double-check ourselves in debug mode, but we control // the `Any` here so an unsafe downcast should also // work. - let state = vmctx.as_ref().host_state(); - debug_assert!(state.is::>()); - let state = &*(state as *const _ as *const TrampolineState); + // + // SAFETY: this function is only usable with `TrampolineState`. + let state = unsafe { + debug_assert!(state.is::>()); + &*(state as *const _ as *const TrampolineState) + }; let mut values_vec = NonNull::slice_from_raw_parts(values_vec, values_vec_len); - (state.func)(caller_vmctx, values_vec.as_mut()) + // SAFETY: it's a contract of this function itself that the values + // provided are valid to view as a slice. + let values_vec = unsafe { values_vec.as_mut() }; + (state.func)(caller_vmctx, values_vec) }) } diff --git a/crates/wasmtime/src/runtime/trampoline/memory.rs b/crates/wasmtime/src/runtime/trampoline/memory.rs index febb20814c..8f74bd0ef1 100644 --- a/crates/wasmtime/src/runtime/trampoline/memory.rs +++ b/crates/wasmtime/src/runtime/trampoline/memory.rs @@ -165,7 +165,7 @@ unsafe impl InstanceAllocatorImpl for SingleMemoryInstance<'_> { self.ondemand.decrement_core_instance_count(); } - unsafe fn allocate_memory( + fn allocate_memory( &self, request: &mut InstanceAllocationRequest, ty: &wasmtime_environ::Memory, @@ -197,11 +197,13 @@ unsafe impl InstanceAllocatorImpl for SingleMemoryInstance<'_> { allocation_index: MemoryAllocationIndex, memory: Memory, ) { - self.ondemand - .deallocate_memory(memory_index, allocation_index, memory) + unsafe { + self.ondemand + .deallocate_memory(memory_index, allocation_index, memory) + } } - unsafe fn allocate_table( + fn allocate_table( &self, req: &mut InstanceAllocationRequest, ty: &wasmtime_environ::Table, @@ -217,8 +219,10 @@ unsafe impl InstanceAllocatorImpl for SingleMemoryInstance<'_> { allocation_index: TableAllocationIndex, table: Table, ) { - self.ondemand - .deallocate_table(table_index, allocation_index, table) + unsafe { + self.ondemand + .deallocate_table(table_index, allocation_index, table) + } } #[cfg(feature = "async")] diff --git a/crates/wasmtime/src/runtime/values.rs b/crates/wasmtime/src/runtime/values.rs index f63bfd12f9..260f235127 100644 --- a/crates/wasmtime/src/runtime/values.rs +++ b/crates/wasmtime/src/runtime/values.rs @@ -230,11 +230,12 @@ impl Val { /// Returns an error if this value is a GC reference and the GC reference /// has been unrooted. /// - /// # Unsafety + /// # Safety /// - /// This method is unsafe for the reasons that [`ExternRef::to_raw`] and - /// [`Func::to_raw`] are unsafe. - pub unsafe fn to_raw(&self, store: impl AsContextMut) -> Result { + /// The returned [`ValRaw`] does not carry type information and is only safe + /// to use within the context of this store itself. For more information see + /// [`ExternRef::to_raw`] and [`Func::to_raw`]. + pub fn to_raw(&self, store: impl AsContextMut) -> Result { match self { Val::I32(i) => Ok(ValRaw::i32(*i)), Val::I64(i) => Ok(ValRaw::i64(*i)), @@ -269,9 +270,11 @@ impl Val { /// otherwise that `raw` should have the type `ty` specified. pub unsafe fn from_raw(mut store: impl AsContextMut, raw: ValRaw, ty: ValType) -> Val { let mut store = AutoAssertNoGc::new(store.as_context_mut().0); - Self::_from_raw(&mut store, raw, &ty) + // SAFETY: `_from_raw` has the same contract as this function. + unsafe { Self::_from_raw(&mut store, raw, &ty) } } + /// Same as [`Self::from_raw`], but with a monomorphic store. pub(crate) unsafe fn _from_raw( store: &mut AutoAssertNoGc<'_>, raw: ValRaw, @@ -285,9 +288,11 @@ impl Val { ValType::V128 => Val::V128(raw.get_v128().into()), ValType::Ref(ref_ty) => { let ref_ = match ref_ty.heap_type() { - HeapType::Func | HeapType::ConcreteFunc(_) => { + // SAFETY: it's a safety contract of this function that the + // funcref is valid and owned by the provided store. + HeapType::Func | HeapType::ConcreteFunc(_) => unsafe { Func::_from_raw(store, raw.get_funcref()).into() - } + }, HeapType::NoFunc => Ref::Func(None), diff --git a/crates/wasmtime/src/runtime/vm.rs b/crates/wasmtime/src/runtime/vm.rs index fe804b5bd6..b5185ddfc8 100644 --- a/crates/wasmtime/src/runtime/vm.rs +++ b/crates/wasmtime/src/runtime/vm.rs @@ -282,7 +282,7 @@ impl dyn VMStore + '_ { /// This method is not safe as there's no static guarantee that `T` is /// correct for this store. pub(crate) unsafe fn unchecked_context_mut(&mut self) -> StoreContextMut<'_, T> { - StoreContextMut(&mut *(self as *mut dyn VMStore as *mut StoreInner)) + unsafe { StoreContextMut(&mut *(self as *mut dyn VMStore as *mut StoreInner)) } } } diff --git a/crates/wasmtime/src/runtime/vm/component.rs b/crates/wasmtime/src/runtime/vm/component.rs index 328db1eccf..b0d2e163aa 100644 --- a/crates/wasmtime/src/runtime/vm/component.rs +++ b/crates/wasmtime/src/runtime/vm/component.rs @@ -138,17 +138,8 @@ pub struct ComponentInstance { /// which this function pointer was registered. /// * `ty` - the type index, relative to the tables in `vmctx`, that is the /// type of the function being called. -/// * `caller_instance` - the `RuntimeComponentInstanceIndex` representing the -/// caller component instance, used to track the owner of an async host task. -/// * `flags` - the component flags for may_enter/leave corresponding to the -/// component instance that the lowering happened within. -/// * `opt_memory` - this nullable pointer represents the memory configuration -/// option for the canonical ABI options. -/// * `opt_realloc` - this nullable pointer represents the realloc configuration -/// option for the canonical ABI options. -/// * `string_encoding` - this is the configured string encoding for the -/// canonical ABI this lowering corresponds to. -/// * `async_` - whether the caller is using the async ABI. +/// * `options` - the `OptionsIndex` which indicates the canonical ABI options +/// in use for this call. /// * `args_and_results` - pointer to stack-allocated space in the caller where /// all the arguments are stored as well as where the results will be written /// to. The size and initialized bytes of this depends on the core wasm type @@ -159,21 +150,11 @@ pub struct ComponentInstance { /// This function returns a `bool` which indicates whether the call succeeded /// or not. On failure this function records trap information in TLS which /// should be suitable for reading later. -// -// FIXME: 11 arguments is probably too many. The `data` through `string-encoding` -// parameters should probably get packaged up into the `VMComponentContext`. -// Needs benchmarking one way or another though to figure out what the best -// balance is here. pub type VMLoweringCallee = extern "C" fn( vmctx: NonNull, data: NonNull, ty: u32, - caller_instance: u32, - flags: NonNull, - opt_memory: *mut VMMemoryDefinition, - opt_realloc: *mut VMFuncRef, - string_encoding: u8, - async_: u8, + options: u32, args_and_results: NonNull>, nargs_and_results: usize, ) -> bool; @@ -222,16 +203,28 @@ impl ComponentInstance { /// /// This is `unsafe` because `vmctx` cannot be guaranteed to be a valid /// pointer and it cannot be proven statically that it's safe to get a - /// mutable reference at this time to the instance from `vmctx`. + /// mutable reference at this time to the instance from `vmctx`. Note that + /// it must be also safe to borrow the store mutably, meaning it can't + /// already be in use elsewhere. pub unsafe fn from_vmctx( vmctx: NonNull, f: impl FnOnce(&mut dyn VMStore, Instance) -> R, ) -> R { - let mut ptr = vmctx - .byte_sub(mem::size_of::()) - .cast::(); - let reference = ptr.as_mut(); - let store = &mut *reference.store.0.as_ptr(); + // SAFETY: it's a contract of this function that `vmctx` is a valid + // allocation which can go backwards to a `ComponentInstance`. + let mut ptr = unsafe { + vmctx + .byte_sub(mem::size_of::()) + .cast::() + }; + // SAFETY: it's a contract of this function that it's safe to use `ptr` + // as a mutable reference. + let reference = unsafe { ptr.as_mut() }; + + // SAFETY: it's a contract of this function that it's safe to use the + // store mutably at this time. + let store = unsafe { &mut *reference.store.0.as_ptr() }; + let instance = Instance::from_wasmtime(store, reference.id); f(store, instance) } @@ -245,11 +238,15 @@ impl ComponentInstance { pub(crate) unsafe fn vmctx_instance_id( vmctx: NonNull, ) -> ComponentInstanceId { - vmctx - .byte_sub(mem::size_of::()) - .cast::() - .as_ref() - .id + // SAFETY: it's a contract of this function that `vmctx` is a valid + // pointer with a `ComponentInstance` in front which can be read. + unsafe { + vmctx + .byte_sub(mem::size_of::()) + .cast::() + .as_ref() + .id + } } /// Returns the layout corresponding to what would be an allocation of a @@ -567,69 +564,118 @@ impl ComponentInstance { unsafe fn initialize_vmctx(mut self: Pin<&mut Self>) { let offset = self.offsets.magic(); - *self.as_mut().vmctx_plus_offset_mut(offset) = VMCOMPONENT_MAGIC; + // SAFETY: it's safe to write the magic value during initialization and + // this is also the right type of value to write. + unsafe { + *self.as_mut().vmctx_plus_offset_mut(offset) = VMCOMPONENT_MAGIC; + } + // Initialize the built-in functions + // + // SAFETY: it's safe to initialize the vmctx in this function and this + // is also the right type of value to store in the vmctx. static BUILTINS: libcalls::VMComponentBuiltins = libcalls::VMComponentBuiltins::INIT; let ptr = BUILTINS.expose_provenance(); let offset = self.offsets.builtins(); - *self.as_mut().vmctx_plus_offset_mut(offset) = VmPtr::from(ptr); + unsafe { + *self.as_mut().vmctx_plus_offset_mut(offset) = VmPtr::from(ptr); + } + + // SAFETY: it's safe to initialize the vmctx in this function and this + // is also the right type of value to store in the vmctx. let offset = self.offsets.vm_store_context(); - *self.as_mut().vmctx_plus_offset_mut(offset) = - VmPtr::from(self.store.0.as_ref().vm_store_context_ptr()); + unsafe { + *self.as_mut().vmctx_plus_offset_mut(offset) = + VmPtr::from(self.store.0.as_ref().vm_store_context_ptr()); + } for i in 0..self.offsets.num_runtime_component_instances { let i = RuntimeComponentInstanceIndex::from_u32(i); let mut def = VMGlobalDefinition::new(); - *def.as_i32_mut() = FLAG_MAY_ENTER | FLAG_MAY_LEAVE; - self.instance_flags(i).as_raw().write(def); + // SAFETY: this is a valid initialization of all globals which are + // 32-bit values. + unsafe { + *def.as_i32_mut() = FLAG_MAY_ENTER | FLAG_MAY_LEAVE; + self.instance_flags(i).as_raw().write(def); + } } // In debug mode set non-null bad values to all "pointer looking" bits // and pieces related to lowering and such. This'll help detect any // erroneous usage and enable debug assertions above as well to prevent // loading these before they're configured or setting them twice. + // + // SAFETY: it's valid to write a garbage pointer during initialization + // when this is otherwise uninitialized memory if cfg!(debug_assertions) { for i in 0..self.offsets.num_lowerings { let i = LoweredIndex::from_u32(i); let offset = self.offsets.lowering_callee(i); - *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + // SAFETY: see above + unsafe { + *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + } let offset = self.offsets.lowering_data(i); - *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + // SAFETY: see above + unsafe { + *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + } } for i in 0..self.offsets.num_trampolines { let i = TrampolineIndex::from_u32(i); let offset = self.offsets.trampoline_func_ref(i); - *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + // SAFETY: see above + unsafe { + *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + } } for i in 0..self.offsets.num_runtime_memories { let i = RuntimeMemoryIndex::from_u32(i); let offset = self.offsets.runtime_memory(i); - *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + // SAFETY: see above + unsafe { + *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + } } for i in 0..self.offsets.num_runtime_reallocs { let i = RuntimeReallocIndex::from_u32(i); let offset = self.offsets.runtime_realloc(i); - *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + // SAFETY: see above + unsafe { + *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + } } for i in 0..self.offsets.num_runtime_callbacks { let i = RuntimeCallbackIndex::from_u32(i); let offset = self.offsets.runtime_callback(i); - *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + // SAFETY: see above + unsafe { + *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + } } for i in 0..self.offsets.num_runtime_post_returns { let i = RuntimePostReturnIndex::from_u32(i); let offset = self.offsets.runtime_post_return(i); - *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + // SAFETY: see above + unsafe { + *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + } } for i in 0..self.offsets.num_resources { let i = ResourceIndex::from_u32(i); let offset = self.offsets.resource_destructor(i); - *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + // SAFETY: see above + unsafe { + *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + } } for i in 0..self.offsets.num_runtime_tables { let i = RuntimeTableIndex::from_u32(i); let offset = self.offsets.runtime_table(i); - *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + // SAFETY: see above + unsafe { + *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; + } } } } @@ -869,10 +915,20 @@ impl VMComponentContext { /// Helper function to cast between context types using a debug assertion to /// protect against some mistakes. + /// + /// # Safety + /// + /// The `opaque` value must be a valid pointer where it's safe to read its + /// "magic" value. #[inline] pub unsafe fn from_opaque(opaque: NonNull) -> NonNull { // See comments in `VMContext::from_opaque` for this debug assert - debug_assert_eq!(opaque.as_ref().magic, VMCOMPONENT_MAGIC); + // + // SAFETY: it's a contract of this function that it's safe to read + // `opaque`. + unsafe { + debug_assert_eq!(opaque.as_ref().magic, VMCOMPONENT_MAGIC); + } opaque.cast() } } @@ -902,43 +958,49 @@ impl InstanceFlags { #[inline] pub unsafe fn may_leave(&self) -> bool { - *self.as_raw().as_ref().as_i32() & FLAG_MAY_LEAVE != 0 + unsafe { *self.as_raw().as_ref().as_i32() & FLAG_MAY_LEAVE != 0 } } #[inline] pub unsafe fn set_may_leave(&mut self, val: bool) { - if val { - *self.as_raw().as_mut().as_i32_mut() |= FLAG_MAY_LEAVE; - } else { - *self.as_raw().as_mut().as_i32_mut() &= !FLAG_MAY_LEAVE; + unsafe { + if val { + *self.as_raw().as_mut().as_i32_mut() |= FLAG_MAY_LEAVE; + } else { + *self.as_raw().as_mut().as_i32_mut() &= !FLAG_MAY_LEAVE; + } } } #[inline] pub unsafe fn may_enter(&self) -> bool { - *self.as_raw().as_ref().as_i32() & FLAG_MAY_ENTER != 0 + unsafe { *self.as_raw().as_ref().as_i32() & FLAG_MAY_ENTER != 0 } } #[inline] pub unsafe fn set_may_enter(&mut self, val: bool) { - if val { - *self.as_raw().as_mut().as_i32_mut() |= FLAG_MAY_ENTER; - } else { - *self.as_raw().as_mut().as_i32_mut() &= !FLAG_MAY_ENTER; + unsafe { + if val { + *self.as_raw().as_mut().as_i32_mut() |= FLAG_MAY_ENTER; + } else { + *self.as_raw().as_mut().as_i32_mut() &= !FLAG_MAY_ENTER; + } } } #[inline] pub unsafe fn needs_post_return(&self) -> bool { - *self.as_raw().as_ref().as_i32() & FLAG_NEEDS_POST_RETURN != 0 + unsafe { *self.as_raw().as_ref().as_i32() & FLAG_NEEDS_POST_RETURN != 0 } } #[inline] pub unsafe fn set_needs_post_return(&mut self, val: bool) { - if val { - *self.as_raw().as_mut().as_i32_mut() |= FLAG_NEEDS_POST_RETURN; - } else { - *self.as_raw().as_mut().as_i32_mut() &= !FLAG_NEEDS_POST_RETURN; + unsafe { + if val { + *self.as_raw().as_mut().as_i32_mut() |= FLAG_NEEDS_POST_RETURN; + } else { + *self.as_raw().as_mut().as_i32_mut() &= !FLAG_NEEDS_POST_RETURN; + } } } diff --git a/crates/wasmtime/src/runtime/vm/component/libcalls.rs b/crates/wasmtime/src/runtime/vm/component/libcalls.rs index 5f8de8511d..be65a8820a 100644 --- a/crates/wasmtime/src/runtime/vm/component/libcalls.rs +++ b/crates/wasmtime/src/runtime/vm/component/libcalls.rs @@ -7,10 +7,9 @@ use crate::runtime::component::concurrent::ResourcePair; use crate::runtime::vm::component::{ComponentInstance, VMComponentContext}; use crate::runtime::vm::{HostResultHasUnwindSentinel, VMStore, VmSafe}; use core::cell::Cell; -use core::convert::Infallible; use core::ptr::NonNull; use core::slice; -use wasmtime_environ::component::TypeResourceTableIndex; +use wasmtime_environ::component::*; const UTF16_TAG: usize = 1 << 31; @@ -100,7 +99,7 @@ mod trampolines { { $(shims!(@validate_param $pname $param);)* - let ret = crate::runtime::vm::traphandlers::catch_unwind_and_record_trap(|| { + let ret = crate::runtime::vm::traphandlers::catch_unwind_and_record_trap(|| unsafe { ComponentInstance::from_vmctx(vmctx, |store, instance| { shims!(@invoke $name(store, instance,) $($pname)*) }) @@ -123,7 +122,9 @@ mod trampolines { (@convert_ret $ret:ident) => ($ret); (@convert_ret $ret:ident $retptr:ident: ptr_size) => ({ let (a, b) = $ret; - *$retptr = b; + unsafe { + *$retptr = b; + } a }); (@convert_ret $ret:ident $name:ident: $ty:ident $($rest:tt)*) => ( @@ -184,8 +185,8 @@ unsafe fn utf8_to_utf8( len: usize, dst: *mut u8, ) -> Result<()> { - let src = slice::from_raw_parts(src, len); - let dst = slice::from_raw_parts_mut(dst, len); + let src = unsafe { slice::from_raw_parts(src, len) }; + let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; assert_no_overlap(src, dst); log::trace!("utf8-to-utf8 {len}"); let src = core::str::from_utf8(src).map_err(|_| anyhow!("invalid utf8 encoding"))?; @@ -205,8 +206,8 @@ unsafe fn utf16_to_utf16( len: usize, dst: *mut u16, ) -> Result<()> { - let src = slice::from_raw_parts(src, len); - let dst = slice::from_raw_parts_mut(dst, len); + let src = unsafe { slice::from_raw_parts(src, len) }; + let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; assert_no_overlap(src, dst); log::trace!("utf16-to-utf16 {len}"); run_utf16_to_utf16(src, dst)?; @@ -241,8 +242,8 @@ unsafe fn latin1_to_latin1( len: usize, dst: *mut u8, ) -> Result<()> { - let src = slice::from_raw_parts(src, len); - let dst = slice::from_raw_parts_mut(dst, len); + let src = unsafe { slice::from_raw_parts(src, len) }; + let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; assert_no_overlap(src, dst); log::trace!("latin1-to-latin1 {len}"); dst.copy_from_slice(src); @@ -260,8 +261,8 @@ unsafe fn latin1_to_utf16( len: usize, dst: *mut u16, ) -> Result<()> { - let src = slice::from_raw_parts(src, len); - let dst = slice::from_raw_parts_mut(dst, len); + let src = unsafe { slice::from_raw_parts(src, len) }; + let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; assert_no_overlap(src, dst); for (src, dst) in src.iter().zip(dst) { *dst = u16::from(*src).to_le(); @@ -291,8 +292,8 @@ unsafe fn utf8_to_utf16( len: usize, dst: *mut u16, ) -> Result { - let src = slice::from_raw_parts(src, len); - let dst = slice::from_raw_parts_mut(dst, len); + let src = unsafe { slice::from_raw_parts(src, len) }; + let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; assert_no_overlap(src, dst); let result = run_utf8_to_utf16(src, dst)?; @@ -337,8 +338,8 @@ unsafe fn utf16_to_utf8( dst: *mut u8, dst_len: usize, ) -> Result { - let src = slice::from_raw_parts(src, src_len); - let mut dst = slice::from_raw_parts_mut(dst, dst_len); + let src = unsafe { slice::from_raw_parts(src, src_len) }; + let mut dst = unsafe { slice::from_raw_parts_mut(dst, dst_len) }; assert_no_overlap(src, dst); // This iterator will convert to native endianness and additionally count @@ -392,8 +393,8 @@ unsafe fn latin1_to_utf8( dst: *mut u8, dst_len: usize, ) -> Result { - let src = slice::from_raw_parts(src, src_len); - let dst = slice::from_raw_parts_mut(dst, dst_len); + let src = unsafe { slice::from_raw_parts(src, src_len) }; + let dst = unsafe { slice::from_raw_parts_mut(dst, dst_len) }; assert_no_overlap(src, dst); let (read, written) = encoding_rs::mem::convert_latin1_to_utf8_partial(src, dst); log::trace!("latin1-to-utf8 {src_len}/{dst_len} => ({read}, {written})"); @@ -417,12 +418,12 @@ unsafe fn utf16_to_compact_probably_utf16( len: usize, dst: *mut u16, ) -> Result { - let src = slice::from_raw_parts(src, len); - let dst = slice::from_raw_parts_mut(dst, len); + let src = unsafe { slice::from_raw_parts(src, len) }; + let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; assert_no_overlap(src, dst); let all_latin1 = run_utf16_to_utf16(src, dst)?; if all_latin1 { - let (left, dst, right) = dst.align_to_mut::(); + let (left, dst, right) = unsafe { dst.align_to_mut::() }; assert!(left.is_empty()); assert!(right.is_empty()); for i in 0..len { @@ -453,8 +454,8 @@ unsafe fn utf8_to_latin1( len: usize, dst: *mut u8, ) -> Result { - let src = slice::from_raw_parts(src, len); - let dst = slice::from_raw_parts_mut(dst, len); + let src = unsafe { slice::from_raw_parts(src, len) }; + let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; assert_no_overlap(src, dst); let read = encoding_rs::mem::utf8_latin1_up_to(src); let written = encoding_rs::mem::convert_utf8_to_latin1_lossy(&src[..read], dst); @@ -475,8 +476,8 @@ unsafe fn utf16_to_latin1( len: usize, dst: *mut u8, ) -> Result { - let src = slice::from_raw_parts(src, len); - let dst = slice::from_raw_parts_mut(dst, len); + let src = unsafe { slice::from_raw_parts(src, len) }; + let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; assert_no_overlap(src, dst); let mut size = 0; @@ -519,8 +520,8 @@ unsafe fn utf8_to_compact_utf16( dst_len: usize, latin1_bytes_so_far: usize, ) -> Result { - let src = slice::from_raw_parts(src, src_len); - let dst = slice::from_raw_parts_mut(dst, dst_len); + let src = unsafe { slice::from_raw_parts(src, src_len) }; + let dst = unsafe { slice::from_raw_parts_mut(dst, dst_len) }; assert_no_overlap(src, dst); let dst = inflate_latin1_bytes(dst, latin1_bytes_so_far); @@ -539,8 +540,8 @@ unsafe fn utf16_to_compact_utf16( dst_len: usize, latin1_bytes_so_far: usize, ) -> Result { - let src = slice::from_raw_parts(src, src_len); - let dst = slice::from_raw_parts_mut(dst, dst_len); + let src = unsafe { slice::from_raw_parts(src, src_len) }; + let dst = unsafe { slice::from_raw_parts_mut(dst, dst_len) }; assert_no_overlap(src, dst); let dst = inflate_latin1_bytes(dst, latin1_bytes_so_far); @@ -652,7 +653,7 @@ fn resource_exit_call(store: &mut dyn VMStore, instance: Instance) -> Result<()> instance.resource_exit_call(store) } -fn trap(_store: &mut dyn VMStore, _instance: Instance, code: u8) -> Result { +fn trap(_store: &mut dyn VMStore, _instance: Instance, code: u8) -> Result<()> { Err(wasmtime_environ::Trap::from_u8(code).unwrap().into()) } @@ -664,7 +665,7 @@ fn backpressure_set( enabled: u32, ) -> Result<()> { instance.concurrent_state_mut(store).backpressure_set( - wasmtime_environ::component::RuntimeComponentInstanceIndex::from_u32(caller_instance), + RuntimeComponentInstanceIndex::from_u32(caller_instance), enabled, ) } @@ -674,18 +675,15 @@ unsafe fn task_return( store: &mut dyn VMStore, instance: Instance, ty: u32, - memory: *mut u8, - string_encoding: u8, + options: u32, storage: *mut u8, storage_len: usize, ) -> Result<()> { instance.task_return( store, - wasmtime_environ::component::TypeTupleIndex::from_u32(ty), - memory.cast::(), - string_encoding, - storage.cast::(), - storage_len, + TypeTupleIndex::from_u32(ty), + OptionsIndex::from_u32(options), + unsafe { core::slice::from_raw_parts(storage.cast(), storage_len) }, ) } @@ -693,7 +691,7 @@ unsafe fn task_return( fn task_cancel(store: &mut dyn VMStore, instance: Instance, caller_instance: u32) -> Result<()> { instance.task_cancel( store, - wasmtime_environ::component::RuntimeComponentInstanceIndex::from_u32(caller_instance), + RuntimeComponentInstanceIndex::from_u32(caller_instance), ) } @@ -703,49 +701,31 @@ fn waitable_set_new( instance: Instance, caller_instance: u32, ) -> Result { - instance.concurrent_state_mut(store).waitable_set_new( - wasmtime_environ::component::RuntimeComponentInstanceIndex::from_u32(caller_instance), - ) + instance + .concurrent_state_mut(store) + .waitable_set_new(RuntimeComponentInstanceIndex::from_u32(caller_instance)) } #[cfg(feature = "component-model-async")] -unsafe fn waitable_set_wait( +fn waitable_set_wait( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, - async_: u8, - memory: *mut u8, + options: u32, set: u32, payload: u32, ) -> Result { - instance.waitable_set_wait( - store, - wasmtime_environ::component::RuntimeComponentInstanceIndex::from_u32(caller_instance), - async_ != 0, - memory.cast::(), - set, - payload, - ) + instance.waitable_set_wait(store, OptionsIndex::from_u32(options), set, payload) } #[cfg(feature = "component-model-async")] -unsafe fn waitable_set_poll( +fn waitable_set_poll( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, - async_: u8, - memory: *mut u8, + options: u32, set: u32, payload: u32, ) -> Result { - instance.waitable_set_poll( - store, - wasmtime_environ::component::RuntimeComponentInstanceIndex::from_u32(caller_instance), - async_ != 0, - memory.cast::(), - set, - payload, - ) + instance.waitable_set_poll(store, OptionsIndex::from_u32(options), set, payload) } #[cfg(feature = "component-model-async")] @@ -756,7 +736,7 @@ fn waitable_set_drop( set: u32, ) -> Result<()> { instance.concurrent_state_mut(store).waitable_set_drop( - wasmtime_environ::component::RuntimeComponentInstanceIndex::from_u32(caller_instance), + RuntimeComponentInstanceIndex::from_u32(caller_instance), set, ) } @@ -770,7 +750,7 @@ fn waitable_join( set: u32, ) -> Result<()> { instance.concurrent_state_mut(store).waitable_join( - wasmtime_environ::component::RuntimeComponentInstanceIndex::from_u32(caller_instance), + RuntimeComponentInstanceIndex::from_u32(caller_instance), waitable, set, ) @@ -789,7 +769,7 @@ fn subtask_drop( task_id: u32, ) -> Result<()> { instance.concurrent_state_mut(store).subtask_drop( - wasmtime_environ::component::RuntimeComponentInstanceIndex::from_u32(caller_instance), + RuntimeComponentInstanceIndex::from_u32(caller_instance), task_id, ) } @@ -804,7 +784,7 @@ fn subtask_cancel( ) -> Result { instance.subtask_cancel( store, - wasmtime_environ::component::RuntimeComponentInstanceIndex::from_u32(caller_instance), + RuntimeComponentInstanceIndex::from_u32(caller_instance), async_ != 0, task_id, ) @@ -825,19 +805,21 @@ unsafe fn prepare_call( storage: *mut u8, storage_len: usize, ) -> Result<()> { - store.component_async_store().prepare_call( - instance, - memory.cast::(), - start.cast::(), - return_.cast::(), - wasmtime_environ::component::RuntimeComponentInstanceIndex::from_u32(caller_instance), - wasmtime_environ::component::RuntimeComponentInstanceIndex::from_u32(callee_instance), - wasmtime_environ::component::TypeTupleIndex::from_u32(task_return_type), - u8::try_from(string_encoding).unwrap(), - result_count_or_max_if_async, - storage.cast::(), - storage_len, - ) + unsafe { + store.component_async_store().prepare_call( + instance, + memory.cast::(), + start.cast::(), + return_.cast::(), + RuntimeComponentInstanceIndex::from_u32(caller_instance), + RuntimeComponentInstanceIndex::from_u32(callee_instance), + TypeTupleIndex::from_u32(task_return_type), + u8::try_from(string_encoding).unwrap(), + result_count_or_max_if_async, + storage.cast::(), + storage_len, + ) + } } #[cfg(feature = "component-model-async")] @@ -845,19 +827,21 @@ unsafe fn sync_start( store: &mut dyn VMStore, instance: Instance, callback: *mut u8, - callee: *mut u8, - param_count: u32, storage: *mut u8, storage_len: usize, + callee: *mut u8, + param_count: u32, ) -> Result<()> { - store.component_async_store().sync_start( - instance, - callback.cast::(), - callee.cast::(), - param_count, - storage.cast::>(), - storage_len, - ) + unsafe { + store.component_async_store().sync_start( + instance, + callback.cast::(), + callee.cast::(), + param_count, + storage.cast::>(), + storage_len, + ) + } } #[cfg(feature = "component-model-async")] @@ -871,15 +855,17 @@ unsafe fn async_start( result_count: u32, flags: u32, ) -> Result { - store.component_async_store().async_start( - instance, - callback.cast::(), - post_return.cast::(), - callee.cast::(), - param_count, - result_count, - flags, - ) + unsafe { + store.component_async_store().async_start( + instance, + callback.cast::(), + post_return.cast::(), + callee.cast::(), + param_count, + result_count, + flags, + ) + } } #[cfg(feature = "component-model-async")] @@ -892,8 +878,8 @@ fn future_transfer( ) -> Result { instance.concurrent_state_mut(store).future_transfer( src_idx, - wasmtime_environ::component::TypeFutureTableIndex::from_u32(src_table), - wasmtime_environ::component::TypeFutureTableIndex::from_u32(dst_table), + TypeFutureTableIndex::from_u32(src_table), + TypeFutureTableIndex::from_u32(dst_table), ) } @@ -907,8 +893,8 @@ fn stream_transfer( ) -> Result { instance.concurrent_state_mut(store).stream_transfer( src_idx, - wasmtime_environ::component::TypeStreamTableIndex::from_u32(src_table), - wasmtime_environ::component::TypeStreamTableIndex::from_u32(dst_table), + TypeStreamTableIndex::from_u32(src_table), + TypeStreamTableIndex::from_u32(dst_table), ) } @@ -920,10 +906,8 @@ fn error_context_transfer( src_table: u32, dst_table: u32, ) -> Result { - let src_table = - wasmtime_environ::component::TypeComponentLocalErrorContextTableIndex::from_u32(src_table); - let dst_table = - wasmtime_environ::component::TypeComponentLocalErrorContextTableIndex::from_u32(dst_table); + let src_table = TypeComponentLocalErrorContextTableIndex::from_u32(src_table); + let dst_table = TypeComponentLocalErrorContextTableIndex::from_u32(dst_table); instance .concurrent_state_mut(store) .error_context_transfer(src_idx, src_table, dst_table) @@ -942,54 +926,42 @@ unsafe impl HostResultHasUnwindSentinel for ResourcePair { #[cfg(feature = "component-model-async")] fn future_new(store: &mut dyn VMStore, instance: Instance, ty: u32) -> Result { - instance.concurrent_state_mut(store).future_new( - wasmtime_environ::component::TypeFutureTableIndex::from_u32(ty), - ) + instance + .concurrent_state_mut(store) + .future_new(TypeFutureTableIndex::from_u32(ty)) } #[cfg(feature = "component-model-async")] -unsafe fn future_write( +fn future_write( store: &mut dyn VMStore, instance: Instance, - memory: *mut u8, - realloc: *mut u8, - string_encoding: u8, - async_: u8, ty: u32, + options: u32, future: u32, address: u32, ) -> Result { store.component_async_store().future_write( instance, - memory.cast::(), - realloc.cast::(), - string_encoding, - async_ != 0, - wasmtime_environ::component::TypeFutureTableIndex::from_u32(ty), + TypeFutureTableIndex::from_u32(ty), + OptionsIndex::from_u32(options), future, address, ) } #[cfg(feature = "component-model-async")] -unsafe fn future_read( +fn future_read( store: &mut dyn VMStore, instance: Instance, - memory: *mut u8, - realloc: *mut u8, - string_encoding: u8, - async_: u8, ty: u32, + options: u32, future: u32, address: u32, ) -> Result { store.component_async_store().future_read( instance, - memory.cast::(), - realloc.cast::(), - string_encoding, - async_ != 0, - wasmtime_environ::component::TypeFutureTableIndex::from_u32(ty), + TypeFutureTableIndex::from_u32(ty), + OptionsIndex::from_u32(options), future, address, ) @@ -1004,7 +976,7 @@ fn future_cancel_write( writer: u32, ) -> Result { instance.concurrent_state_mut(store).future_cancel_write( - wasmtime_environ::component::TypeFutureTableIndex::from_u32(ty), + TypeFutureTableIndex::from_u32(ty), async_ != 0, writer, ) @@ -1019,7 +991,7 @@ fn future_cancel_read( reader: u32, ) -> Result { instance.concurrent_state_mut(store).future_cancel_read( - wasmtime_environ::component::TypeFutureTableIndex::from_u32(ty), + TypeFutureTableIndex::from_u32(ty), async_ != 0, reader, ) @@ -1032,9 +1004,9 @@ fn future_drop_writable( ty: u32, writer: u32, ) -> Result<()> { - instance.future_drop_writable( - store, - wasmtime_environ::component::TypeFutureTableIndex::from_u32(ty), + store.component_async_store().future_drop_writable( + instance, + TypeFutureTableIndex::from_u32(ty), writer, ) } @@ -1046,40 +1018,30 @@ fn future_drop_readable( ty: u32, reader: u32, ) -> Result<()> { - instance.future_drop_readable( - store, - wasmtime_environ::component::TypeFutureTableIndex::from_u32(ty), - reader, - ) + instance.future_drop_readable(store, TypeFutureTableIndex::from_u32(ty), reader) } #[cfg(feature = "component-model-async")] fn stream_new(store: &mut dyn VMStore, instance: Instance, ty: u32) -> Result { - instance.concurrent_state_mut(store).stream_new( - wasmtime_environ::component::TypeStreamTableIndex::from_u32(ty), - ) + instance + .concurrent_state_mut(store) + .stream_new(TypeStreamTableIndex::from_u32(ty)) } #[cfg(feature = "component-model-async")] -unsafe fn stream_write( +fn stream_write( store: &mut dyn VMStore, instance: Instance, - memory: *mut u8, - realloc: *mut u8, - string_encoding: u8, - async_: u8, ty: u32, + options: u32, stream: u32, address: u32, count: u32, ) -> Result { store.component_async_store().stream_write( instance, - memory.cast::(), - realloc.cast::(), - string_encoding, - async_ != 0, - wasmtime_environ::component::TypeStreamTableIndex::from_u32(ty), + TypeStreamTableIndex::from_u32(ty), + OptionsIndex::from_u32(options), stream, address, count, @@ -1087,25 +1049,19 @@ unsafe fn stream_write( } #[cfg(feature = "component-model-async")] -unsafe fn stream_read( +fn stream_read( store: &mut dyn VMStore, instance: Instance, - memory: *mut u8, - realloc: *mut u8, - string_encoding: u8, - async_: u8, ty: u32, + options: u32, stream: u32, address: u32, count: u32, ) -> Result { store.component_async_store().stream_read( instance, - memory.cast::(), - realloc.cast::(), - string_encoding, - async_ != 0, - wasmtime_environ::component::TypeStreamTableIndex::from_u32(ty), + TypeStreamTableIndex::from_u32(ty), + OptionsIndex::from_u32(options), stream, address, count, @@ -1121,7 +1077,7 @@ fn stream_cancel_write( writer: u32, ) -> Result { instance.concurrent_state_mut(store).stream_cancel_write( - wasmtime_environ::component::TypeStreamTableIndex::from_u32(ty), + TypeStreamTableIndex::from_u32(ty), async_ != 0, writer, ) @@ -1136,7 +1092,7 @@ fn stream_cancel_read( reader: u32, ) -> Result { instance.concurrent_state_mut(store).stream_cancel_read( - wasmtime_environ::component::TypeStreamTableIndex::from_u32(ty), + TypeStreamTableIndex::from_u32(ty), async_ != 0, reader, ) @@ -1149,9 +1105,9 @@ fn stream_drop_writable( ty: u32, writer: u32, ) -> Result<()> { - instance.stream_drop_writable( - store, - wasmtime_environ::component::TypeStreamTableIndex::from_u32(ty), + store.component_async_store().stream_drop_writable( + instance, + TypeStreamTableIndex::from_u32(ty), writer, ) } @@ -1163,21 +1119,15 @@ fn stream_drop_readable( ty: u32, reader: u32, ) -> Result<()> { - instance.stream_drop_readable( - store, - wasmtime_environ::component::TypeStreamTableIndex::from_u32(ty), - reader, - ) + instance.stream_drop_readable(store, TypeStreamTableIndex::from_u32(ty), reader) } #[cfg(feature = "component-model-async")] -unsafe fn flat_stream_write( +fn flat_stream_write( store: &mut dyn VMStore, instance: Instance, - memory: *mut u8, - realloc: *mut u8, - async_: u8, ty: u32, + options: u32, payload_size: u32, payload_align: u32, stream: u32, @@ -1186,10 +1136,8 @@ unsafe fn flat_stream_write( ) -> Result { store.component_async_store().flat_stream_write( instance, - memory.cast::(), - realloc.cast::(), - async_ != 0, - wasmtime_environ::component::TypeStreamTableIndex::from_u32(ty), + TypeStreamTableIndex::from_u32(ty), + OptionsIndex::from_u32(options), payload_size, payload_align, stream, @@ -1199,13 +1147,11 @@ unsafe fn flat_stream_write( } #[cfg(feature = "component-model-async")] -unsafe fn flat_stream_read( +fn flat_stream_read( store: &mut dyn VMStore, instance: Instance, - memory: *mut u8, - realloc: *mut u8, - async_: u8, ty: u32, + options: u32, payload_size: u32, payload_align: u32, stream: u32, @@ -1214,10 +1160,8 @@ unsafe fn flat_stream_read( ) -> Result { store.component_async_store().flat_stream_read( instance, - memory.cast::(), - realloc.cast::(), - async_ != 0, - wasmtime_environ::component::TypeStreamTableIndex::from_u32(ty), + TypeStreamTableIndex::from_u32(ty), + OptionsIndex::from_u32(options), payload_size, payload_align, stream, @@ -1227,44 +1171,36 @@ unsafe fn flat_stream_read( } #[cfg(feature = "component-model-async")] -unsafe fn error_context_new( +fn error_context_new( store: &mut dyn VMStore, instance: Instance, - memory: *mut u8, - realloc: *mut u8, - string_encoding: u8, ty: u32, + options: u32, debug_msg_address: u32, debug_msg_len: u32, ) -> Result { instance.error_context_new( store.store_opaque_mut(), - memory.cast::(), - realloc.cast::(), - string_encoding, - wasmtime_environ::component::TypeComponentLocalErrorContextTableIndex::from_u32(ty), + TypeComponentLocalErrorContextTableIndex::from_u32(ty), + OptionsIndex::from_u32(options), debug_msg_address, debug_msg_len, ) } #[cfg(feature = "component-model-async")] -unsafe fn error_context_debug_message( +fn error_context_debug_message( store: &mut dyn VMStore, instance: Instance, - memory: *mut u8, - realloc: *mut u8, - string_encoding: u8, ty: u32, + options: u32, err_ctx_handle: u32, debug_msg_address: u32, ) -> Result<()> { store.component_async_store().error_context_debug_message( instance, - memory.cast::(), - realloc.cast::(), - string_encoding, - wasmtime_environ::component::TypeComponentLocalErrorContextTableIndex::from_u32(ty), + TypeComponentLocalErrorContextTableIndex::from_u32(ty), + OptionsIndex::from_u32(options), err_ctx_handle, debug_msg_address, ) @@ -1278,7 +1214,7 @@ fn error_context_drop( err_ctx_handle: u32, ) -> Result<()> { instance.concurrent_state_mut(store).error_context_drop( - wasmtime_environ::component::TypeComponentLocalErrorContextTableIndex::from_u32(ty), + TypeComponentLocalErrorContextTableIndex::from_u32(ty), err_ctx_handle, ) } diff --git a/crates/wasmtime/src/runtime/vm/const_expr.rs b/crates/wasmtime/src/runtime/vm/const_expr.rs index 31cd94e926..81e28ce2fd 100644 --- a/crates/wasmtime/src/runtime/vm/const_expr.rs +++ b/crates/wasmtime/src/runtime/vm/const_expr.rs @@ -82,7 +82,7 @@ impl ConstEvalContext { .zip(struct_ty.fields()) .map(|(raw, ty)| { let ty = ty.element_type().unpack(); - Val::_from_raw(store, *raw, ty) + unsafe { Val::_from_raw(store, *raw, ty) } }) .collect::>(); @@ -262,11 +262,9 @@ impl ConstExprEvaluator { } let start = self.stack.len() - len; - let s = context.struct_new( - &mut store, - interned_type_index, - &self.stack[start..], - )?; + let s = unsafe { + context.struct_new(&mut store, interned_type_index, &self.stack[start..])? + }; self.stack.truncate(start); self.stack.push(s); } @@ -287,7 +285,9 @@ impl ConstExprEvaluator { let len = self.pop()?.get_i32().unsigned(); - let elem = Val::_from_raw(&mut store, self.pop()?, ty.element_type().unpack()); + let elem = unsafe { + Val::_from_raw(&mut store, self.pop()?, ty.element_type().unpack()) + }; let pre = ArrayRefPre::_new(&mut store, ty); let array = unsafe { ArrayRef::new_maybe_async(&mut store, &pre, &elem, len)? }; @@ -339,7 +339,7 @@ impl ConstExprEvaluator { let elems = self .stack .drain(start..) - .map(|raw| Val::_from_raw(&mut store, raw, elem_ty)) + .map(|raw| unsafe { Val::_from_raw(&mut store, raw, elem_ty) }) .collect::>(); let pre = ArrayRefPre::_new(&mut store, ty); diff --git a/crates/wasmtime/src/runtime/vm/cow.rs b/crates/wasmtime/src/runtime/vm/cow.rs index 9354f6a2ee..68f2eb7e1f 100644 --- a/crates/wasmtime/src/runtime/vm/cow.rs +++ b/crates/wasmtime/src/runtime/vm/cow.rs @@ -127,19 +127,23 @@ impl MemoryImage { } unsafe fn map_at(&self, mmap_base: &MmapOffset) -> Result<()> { - mmap_base.map_image_at( - &self.source, - self.source_offset, - self.linear_memory_offset, - self.len, - ) + unsafe { + mmap_base.map_image_at( + &self.source, + self.source_offset, + self.linear_memory_offset, + self.len, + ) + } } unsafe fn remap_as_zeros_at(&self, base: *mut u8) -> Result<()> { - self.source.remap_as_zeros_at( - base.add(self.linear_memory_offset.byte_count()), - self.len.byte_count(), - )?; + unsafe { + self.source.remap_as_zeros_at( + base.add(self.linear_memory_offset.byte_count()), + self.len.byte_count(), + )?; + } Ok(()) } } @@ -526,7 +530,9 @@ impl MemoryImageSlot { self.reset_with_anon_memory() } DecommitBehavior::RestoreOriginalMapping => { - self.reset_with_original_mapping(keep_resident, decommit); + unsafe { + self.reset_with_original_mapping(keep_resident, decommit); + } Ok(()) } } @@ -587,36 +593,44 @@ impl MemoryImageSlot { let remaining_memset = excess.min(mem_after_image); // This is memset (1) - ptr::write_bytes( - self.base.as_mut_ptr(), - 0u8, - image.linear_memory_offset.byte_count(), - ); + unsafe { + ptr::write_bytes( + self.base.as_mut_ptr(), + 0u8, + image.linear_memory_offset.byte_count(), + ); + } // This is madvise (2) - self.restore_original_mapping( - image.linear_memory_offset, - image.len, - &mut decommit, - ); + unsafe { + self.restore_original_mapping( + image.linear_memory_offset, + image.len, + &mut decommit, + ); + } // This is memset (3) - ptr::write_bytes( - self.base.as_mut_ptr().add(image_end.byte_count()), - 0u8, - remaining_memset.byte_count(), - ); + unsafe { + ptr::write_bytes( + self.base.as_mut_ptr().add(image_end.byte_count()), + 0u8, + remaining_memset.byte_count(), + ); + } // This is madvise (4) - self.restore_original_mapping( - image_end - .checked_add(remaining_memset) - .expect("image_end + remaining_memset is in bounds"), - mem_after_image - .checked_sub(remaining_memset) - .expect("remaining_memset defined to be <= mem_after_image"), - &mut decommit, - ); + unsafe { + self.restore_original_mapping( + image_end + .checked_add(remaining_memset) + .expect("image_end + remaining_memset is in bounds"), + mem_after_image + .checked_sub(remaining_memset) + .expect("remaining_memset defined to be <= mem_after_image"), + &mut decommit, + ); + } } else { // If the image starts after the `keep_resident` threshold // then we memset the start of linear memory and then use @@ -638,16 +652,20 @@ impl MemoryImageSlot { // Note that the memset may be zero bytes here. // This is memset (1) - ptr::write_bytes(self.base.as_mut_ptr(), 0u8, keep_resident.byte_count()); + unsafe { + ptr::write_bytes(self.base.as_mut_ptr(), 0u8, keep_resident.byte_count()); + } // This is madvise (2) - self.restore_original_mapping( - keep_resident, - self.accessible - .checked_sub(keep_resident) - .expect("keep_resident is a subset of accessible memory"), - decommit, - ); + unsafe { + self.restore_original_mapping( + keep_resident, + self.accessible + .checked_sub(keep_resident) + .expect("keep_resident is a subset of accessible memory"), + decommit, + ); + } }; } @@ -656,14 +674,16 @@ impl MemoryImageSlot { // the rest. None => { let size_to_memset = keep_resident.min(self.accessible); - ptr::write_bytes(self.base.as_mut_ptr(), 0u8, size_to_memset.byte_count()); - self.restore_original_mapping( - size_to_memset, - self.accessible - .checked_sub(size_to_memset) - .expect("size_to_memset is defined to be <= self.accessible"), - decommit, - ); + unsafe { + ptr::write_bytes(self.base.as_mut_ptr(), 0u8, size_to_memset.byte_count()); + self.restore_original_mapping( + size_to_memset, + self.accessible + .checked_sub(size_to_memset) + .expect("size_to_memset is defined to be <= self.accessible"), + decommit, + ); + } } } } @@ -684,10 +704,12 @@ impl MemoryImageSlot { vm::decommit_behavior(), DecommitBehavior::RestoreOriginalMapping ); - decommit( - self.base.as_mut_ptr().add(base.byte_count()), - len.byte_count(), - ); + unsafe { + decommit( + self.base.as_mut_ptr().add(base.byte_count()), + len.byte_count(), + ); + } } fn set_protection(&self, range: Range, readwrite: bool) -> Result<()> { diff --git a/crates/wasmtime/src/runtime/vm/debug_builtins.rs b/crates/wasmtime/src/runtime/vm/debug_builtins.rs index cdc078682e..4b5d5aa81d 100644 --- a/crates/wasmtime/src/runtime/vm/debug_builtins.rs +++ b/crates/wasmtime/src/runtime/vm/debug_builtins.rs @@ -1,6 +1,6 @@ #![doc(hidden)] -use crate::runtime::vm::instance::Instance; +use crate::runtime::vm::instance::InstanceAndStore; use crate::runtime::vm::vmcontext::VMContext; use core::ptr::NonNull; use wasmtime_environ::{EntityRef, MemoryIndex}; @@ -13,26 +13,31 @@ static mut VMCTX_AND_MEMORY: (NonNull, usize) = (NonNull::dangling(), #[versioned_export] pub unsafe extern "C" fn resolve_vmctx_memory_ptr(p: *const u32) -> *const u8 { - let ptr = std::ptr::read(p); - assert!( - VMCTX_AND_MEMORY.0 != NonNull::dangling(), - "must call `__vmctx->set()` before resolving Wasm pointers" - ); - Instance::from_vmctx(VMCTX_AND_MEMORY.0, |handle| { + unsafe { + let ptr = core::ptr::read(p); assert!( - VMCTX_AND_MEMORY.1 < handle.env_module().memories.len(), - "memory index for debugger is out of bounds" + VMCTX_AND_MEMORY.0 != NonNull::dangling(), + "must call `__vmctx->set()` before resolving Wasm pointers" ); - let index = MemoryIndex::new(VMCTX_AND_MEMORY.1); - let mem = handle.get_memory(index); - mem.base.as_ptr().add(ptr as usize) - }) + InstanceAndStore::from_vmctx(VMCTX_AND_MEMORY.0, |handle| { + let (handle, _) = handle.unpack_mut(); + assert!( + VMCTX_AND_MEMORY.1 < handle.env_module().memories.len(), + "memory index for debugger is out of bounds" + ); + let index = MemoryIndex::new(VMCTX_AND_MEMORY.1); + let mem = handle.get_memory(index); + mem.base.as_ptr().add(ptr as usize) + }) + } } #[versioned_export] pub unsafe extern "C" fn set_vmctx_memory(vmctx_ptr: *mut VMContext) { - // TODO multi-memory - VMCTX_AND_MEMORY = (NonNull::new(vmctx_ptr).unwrap(), 0); + unsafe { + // TODO multi-memory + VMCTX_AND_MEMORY = (NonNull::new(vmctx_ptr).unwrap(), 0); + } } /// A bit of a hack around various linkage things. The goal here is to force the diff --git a/crates/wasmtime/src/runtime/vm/gc/enabled/null.rs b/crates/wasmtime/src/runtime/vm/gc/enabled/null.rs index 2506bd081c..00c6bf0467 100644 --- a/crates/wasmtime/src/runtime/vm/gc/enabled/null.rs +++ b/crates/wasmtime/src/runtime/vm/gc/enabled/null.rs @@ -350,7 +350,7 @@ unsafe impl GcHeap for NullHeap { } unsafe fn vmctx_gc_heap_data(&self) -> NonNull { - let ptr_to_next: *mut NonZeroU32 = self.next.get(); + let ptr_to_next: *mut NonZeroU32 = unsafe { self.next.get() }; NonNull::new(ptr_to_next).unwrap().cast() } } diff --git a/crates/wasmtime/src/runtime/vm/gc/gc_runtime.rs b/crates/wasmtime/src/runtime/vm/gc/gc_runtime.rs index 65ff869193..84b9d97926 100644 --- a/crates/wasmtime/src/runtime/vm/gc/gc_runtime.rs +++ b/crates/wasmtime/src/runtime/vm/gc/gc_runtime.rs @@ -619,22 +619,26 @@ impl GcRootsList { /// Add a GC root that is inside a Wasm stack frame to this list. #[inline] pub unsafe fn add_wasm_stack_root(&mut self, ptr_to_root: SendSyncPtr) { - log::trace!( - "Adding Wasm stack root: {:#p} -> {:#p}", - ptr_to_root, - VMGcRef::from_raw_u32(*ptr_to_root.as_ref()).unwrap() - ); - debug_assert!(VMGcRef::from_raw_u32(*ptr_to_root.as_ref()).is_some()); + unsafe { + log::trace!( + "Adding Wasm stack root: {:#p} -> {:#p}", + ptr_to_root, + VMGcRef::from_raw_u32(*ptr_to_root.as_ref()).unwrap() + ); + debug_assert!(VMGcRef::from_raw_u32(*ptr_to_root.as_ref()).is_some()); + } self.0.push(RawGcRoot::Stack(ptr_to_root)); } /// Add a GC root to this list. #[inline] pub unsafe fn add_root(&mut self, ptr_to_root: SendSyncPtr, why: &str) { - log::trace!( - "Adding non-stack root: {why}: {:#p}", - ptr_to_root.as_ref().unchecked_copy() - ); + unsafe { + log::trace!( + "Adding non-stack root: {why}: {:#p}", + ptr_to_root.as_ref().unchecked_copy() + ); + } self.0.push(RawGcRoot::NonStack(ptr_to_root)) } diff --git a/crates/wasmtime/src/runtime/vm/instance.rs b/crates/wasmtime/src/runtime/vm/instance.rs index f7cf7adf3b..98199ee2ce 100644 --- a/crates/wasmtime/src/runtime/vm/instance.rs +++ b/crates/wasmtime/src/runtime/vm/instance.rs @@ -125,11 +125,18 @@ impl InstanceAndStore { f: impl for<'a> FnOnce(&'a mut Self) -> R, ) -> R { const _: () = assert!(mem::size_of::() == mem::size_of::()); - let mut ptr = vmctx - .byte_sub(mem::size_of::()) - .cast::(); + // SAFETY: The validity of this `byte_sub` relies on `vmctx` being a + // valid allocation which is itself a contract of this function. + let mut ptr = unsafe { + vmctx + .byte_sub(mem::size_of::()) + .cast::() + }; - f(ptr.as_mut()) + // SAFETY: the ability to interpret `vmctx` as a safe pointer and + // continue on is a contract of this function itself, so the safety here + // is effectively up to callers. + unsafe { f(ptr.as_mut()) } } /// Unpacks this `InstanceAndStore` into its underlying `Instance` and `dyn @@ -263,7 +270,13 @@ impl Instance { /// /// It is assumed the memory was properly aligned and the /// allocation was `alloc_size` in bytes. - fn new( + /// + /// # Safety + /// + /// The `req.imports` field must be appropriately sized/typed for the module + /// being allocated according to `req.runtime_info`. Additionally `memories` + /// and `tables` must have been allocated for `req.store`. + unsafe fn new( req: InstanceAllocationRequest, memories: PrimaryMap, tables: PrimaryMap, @@ -315,39 +328,65 @@ impl Instance { ret } - /// Converts the provided `*mut VMContext` to an `Instance` pointer and runs - /// the provided closure with the instance. + /// Converts the provided `*mut VMContext` to an `Instance` pointer and + /// returns it with the same lifetime as `self`. /// - /// This method will move the `vmctx` pointer backwards to point to the - /// original `Instance` that precedes it. The closure is provided a - /// temporary version of the `Instance` pointer with a constrained lifetime - /// to the closure to ensure it doesn't accidentally escape. + /// This function can be used when traversing a `VMContext` to reach into + /// the context needed for imports, optionally. /// - /// # Unsafety + /// # Safety /// - /// Callers must validate that the `vmctx` pointer is a valid allocation - /// and that it's valid to acquire `Pin<&mut Instance>` at this time. For example - /// this can't be called twice on the same `VMContext` to get two active - /// pointers to the same `Instance`. + /// This function requires that the `vmctx` pointer is indeed valid and + /// from the store that `self` belongs to. #[inline] - pub unsafe fn from_vmctx( - vmctx: NonNull, - f: impl FnOnce(Pin<&mut Instance>) -> R, - ) -> R { - let mut ptr = vmctx - .byte_sub(mem::size_of::()) - .cast::(); - f(Pin::new_unchecked(ptr.as_mut())) + unsafe fn sibling_vmctx<'a>(&'a self, vmctx: NonNull) -> &'a Instance { + // SAFETY: it's a contract of this function itself that `vmctx` is a + // valid pointer such that this pointer arithmetic is valid. + let ptr = unsafe { + vmctx + .byte_sub(mem::size_of::()) + .cast::() + }; + // SAFETY: it's a contract of this function itself that `vmctx` is a + // valid pointer to dereference. Additionally the lifetime of the return + // value is constrained to be the same as `self` to avoid granting a + // too-long lifetime. + unsafe { ptr.as_ref() } } - /// Returns the `InstanceId` associated with the `vmctx` provided. + /// Same as [`Self::sibling_vmctx`], but the mutable version. /// /// # Safety /// - /// The `vmctx` pointer must be a valid pointer to read the `InstanceId` - /// from. - unsafe fn vmctx_instance_id(vmctx: NonNull) -> InstanceId { - Instance::from_vmctx(vmctx, |i| i.id) + /// This function requires that the `vmctx` pointer is indeed valid and + /// from the store that `self` belongs to. + /// + /// (Note that it is *NOT* required that `vmctx` be distinct from this + /// instance's `vmctx`, or that usage of the resulting instance is limited + /// to its defined items! The returned borrow has the same lifetime as + /// `self`, which means that this instance cannot be used while the + /// resulting instance is in use, and we therefore do not need to worry + /// about mutable aliasing between this instance and the resulting + /// instance.) + #[inline] + unsafe fn sibling_vmctx_mut<'a>( + self: Pin<&'a mut Self>, + vmctx: NonNull, + ) -> Pin<&'a mut Instance> { + // SAFETY: it's a contract of this function itself that `vmctx` is a + // valid pointer such that this pointer arithmetic is valid. + let mut ptr = unsafe { + vmctx + .byte_sub(mem::size_of::()) + .cast::() + }; + + // SAFETY: it's a contract of this function itself that `vmctx` is a + // valid pointer to dereference. Additionally the lifetime of the return + // value is constrained to be the same as `self` to avoid granting a + // too-long lifetime. Finally mutable references to an instance are + // always through `Pin`, so it's safe to create a pin-pointer here. + unsafe { Pin::new_unchecked(ptr.as_mut()) } } pub(crate) fn env_module(&self) -> &Arc { @@ -527,38 +566,42 @@ impl Instance { } pub(crate) unsafe fn set_store(mut self: Pin<&mut Self>, store: Option>) { - *self.as_mut().store_mut() = store.map(VMStoreRawPtr); - if let Some(mut store) = store { - let store = store.as_mut(); - self.vm_store_context() - .write(Some(store.vm_store_context_ptr().into())); - #[cfg(target_has_atomic = "64")] - { - *self.as_mut().epoch_ptr() = - Some(NonNull::from(store.engine().epoch_counter()).into()); - } + // FIXME: should be more targeted ideally with the `unsafe` than just + // throwing this entire function in a large `unsafe` block. + unsafe { + *self.as_mut().store_mut() = store.map(VMStoreRawPtr); + if let Some(mut store) = store { + let store = store.as_mut(); + self.vm_store_context() + .write(Some(store.vm_store_context_ptr().into())); + #[cfg(target_has_atomic = "64")] + { + *self.as_mut().epoch_ptr() = + Some(NonNull::from(store.engine().epoch_counter()).into()); + } - if self.env_module().needs_gc_heap { - self.as_mut().set_gc_heap(Some(store.gc_store().expect( - "if we need a GC heap, then `Instance::new_raw` should have already \ + if self.env_module().needs_gc_heap { + self.as_mut().set_gc_heap(Some(store.gc_store().expect( + "if we need a GC heap, then `Instance::new_raw` should have already \ allocated it for us", - ))); + ))); + } else { + self.as_mut().set_gc_heap(None); + } } else { + self.vm_store_context().write(None); + #[cfg(target_has_atomic = "64")] + { + *self.as_mut().epoch_ptr() = None; + } self.as_mut().set_gc_heap(None); } - } else { - self.vm_store_context().write(None); - #[cfg(target_has_atomic = "64")] - { - *self.as_mut().epoch_ptr() = None; - } - self.as_mut().set_gc_heap(None); } } unsafe fn set_gc_heap(self: Pin<&mut Self>, gc_store: Option<&GcStore>) { if let Some(gc_store) = gc_store { - *self.gc_heap_data() = Some(gc_store.gc_heap.vmctx_gc_heap_data().into()); + *self.gc_heap_data() = Some(unsafe { gc_store.gc_heap.vmctx_gc_heap_data().into() }); } else { *self.gc_heap_data() = None; } @@ -607,7 +650,7 @@ impl Instance { // SAFETY: validity of this `Instance` guarantees validity of the // `vmctx` pointer being read here to find the transitive // `InstanceId` that the import is associated with. - let id = unsafe { Instance::vmctx_instance_id(import.vmctx.as_non_null()) }; + let id = unsafe { self.sibling_vmctx(import.vmctx.as_non_null()).id }; (id, import.index) }; crate::Table::from_raw(StoreInstanceId::new(store, id), def_index) @@ -627,7 +670,7 @@ impl Instance { // SAFETY: validity of this `Instance` guarantees validity of the // `vmctx` pointer being read here to find the transitive // `InstanceId` that the import is associated with. - let id = unsafe { Instance::vmctx_instance_id(import.vmctx.as_non_null()) }; + let id = unsafe { self.sibling_vmctx(import.vmctx.as_non_null()).id }; (id, import.index) }; crate::Memory::from_raw(StoreInstanceId::new(store, id), def_index) @@ -651,7 +694,7 @@ impl Instance { // imports meaning we can read the id of the vmctx within. let id = unsafe { let vmctx = VMContext::from_opaque(import.vmctx.unwrap().as_non_null()); - Instance::vmctx_instance_id(vmctx) + self.sibling_vmctx(vmctx).id }; crate::Global::from_core(StoreInstanceId::new(store, id), index) } @@ -686,7 +729,7 @@ impl Instance { // SAFETY: validity of this `Instance` guarantees validity of the // `vmctx` pointer being read here to find the transitive // `InstanceId` that the import is associated with. - let id = unsafe { Instance::vmctx_instance_id(import.vmctx.as_non_null()) }; + let id = unsafe { self.sibling_vmctx(import.vmctx.as_non_null()).id }; (id, import.index) }; crate::Tag::from_raw(StoreInstanceId::new(store, id), def_index) @@ -701,19 +744,6 @@ impl Instance { self.env_module().exports.iter() } - /// Return the table index for the given `VMTableDefinition`. - pub unsafe fn table_index(&self, table: &VMTableDefinition) -> DefinedTableIndex { - let index = DefinedTableIndex::new( - usize::try_from( - (table as *const VMTableDefinition) - .offset_from(self.table_ptr(DefinedTableIndex::new(0)).as_ptr()), - ) - .unwrap(), - ); - assert!(index.index() < self.tables.len()); - index - } - /// Grow memory by the specified amount of pages. /// /// Returns `None` if memory can't be grown by the specified amount @@ -743,7 +773,7 @@ impl Instance { self: Pin<&mut Self>, table_index: TableIndex, ) -> TableElementType { - unsafe { (*self.get_table(table_index)).element_type() } + self.get_table(table_index).element_type() } /// Grow table by the specified amount of elements, filling them with @@ -958,7 +988,7 @@ impl Instance { pub(crate) fn table_init_segment( store: &mut StoreOpaque, - id: InstanceId, + elements_instance_id: InstanceId, const_evaluator: &mut ConstExprEvaluator, table_index: TableIndex, elements: &TableSegmentElements, @@ -968,14 +998,56 @@ impl Instance { ) -> Result<(), Trap> { // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-init - let mut instance = store.instance_mut(id); - let table = unsafe { &mut *instance.as_mut().get_table(table_index) }; + let elements_instance = store.instance_mut(elements_instance_id); + let elements_module = elements_instance.env_module(); + let top = elements_module.tables[table_index].ref_type.heap_type.top(); + let (defined_table_index, mut table_instance) = + elements_instance.defined_table_index_and_instance(table_index); + let table_instance_id = table_instance.id; + let src = usize::try_from(src).map_err(|_| Trap::TableOutOfBounds)?; let len = usize::try_from(len).map_err(|_| Trap::TableOutOfBounds)?; - let module = instance.env_module().clone(); + + // In the initialization below we need to simultaneously have a mutable + // borrow on the `Table` that we're initializing and the `StoreOpaque` + // that it comes from. To solve this the tables are temporarily removed + // from the instance at `id` to be re-inserted at the end of this + // function via a `Drop` helper. The table and the store are then + // accessed through the drop helper below. + // + // This will cause a runtime panic if the table is actually accessed + // during the lifetime of the functions below, but that's a bug if that + // happens which needs to be fixed anyway. + let tables = mem::replace(table_instance.as_mut().tables_mut(), PrimaryMap::new()); + let mut replace = ReplaceTables { + tables, + id: table_instance_id, + store, + }; + + struct ReplaceTables<'a> { + tables: PrimaryMap, + id: InstanceId, + store: &'a mut StoreOpaque, + } + + impl Drop for ReplaceTables<'_> { + fn drop(&mut self) { + mem::swap( + self.store.instance_mut(self.id).tables_mut(), + &mut self.tables, + ); + debug_assert!(self.tables.is_empty()); + } + } + + // Reborrow the table/store from `replace` for the below code. + let table = &mut replace.tables[defined_table_index].1; + let store = &mut *replace.store; match elements { TableSegmentElements::Functions(funcs) => { + let mut instance = store.instance_mut(elements_instance_id); let elements = funcs .get(src..) .and_then(|s| s.get(..len)) @@ -992,8 +1064,7 @@ impl Instance { .get(src..) .and_then(|s| s.get(..len)) .ok_or(Trap::TableOutOfBounds)?; - let top = module.tables[table_index].ref_type.heap_type.top(); - let mut context = ConstEvalContext::new(id); + let mut context = ConstEvalContext::new(elements_instance_id); match top { WasmHeapTopType::Extern => table.init_gc_refs( dst, @@ -1216,12 +1287,9 @@ impl Instance { self: Pin<&mut Self>, table_index: TableIndex, range: impl Iterator, - ) -> *mut Table { - self.with_defined_table_index_and_instance(table_index, |idx, instance| { - // FIXME(#11179) shouldn't need to subvert the borrow checker - let ret: *mut Table = instance.get_defined_table_with_lazy_init(idx, range); - ret - }) + ) -> &mut Table { + let (idx, instance) = self.defined_table_index_and_instance(table_index); + instance.get_defined_table_with_lazy_init(idx, range) } /// Gets the raw runtime table data structure owned by this instance @@ -1279,12 +1347,9 @@ impl Instance { /// Get a table by index regardless of whether it is locally-defined or an /// imported, foreign table. - pub(crate) fn get_table(self: Pin<&mut Self>, table_index: TableIndex) -> *mut Table { - self.with_defined_table_index_and_instance(table_index, |idx, instance| unsafe { - // SAFETY: the `unsafe` here is projecting from `*mut (A, B)` to - // `*mut A`, which should be a safe operation to do. - &raw mut (*instance.tables_mut().get_raw_mut(idx).unwrap()).1 - }) + pub(crate) fn get_table(self: Pin<&mut Self>, table_index: TableIndex) -> &mut Table { + let (idx, instance) = self.defined_table_index_and_instance(table_index); + instance.get_defined_table(idx) } /// Get a locally-defined table. @@ -1292,22 +1357,22 @@ impl Instance { &mut self.tables_mut()[index].1 } - pub(crate) fn with_defined_table_index_and_instance( - self: Pin<&mut Self>, + pub(crate) fn defined_table_index_and_instance<'a>( + self: Pin<&'a mut Self>, index: TableIndex, - f: impl FnOnce(DefinedTableIndex, Pin<&mut Instance>) -> R, - ) -> R { + ) -> (DefinedTableIndex, Pin<&'a mut Instance>) { if let Some(defined_table_index) = self.env_module().defined_table_index(index) { - f(defined_table_index, self) + (defined_table_index, self) } else { let import = self.imported_table(index); - unsafe { - Instance::from_vmctx(import.vmctx.as_non_null(), |foreign_instance| { - let foreign_table_def = import.from.as_ptr(); - let foreign_table_index = foreign_instance.table_index(&*foreign_table_def); - f(foreign_table_index, foreign_instance) - }) - } + let index = import.index; + let vmctx = import.vmctx.as_non_null(); + // SAFETY: the validity of `self` means that the reachable instances + // should also all be owned by the same store and fully initialized, + // so it's safe to laterally move from a mutable borrow of this + // instance to a mutable borrow of a sibling instance. + let foreign_instance = unsafe { self.sibling_vmctx_mut(vmctx) }; + (index, foreign_instance) } } @@ -1325,57 +1390,80 @@ impl Instance { ) { assert!(ptr::eq(module, self.env_module().as_ref())); - self.vmctx_plus_offset_raw::(offsets.ptr.vmctx_magic()) - .write(VMCONTEXT_MAGIC); - self.as_mut().set_store(store.as_raw()); + // SAFETY: the type of the magic field is indeed `u32` and this function + // is initializing its value. + unsafe { + self.vmctx_plus_offset_raw::(offsets.ptr.vmctx_magic()) + .write(VMCONTEXT_MAGIC); + } + + // SAFETY: it's up to the caller to provide a valid store pointer here. + unsafe { + self.as_mut().set_store(store.as_raw()); + } // Initialize shared types - let types = NonNull::from(self.runtime_info.type_ids()); - self.type_ids_array().write(types.cast().into()); + // + // SAFETY: validity of the vmctx means it should be safe to write to it + // here. + unsafe { + let types = NonNull::from(self.runtime_info.type_ids()); + self.type_ids_array().write(types.cast().into()); + } // Initialize the built-in functions - static BUILTINS: VMBuiltinFunctionsArray = VMBuiltinFunctionsArray::INIT; - let ptr = BUILTINS.expose_provenance(); - self.vmctx_plus_offset_raw(offsets.ptr.vmctx_builtin_functions()) - .write(VmPtr::from(ptr)); + // + // SAFETY: the type of the builtin functions field is indeed a pointer + // and the pointer being filled in here, plus the vmctx is valid to + // write to during initialization. + unsafe { + static BUILTINS: VMBuiltinFunctionsArray = VMBuiltinFunctionsArray::INIT; + let ptr = BUILTINS.expose_provenance(); + self.vmctx_plus_offset_raw(offsets.ptr.vmctx_builtin_functions()) + .write(VmPtr::from(ptr)); + } // Initialize the imports + // + // SAFETY: the vmctx is safe to initialize during this function and + // validity of each item itself is a contract the caller must uphold. debug_assert_eq!(imports.functions.len(), module.num_imported_funcs); - ptr::copy_nonoverlapping( - imports.functions.as_ptr(), - self.vmctx_plus_offset_raw(offsets.vmctx_imported_functions_begin()) - .as_ptr(), - imports.functions.len(), - ); - debug_assert_eq!(imports.tables.len(), module.num_imported_tables); - ptr::copy_nonoverlapping( - imports.tables.as_ptr(), - self.vmctx_plus_offset_raw(offsets.vmctx_imported_tables_begin()) - .as_ptr(), - imports.tables.len(), - ); - debug_assert_eq!(imports.memories.len(), module.num_imported_memories); - ptr::copy_nonoverlapping( - imports.memories.as_ptr(), - self.vmctx_plus_offset_raw(offsets.vmctx_imported_memories_begin()) - .as_ptr(), - imports.memories.len(), - ); - debug_assert_eq!(imports.globals.len(), module.num_imported_globals); - ptr::copy_nonoverlapping( - imports.globals.as_ptr(), - self.vmctx_plus_offset_raw(offsets.vmctx_imported_globals_begin()) - .as_ptr(), - imports.globals.len(), - ); - - debug_assert_eq!(imports.tags.len(), module.num_imported_tags); - ptr::copy_nonoverlapping( - imports.tags.as_ptr(), - self.vmctx_plus_offset_raw(offsets.vmctx_imported_tags_begin()) - .as_ptr(), - imports.tags.len(), - ); + unsafe { + ptr::copy_nonoverlapping( + imports.functions.as_ptr(), + self.vmctx_plus_offset_raw(offsets.vmctx_imported_functions_begin()) + .as_ptr(), + imports.functions.len(), + ); + debug_assert_eq!(imports.tables.len(), module.num_imported_tables); + ptr::copy_nonoverlapping( + imports.tables.as_ptr(), + self.vmctx_plus_offset_raw(offsets.vmctx_imported_tables_begin()) + .as_ptr(), + imports.tables.len(), + ); + debug_assert_eq!(imports.memories.len(), module.num_imported_memories); + ptr::copy_nonoverlapping( + imports.memories.as_ptr(), + self.vmctx_plus_offset_raw(offsets.vmctx_imported_memories_begin()) + .as_ptr(), + imports.memories.len(), + ); + debug_assert_eq!(imports.globals.len(), module.num_imported_globals); + ptr::copy_nonoverlapping( + imports.globals.as_ptr(), + self.vmctx_plus_offset_raw(offsets.vmctx_imported_globals_begin()) + .as_ptr(), + imports.globals.len(), + ); + debug_assert_eq!(imports.tags.len(), module.num_imported_tags); + ptr::copy_nonoverlapping( + imports.tags.as_ptr(), + self.vmctx_plus_offset_raw(offsets.vmctx_imported_tags_begin()) + .as_ptr(), + imports.tags.len(), + ); + } // N.B.: there is no need to initialize the funcrefs array because we // eagerly construct each element in it whenever asked for a reference @@ -1383,11 +1471,17 @@ impl Instance { // the lazy-init, so we don't need to initialize any state now. // Initialize the defined tables - let mut ptr = self.vmctx_plus_offset_raw(offsets.vmctx_tables_begin()); - let tables = self.as_mut().tables_mut(); - for i in 0..module.num_defined_tables() { - ptr.write(tables[DefinedTableIndex::new(i)].1.vmtable()); - ptr = ptr.add(1); + // + // SAFETY: it's safe to initialize these tables during initialization + // here and the various types of pointers and such here should all be + // valid. + unsafe { + let mut ptr = self.vmctx_plus_offset_raw(offsets.vmctx_tables_begin()); + let tables = self.as_mut().tables_mut(); + for i in 0..module.num_defined_tables() { + ptr.write(tables[DefinedTableIndex::new(i)].1.vmtable()); + ptr = ptr.add(1); + } } // Initialize the defined memories. This fills in both the @@ -1395,45 +1489,66 @@ impl Instance { // time. Entries in `defined_memories` hold a pointer to a definition // (all memories) whereas the `owned_memories` hold the actual // definitions of memories owned (not shared) in the module. - let mut ptr = self.vmctx_plus_offset_raw(offsets.vmctx_memories_begin()); - let mut owned_ptr = self.vmctx_plus_offset_raw(offsets.vmctx_owned_memories_begin()); - let memories = self.as_mut().memories_mut(); - for i in 0..module.num_defined_memories() { - let defined_memory_index = DefinedMemoryIndex::new(i); - let memory_index = module.memory_index(defined_memory_index); - if module.memories[memory_index].shared { - let def_ptr = memories[defined_memory_index] - .1 - .as_shared_memory() - .unwrap() - .vmmemory_ptr(); - ptr.write(VmPtr::from(def_ptr)); - } else { - owned_ptr.write(memories[defined_memory_index].1.vmmemory()); - ptr.write(VmPtr::from(owned_ptr)); - owned_ptr = owned_ptr.add(1); + // + // SAFETY: it's safe to initialize these memories during initialization + // here and the various types of pointers and such here should all be + // valid. + unsafe { + let mut ptr = self.vmctx_plus_offset_raw(offsets.vmctx_memories_begin()); + let mut owned_ptr = self.vmctx_plus_offset_raw(offsets.vmctx_owned_memories_begin()); + let memories = self.as_mut().memories_mut(); + for i in 0..module.num_defined_memories() { + let defined_memory_index = DefinedMemoryIndex::new(i); + let memory_index = module.memory_index(defined_memory_index); + if module.memories[memory_index].shared { + let def_ptr = memories[defined_memory_index] + .1 + .as_shared_memory() + .unwrap() + .vmmemory_ptr(); + ptr.write(VmPtr::from(def_ptr)); + } else { + owned_ptr.write(memories[defined_memory_index].1.vmmemory()); + ptr.write(VmPtr::from(owned_ptr)); + owned_ptr = owned_ptr.add(1); + } + ptr = ptr.add(1); } - ptr = ptr.add(1); } // Zero-initialize the globals so that nothing is uninitialized memory // after this function returns. The globals are actually initialized // with their const expression initializers after the instance is fully // allocated. - for (index, _init) in module.global_initializers.iter() { - self.global_ptr(index).write(VMGlobalDefinition::new()); + // + // SAFETY: it's safe to initialize globals during initialization + // here. Note that while the value being written is not valid for all + // types of globals it's initializing the memory to zero instead of + // being in an undefined state. So it's still unsafe to access globals + // after this, but if it's read then it'd hopefully crash faster than + // leaving this undefined. + unsafe { + for (index, _init) in module.global_initializers.iter() { + self.global_ptr(index).write(VMGlobalDefinition::new()); + } } // Initialize the defined tags - let mut ptr = self.vmctx_plus_offset_raw(offsets.vmctx_tags_begin()); - for i in 0..module.num_defined_tags() { - let defined_index = DefinedTagIndex::new(i); - let tag_index = module.tag_index(defined_index); - let tag = module.tags[tag_index]; - ptr.write(VMTagDefinition::new( - tag.signature.unwrap_engine_type_index(), - )); - ptr = ptr.add(1); + // + // SAFETY: it's safe to initialize these tags during initialization + // here and the various types of pointers and such here should all be + // valid. + unsafe { + let mut ptr = self.vmctx_plus_offset_raw(offsets.vmctx_tags_begin()); + for i in 0..module.num_defined_tags() { + let defined_index = DefinedTagIndex::new(i); + let tag_index = module.tag_index(defined_index); + let tag = module.tags[tag_index]; + ptr.write(VMTagDefinition::new( + tag.signature.unwrap_engine_type_index(), + )); + ptr = ptr.add(1); + } } } @@ -1549,7 +1664,7 @@ impl Instance { unsafe { &mut self.get_unchecked_mut().memories } } - fn tables_mut( + pub(crate) fn tables_mut( self: Pin<&mut Self>, ) -> &mut PrimaryMap { // SAFETY: see `store_mut` above. diff --git a/crates/wasmtime/src/runtime/vm/instance/allocator.rs b/crates/wasmtime/src/runtime/vm/instance/allocator.rs index fbfd8fea1a..2ad773b3ad 100644 --- a/crates/wasmtime/src/runtime/vm/instance/allocator.rs +++ b/crates/wasmtime/src/runtime/vm/instance/allocator.rs @@ -122,7 +122,7 @@ impl StorePtr { /// /// Safety: must not be used outside the original lifetime of the borrow. pub(crate) unsafe fn get(&mut self) -> Option<&mut dyn VMStore> { - let ptr = self.0?.as_mut(); + let ptr = unsafe { self.0?.as_mut() }; Some(ptr) } } @@ -253,13 +253,7 @@ pub unsafe trait InstanceAllocatorImpl { fn decrement_core_instance_count(&self); /// Allocate a memory for an instance. - /// - /// # Unsafety - /// - /// The memory and its associated module must have already been validated by - /// `Self::validate_memory` (or transtively via - /// `Self::validate_{module,component}`) and passed that validation. - unsafe fn allocate_memory( + fn allocate_memory( &self, request: &mut InstanceAllocationRequest, ty: &wasmtime_environ::Memory, @@ -282,12 +276,7 @@ pub unsafe trait InstanceAllocatorImpl { ); /// Allocate a table for an instance. - /// - /// # Unsafety - /// - /// The table and its associated module must have already been validated by - /// `Self::validate_module` and passed that validation. - unsafe fn allocate_table( + fn allocate_table( &self, req: &mut InstanceAllocationRequest, table: &wasmtime_environ::Table, @@ -409,10 +398,10 @@ pub trait InstanceAllocator: InstanceAllocatorImpl { /// Note that the returned instance must still have `.initialize(..)` called /// on it to complete the instantiation process. /// - /// # Unsafety + /// # Safety /// - /// The request's associated module, memories, tables, and vmctx must have - /// already have been validated by `Self::validate_module`. + /// The `request` provided must be valid, e.g. the imports within are + /// correctly sized/typed for the instance being created. unsafe fn allocate_module( &self, mut request: InstanceAllocationRequest, @@ -436,10 +425,16 @@ pub trait InstanceAllocator: InstanceAllocatorImpl { self.allocate_tables(&mut request, &mut tables)?; Ok(()) })() { - Ok(_) => Ok(Instance::new(request, memories, tables, &module.memories)), + // SAFETY: memories/tables were just allocated from the store within + // `request` and this function's own contract requires that the + // imports are valid. + Ok(_) => unsafe { Ok(Instance::new(request, memories, tables, &module.memories)) }, Err(e) => { - self.deallocate_memories(&mut memories); - self.deallocate_tables(&mut tables); + // SAFETY: these were previously allocated by this allocator + unsafe { + self.deallocate_memories(&mut memories); + self.deallocate_tables(&mut tables); + } self.decrement_core_instance_count(); Err(e) } @@ -455,20 +450,20 @@ pub trait InstanceAllocator: InstanceAllocatorImpl { /// /// The instance must have previously been allocated by `Self::allocate`. unsafe fn deallocate_module(&self, handle: &mut InstanceHandle) { - self.deallocate_memories(handle.get_mut().memories_mut()); - self.deallocate_tables(handle.get_mut().tables_mut()); + // SAFETY: the contract of `deallocate_*` is itself a contract of this + // function, that the memories/tables were previously allocated from + // here. + unsafe { + self.deallocate_memories(handle.get_mut().memories_mut()); + self.deallocate_tables(handle.get_mut().tables_mut()); + } self.decrement_core_instance_count(); } /// Allocate the memories for the given instance allocation request, pushing /// them into `memories`. - /// - /// # Unsafety - /// - /// The request's associated module and memories must have previously been - /// validated by `Self::validate_module`. - unsafe fn allocate_memories( + fn allocate_memories( &self, request: &mut InstanceAllocationRequest, memories: &mut PrimaryMap, @@ -484,12 +479,8 @@ pub trait InstanceAllocator: InstanceAllocatorImpl { .defined_memory_index(memory_index) .expect("should be a defined memory since we skipped imported ones"); - memories.push(self.allocate_memory( - request, - ty, - request.tunables, - Some(memory_index), - )?); + let memory = self.allocate_memory(request, ty, request.tunables, Some(memory_index))?; + memories.push(memory); } Ok(()) @@ -510,18 +501,19 @@ pub trait InstanceAllocator: InstanceAllocatorImpl { // about leaking subsequent memories if the first memory failed to // deallocate. If deallocating memory ever becomes fallible, we will // need to be careful here! - self.deallocate_memory(Some(memory_index), allocation_index, memory); + // + // SAFETY: the unsafe contract here is the same as the unsafe + // contract of this function, that the memories were previously + // allocated by this allocator. + unsafe { + self.deallocate_memory(Some(memory_index), allocation_index, memory); + } } } /// Allocate tables for the given instance allocation request, pushing them /// into `tables`. - /// - /// # Unsafety - /// - /// The request's associated module and tables must have previously been - /// validated by `Self::validate_module`. - unsafe fn allocate_tables( + fn allocate_tables( &self, request: &mut InstanceAllocationRequest, tables: &mut PrimaryMap, @@ -537,7 +529,8 @@ pub trait InstanceAllocator: InstanceAllocatorImpl { .defined_table_index(index) .expect("should be a defined table since we skipped imported ones"); - tables.push(self.allocate_table(request, table, request.tunables, def_index)?); + let table = self.allocate_table(request, table, request.tunables, def_index)?; + tables.push(table); } Ok(()) @@ -554,7 +547,11 @@ pub trait InstanceAllocator: InstanceAllocatorImpl { tables: &mut PrimaryMap, ) { for (table_index, (allocation_index, table)) in mem::take(tables) { - self.deallocate_table(table_index, allocation_index, table); + // SAFETY: the tables here were allocated from this allocator per + // the contract on this function itself. + unsafe { + self.deallocate_table(table_index, allocation_index, table); + } } } } @@ -572,7 +569,6 @@ fn check_table_init_bounds( let mut const_evaluator = ConstExprEvaluator::default(); for segment in module.table_initialization.segments.iter() { - let table = unsafe { &*store.instance_mut(instance).get_table(segment.table_index) }; let mut context = ConstEvalContext::new(instance); let start = unsafe { const_evaluator @@ -582,6 +578,7 @@ fn check_table_init_bounds( let start = usize::try_from(start.get_u32()).unwrap(); let end = start.checked_add(usize::try_from(segment.elements.len()).unwrap()); + let table = store.instance_mut(instance).get_table(segment.table_index); match end { Some(end) if end <= table.size() => { // Initializer is in bounds diff --git a/crates/wasmtime/src/runtime/vm/instance/allocator/on_demand.rs b/crates/wasmtime/src/runtime/vm/instance/allocator/on_demand.rs index 4c3e4f0370..6281b45b20 100644 --- a/crates/wasmtime/src/runtime/vm/instance/allocator/on_demand.rs +++ b/crates/wasmtime/src/runtime/vm/instance/allocator/on_demand.rs @@ -110,7 +110,7 @@ unsafe impl InstanceAllocatorImpl for OnDemandInstanceAllocator { fn decrement_core_instance_count(&self) {} - unsafe fn allocate_memory( + fn allocate_memory( &self, request: &mut InstanceAllocationRequest, ty: &wasmtime_environ::Memory, @@ -133,10 +133,12 @@ unsafe impl InstanceAllocatorImpl for OnDemandInstanceAllocator { ty, tunables, creator, - request - .store - .get() - .expect("if module has memory plans, store is not empty"), + unsafe { + request + .store + .get() + .expect("if module has memory plans, store is not empty") + }, image, )?; Ok((allocation_index, memory)) @@ -152,7 +154,7 @@ unsafe impl InstanceAllocatorImpl for OnDemandInstanceAllocator { // Normal destructors do all the necessary clean up. } - unsafe fn allocate_table( + fn allocate_table( &self, request: &mut InstanceAllocationRequest, ty: &wasmtime_environ::Table, @@ -160,14 +162,12 @@ unsafe impl InstanceAllocatorImpl for OnDemandInstanceAllocator { _table_index: DefinedTableIndex, ) -> Result<(TableAllocationIndex, Table)> { let allocation_index = TableAllocationIndex::default(); - let table = Table::new_dynamic( - ty, - tunables, + let table = Table::new_dynamic(ty, tunables, unsafe { request .store .get() - .expect("if module has table plans, store is not empty"), - )?; + .expect("if module has table plans, store is not empty") + })?; Ok((allocation_index, table)) } diff --git a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs index da9842ca54..2e592d9ab4 100644 --- a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs +++ b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs @@ -622,7 +622,7 @@ unsafe impl InstanceAllocatorImpl for PoolingInstanceAllocator { self.live_core_instances.fetch_sub(1, Ordering::AcqRel); } - unsafe fn allocate_memory( + fn allocate_memory( &self, request: &mut InstanceAllocationRequest, ty: &wasmtime_environ::Memory, @@ -646,14 +646,24 @@ unsafe impl InstanceAllocatorImpl for PoolingInstanceAllocator { let mut queue = DecommitQueue::default(); image .clear_and_remain_ready(self.memories.keep_resident, |ptr, len| { - queue.push_raw(ptr, len); + // SAFETY: the memory in `image` won't be used until this + // decommit queue is flushed, and by definition the memory is + // not in use when calling this function. + unsafe { + queue.push_raw(ptr, len); + } }) .expect("failed to reset memory image"); - queue.push_memory(allocation_index, image); + + // SAFETY: this image is not in use and its memory regions were enqueued + // with `push_raw` above. + unsafe { + queue.push_memory(allocation_index, image); + } self.merge_or_flush(queue); } - unsafe fn allocate_table( + fn allocate_table( &self, request: &mut InstanceAllocationRequest, ty: &wasmtime_environ::Table, @@ -670,11 +680,21 @@ unsafe impl InstanceAllocatorImpl for PoolingInstanceAllocator { mut table: Table, ) { let mut queue = DecommitQueue::default(); - self.tables - .reset_table_pages_to_zero(allocation_index, &mut table, |ptr, len| { - queue.push_raw(ptr, len); - }); - queue.push_table(allocation_index, table); + // SAFETY: This table is no longer in use by the allocator when this + // method is called and additionally all image ranges are pushed with + // the understanding that the memory won't get used until the whole + // queue is flushed. + unsafe { + self.tables + .reset_table_pages_to_zero(allocation_index, &mut table, |ptr, len| { + queue.push_raw(ptr, len); + }); + } + + // SAFETY: the table has had all its memory regions enqueued above. + unsafe { + queue.push_table(allocation_index, table); + } self.merge_or_flush(queue); } @@ -686,9 +706,17 @@ unsafe impl InstanceAllocatorImpl for PoolingInstanceAllocator { #[cfg(feature = "async")] unsafe fn deallocate_fiber_stack(&self, mut stack: wasmtime_fiber::FiberStack) { let mut queue = DecommitQueue::default(); - self.stacks - .zero_stack(&mut stack, |ptr, len| queue.push_raw(ptr, len)); - queue.push_stack(stack); + // SAFETY: the stack is no longer in use by definition when this + // function is called and memory ranges pushed here are otherwise no + // longer in use. + unsafe { + self.stacks + .zero_stack(&mut stack, |ptr, len| queue.push_raw(ptr, len)); + } + // SAFETY: this stack's memory regions were enqueued above. + unsafe { + queue.push_stack(stack); + } self.merge_or_flush(queue); } diff --git a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/index_allocator.rs b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/index_allocator.rs index 667292da4a..7f257fde44 100644 --- a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/index_allocator.rs +++ b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/index_allocator.rs @@ -583,7 +583,7 @@ mod test { #[test] fn test_affinity_allocation_strategy_random() { use rand::Rng; - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); let ids = std::iter::repeat_with(|| { MemoryInModule(CompiledModuleId::new(), DefinedMemoryIndex::new(0)) @@ -598,12 +598,12 @@ mod test { let amt = if cfg!(miri) { 100 } else { 100_000 }; for _ in 0..amt { loop { - if !allocated.is_empty() && rng.gen_bool(0.5) { - let i = rng.gen_range(0..allocated.len()); + if !allocated.is_empty() && rng.random_bool(0.5) { + let i = rng.random_range(0..allocated.len()); let to_free_idx = allocated.swap_remove(i); state.free(to_free_idx); } else { - let id = ids[rng.gen_range(0..ids.len())]; + let id = ids[rng.random_range(0..ids.len())]; let index = match state.alloc(Some(id)) { Some(id) => id, None => continue, diff --git a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/table_pool.rs b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/table_pool.rs index 3c1338f456..13e2629c6e 100644 --- a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/table_pool.rs +++ b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/table_pool.rs @@ -204,17 +204,27 @@ impl TablePool { ) { assert!(table.is_static()); let base = self.get(allocation_index); - let size = HostAlignedByteCount::new_rounded_up(self.data_size(table.element_type())) + let table_byte_size = table.size() * table.element_type().element_size(); + let table_byte_size_page_aligned = HostAlignedByteCount::new_rounded_up(table_byte_size) .expect("table entry size doesn't overflow"); // `memset` the first `keep_resident` bytes. - let size_to_memset = size.min(self.keep_resident); - std::ptr::write_bytes(base, 0, size_to_memset.byte_count()); + let size_to_memset = table_byte_size_page_aligned.min(self.keep_resident); + + // SAFETY: the contract of this function requires that the table is not + // actively in use so it's safe to pave over its allocation with zero + // bytes. + unsafe { + std::ptr::write_bytes(base, 0, size_to_memset.byte_count()); + } // And decommit the rest of it. decommit( - base.add(size_to_memset.byte_count()), - size.checked_sub(size_to_memset) + // SAFETY: `size_to_memset` is less than the size of the allocation, + // so it's safe to use the `add` intrinsic. + unsafe { base.add(size_to_memset.byte_count()) }, + table_byte_size_page_aligned + .checked_sub(size_to_memset) .expect("size_to_memset <= size") .byte_count(), ); diff --git a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/unix_stack_pool.rs b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/unix_stack_pool.rs index 941f7178a8..12bfe2bdb9 100644 --- a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/unix_stack_pool.rs +++ b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/unix_stack_pool.rs @@ -190,11 +190,16 @@ impl StackPool { let rest = stack_size .checked_sub(size_to_memset) .expect("stack_size >= size_to_memset"); - std::ptr::write_bytes( - (bottom_of_stack + rest.byte_count()) as *mut u8, - 0, - size_to_memset.byte_count(), - ); + + // SAFETY: this function's own contract requires that the stack is not + // in use so it's safe to pave over part of it with zero. + unsafe { + std::ptr::write_bytes( + (bottom_of_stack + rest.byte_count()) as *mut u8, + 0, + size_to_memset.byte_count(), + ); + } // Use the system to reset remaining stack pages to zero. decommit(bottom_of_stack as _, rest.byte_count()); diff --git a/crates/wasmtime/src/runtime/vm/interpreter.rs b/crates/wasmtime/src/runtime/vm/interpreter.rs index cba40e864d..3e8e3023fc 100644 --- a/crates/wasmtime/src/runtime/vm/interpreter.rs +++ b/crates/wasmtime/src/runtime/vm/interpreter.rs @@ -104,7 +104,7 @@ unsafe impl Unwind for UnwindPulley { unsafe fn get_next_older_pc_from_fp(&self, fp: usize) -> usize { // The calling convention always pushes the return pointer (aka the PC // of the next older frame) just before this frame. - *(fp as *mut usize).offset(1) + unsafe { *(fp as *mut usize).offset(1) } } fn assert_fp_is_aligned(&self, fp: usize) { let expected = if cfg!(target_pointer_width = "32") { @@ -209,16 +209,16 @@ impl InterpreterRef<'_> { // correct as it's not saving all callee-save state. let setjmp = setjmp(vm); - let old_lr = vm.call_start(&args); + let old_lr = unsafe { vm.call_start(&args) }; // Run the interpreter as much as possible until it finishes, and then // handle each finish condition differently. let ret = loop { - match vm.call_run(bytecode) { + match unsafe { vm.call_run(bytecode) } { // If the VM returned entirely then read the return value and // return that (it indicates whether a trap happened or not. DoneReason::ReturnToHost(()) => { - match vm.call_end(old_lr, [RegType::XReg]).next().unwrap() { + match unsafe { vm.call_end(old_lr, [RegType::XReg]).next().unwrap() } { #[allow( clippy::cast_possible_truncation, reason = "intentionally reading the lower bits only" @@ -238,7 +238,7 @@ impl InterpreterRef<'_> { longjmp(vm, setjmp); break false; } else { - vm = self.call_indirect_host(id); + vm = unsafe { self.call_indirect_host(id) }; bytecode = resume; } } @@ -300,7 +300,7 @@ impl InterpreterRef<'_> { // Decode each argument according to this macro, pulling // arguments from successive registers. - let ret = { + let ret = unsafe { let mut vm = self.vm(); // Convert the pointer from pulley to a native function pointer. union GetNative { @@ -423,7 +423,7 @@ impl InterpreterRef<'_> { use wasmtime_environ::component::ComponentBuiltinFunctionIndex; if id == const { HostCall::ComponentLowerImport.index() } { - call!(@host VMLoweringCallee(nonnull, nonnull, u32, u32, nonnull, ptr, ptr, u8, u8, nonnull, size) -> bool); + call!(@host VMLoweringCallee(nonnull, nonnull, u32, u32, nonnull, size) -> bool); } macro_rules! component { diff --git a/crates/wasmtime/src/runtime/vm/libcalls.rs b/crates/wasmtime/src/runtime/vm/libcalls.rs index 11cb8e620c..5eb70acdc6 100644 --- a/crates/wasmtime/src/runtime/vm/libcalls.rs +++ b/crates/wasmtime/src/runtime/vm/libcalls.rs @@ -57,9 +57,10 @@ #[cfg(feature = "stack-switching")] use super::stack_switching::VMContObj; use crate::prelude::*; +use crate::runtime::store::StoreInstanceId; #[cfg(feature = "gc")] use crate::runtime::vm::VMGcRef; -use crate::runtime::vm::table::{Table, TableElementType}; +use crate::runtime::vm::table::TableElementType; use crate::runtime::vm::vmcontext::VMFuncRef; use crate::runtime::vm::{ HostResultHasUnwindSentinel, Instance, TrapReason, VMStore, f32x4, f64x2, i8x16, @@ -120,7 +121,7 @@ pub mod raw { ) $(-> libcall!(@ty $result))? { $(#[cfg($attr)])? { - crate::runtime::vm::traphandlers::catch_unwind_and_record_trap(|| { + crate::runtime::vm::traphandlers::catch_unwind_and_record_trap(|| unsafe { InstanceAndStore::from_vmctx(vmctx, |pair| { let (instance, store) = pair.unpack_mut(); super::$name(store, instance, $($pname),*) @@ -289,7 +290,8 @@ unsafe fn table_grow_cont_obj( instance.as_mut().table_element_type(table_index), TableElementType::Cont, )); - let element = VMContObj::from_raw_parts(init_value_contref, init_value_revision).into(); + let element = + unsafe { VMContObj::from_raw_parts(init_value_contref, init_value_revision).into() }; let result = instance .defined_table_grow(store, defined_table_index, delta, element)? .map(AllocationSize); @@ -357,7 +359,7 @@ unsafe fn table_fill_cont_obj( let table = instance.get_defined_table(table_index); match table.element_type() { TableElementType::Cont => { - let contobj = VMContObj::from_raw_parts(value_contref, value_revision); + let contobj = unsafe { VMContObj::from_raw_parts(value_contref, value_revision) }; table.fill(store.optional_gc_store_mut(), dst, contobj.into(), len)?; Ok(()) } @@ -366,7 +368,7 @@ unsafe fn table_fill_cont_obj( } // Implementation of `table.copy`. -unsafe fn table_copy( +fn table_copy( store: &mut dyn VMStore, mut instance: Pin<&mut Instance>, dst_table_index: u32, @@ -374,17 +376,34 @@ unsafe fn table_copy( dst: u64, src: u64, len: u64, -) -> Result<()> { +) -> Result<(), Trap> { let dst_table_index = TableIndex::from_u32(dst_table_index); let src_table_index = TableIndex::from_u32(src_table_index); let store = store.store_opaque_mut(); - let dst_table = instance.as_mut().get_table(dst_table_index); - // Lazy-initialize the whole range in the source table first. - let src_range = src..(src.checked_add(len).unwrap_or(u64::MAX)); - let src_table = instance.get_table_with_lazy_init(src_table_index, src_range); - let gc_store = store.optional_gc_store_mut(); - Table::copy(gc_store, dst_table, src_table, dst, src, len)?; - Ok(()) + + // Convert the two table indices relative to `instance` into two + // defining instances and the defined table index within that instance. + let (dst_def_index, dst_instance) = instance + .as_mut() + .defined_table_index_and_instance(dst_table_index); + let dst_instance_id = dst_instance.id(); + let (src_def_index, src_instance) = instance + .as_mut() + .defined_table_index_and_instance(src_table_index); + let src_instance_id = src_instance.id(); + + let src_table = crate::Table::from_raw( + StoreInstanceId::new(store.id(), src_instance_id), + src_def_index, + ); + let dst_table = crate::Table::from_raw( + StoreInstanceId::new(store.id(), dst_instance_id), + dst_def_index, + ); + + // SAFETY: this is only safe if the two tables have the same type, and that + // was validated during wasm-validation time. + unsafe { crate::Table::copy_raw(store, &dst_table, dst, &src_table, src, len) } } // Implementation of `table.init`. @@ -490,7 +509,7 @@ unsafe fn table_get_lazy_init_func_ref( .get(None, index) .expect("table access already bounds-checked"); - match elem.into_func_ref_asserting_initialized() { + match unsafe { elem.into_func_ref_asserting_initialized() } { Some(ptr) => ptr.as_ptr().cast(), None => core::ptr::null_mut(), } @@ -516,10 +535,12 @@ unsafe fn grow_gc_heap( ) -> Result<()> { let orig_len = u64::try_from(store.gc_store()?.gc_heap.vmmemory().current_length()).unwrap(); - store - .maybe_async_gc(None, Some(bytes_needed)) - .context("failed to grow the GC heap") - .context(crate::Trap::AllocationTooLarge)?; + unsafe { + store + .maybe_async_gc(None, Some(bytes_needed)) + .context("failed to grow the GC heap") + .context(crate::Trap::AllocationTooLarge)?; + } // JIT code relies on the memory having grown by `bytes_needed` bytes if // this libcall returns successfully, so trap if we didn't grow that much. @@ -605,7 +626,7 @@ unsafe fn intern_func_ref_for_gc_heap( let func_ref = func_ref.cast::(); let func_ref = NonNull::new(func_ref).map(SendSyncPtr::new); - let func_ref_id = store.gc_store_mut()?.func_ref_table.intern(func_ref); + let func_ref_id = unsafe { store.gc_store_mut()?.func_ref_table.intern(func_ref) }; Ok(func_ref_id.into_raw()) } @@ -692,12 +713,14 @@ unsafe fn array_new_data( .layout(shared_ty) .expect("array types have GC layouts"); let array_layout = gc_layout.unwrap_array(); - let array_ref = store.retry_after_gc_maybe_async((), |store, ()| { - store - .unwrap_gc_store_mut() - .alloc_uninit_array(shared_ty, len, &array_layout)? - .map_err(|bytes_needed| crate::GcHeapOutOfMemory::new((), bytes_needed).into()) - })?; + let array_ref = unsafe { + store.retry_after_gc_maybe_async((), |store, ()| { + store + .unwrap_gc_store_mut() + .alloc_uninit_array(shared_ty, len, &array_layout)? + .map_err(|bytes_needed| crate::GcHeapOutOfMemory::new((), bytes_needed).into()) + })? + }; // Copy the data into the array, initializing it. store @@ -837,7 +860,9 @@ unsafe fn array_new_elem( .iter() .map(|f| { let raw_func_ref = instance.as_mut().get_func_ref(*f); - let func = raw_func_ref.map(|p| Func::from_vm_func_ref(store.id(), p)); + let func = unsafe { + raw_func_ref.map(|p| Func::from_vm_func_ref(store.id(), p)) + }; Val::FuncRef(func) }), ); @@ -930,7 +955,7 @@ unsafe fn array_init_elem( .iter() .map(|f| { let raw_func_ref = instance.as_mut().get_func_ref(*f); - let func = raw_func_ref.map(|p| Func::from_vm_func_ref(store.id(), p)); + let func = unsafe { raw_func_ref.map(|p| Func::from_vm_func_ref(store.id(), p)) }; Val::FuncRef(func) }) .collect::>(), diff --git a/crates/wasmtime/src/runtime/vm/mmap.rs b/crates/wasmtime/src/runtime/vm/mmap.rs index 5e43190fb5..10a5e0212b 100644 --- a/crates/wasmtime/src/runtime/vm/mmap.rs +++ b/crates/wasmtime/src/runtime/vm/mmap.rs @@ -187,7 +187,7 @@ impl Mmap { self.len_aligned() ); - self.sys.make_accessible(start, len) + unsafe { self.sys.make_accessible(start, len) } } } @@ -231,7 +231,9 @@ impl Mmap { pub unsafe fn slice(&self, range: Range) -> &[u8] { assert!(range.start <= range.end); assert!(range.end <= self.len()); - core::slice::from_raw_parts(self.as_ptr().add(range.start), range.end - range.start) + unsafe { + core::slice::from_raw_parts(self.as_ptr().add(range.start), range.end - range.start) + } } /// Return the allocated memory as a mutable slice of u8. @@ -247,7 +249,12 @@ impl Mmap { pub unsafe fn slice_mut(&mut self, range: Range) -> &mut [u8] { assert!(range.start <= range.end); assert!(range.end <= self.len()); - core::slice::from_raw_parts_mut(self.as_mut_ptr().add(range.start), range.end - range.start) + unsafe { + core::slice::from_raw_parts_mut( + self.as_mut_ptr().add(range.start), + range.end - range.start, + ) + } } /// Return the allocated memory as a pointer to u8. @@ -312,9 +319,11 @@ impl Mmap { return Ok(()); } - self.sys - .make_executable(range, enable_branch_protection) - .context("failed to make memory executable") + unsafe { + self.sys + .make_executable(range, enable_branch_protection) + .context("failed to make memory executable") + } } /// Makes the specified `range` within this `Mmap` to be readonly. @@ -334,9 +343,11 @@ impl Mmap { return Ok(()); } - self.sys - .make_readonly(range) - .context("failed to make memory readonly") + unsafe { + self.sys + .make_readonly(range) + .context("failed to make memory readonly") + } } } @@ -419,9 +430,11 @@ impl MmapOffset { .offset .checked_add(memory_offset) .expect("self.offset + memory_offset is in bounds"); - self.mmap - .sys - .map_image_at(image_source, source_offset, total_offset, memory_len) + unsafe { + self.mmap + .sys + .map_image_at(image_source, source_offset, total_offset, memory_len) + } } } diff --git a/crates/wasmtime/src/runtime/vm/mmap_vec.rs b/crates/wasmtime/src/runtime/vm/mmap_vec.rs index 8faac9c0c9..b594815621 100644 --- a/crates/wasmtime/src/runtime/vm/mmap_vec.rs +++ b/crates/wasmtime/src/runtime/vm/mmap_vec.rs @@ -209,7 +209,7 @@ impl MmapVec { }; assert!(range.start <= range.end); assert!(range.end <= len); - mmap.make_executable(range.start..range.end, enable_branch_protection) + unsafe { mmap.make_executable(range.start..range.end, enable_branch_protection) } } /// Makes the specified `range` within this `mmap` to be read-only. @@ -223,7 +223,7 @@ impl MmapVec { }; assert!(range.start <= range.end); assert!(range.end <= len); - mmap.make_readonly(range.start..range.end) + unsafe { mmap.make_readonly(range.start..range.end) } } /// Returns the underlying file that this mmap is mapping, if present. @@ -260,14 +260,14 @@ impl MmapVec { pub unsafe fn as_mut_slice(&mut self) -> &mut [u8] { match self { #[cfg(not(has_virtual_memory))] - MmapVec::Alloc { base, layout } => { + MmapVec::Alloc { base, layout } => unsafe { core::slice::from_raw_parts_mut(base.as_mut(), layout.size()) - } + }, MmapVec::ExternallyOwned { .. } => { panic!("Mutating externally owned memory is prohibited"); } #[cfg(has_virtual_memory)] - MmapVec::Mmap { mmap, len } => mmap.slice_mut(0..*len), + MmapVec::Mmap { mmap, len } => unsafe { mmap.slice_mut(0..*len) }, } } } diff --git a/crates/wasmtime/src/runtime/vm/parking_spot.rs b/crates/wasmtime/src/runtime/vm/parking_spot.rs index 7cfb769301..7d4a655f77 100644 --- a/crates/wasmtime/src/runtime/vm/parking_spot.rs +++ b/crates/wasmtime/src/runtime/vm/parking_spot.rs @@ -246,15 +246,17 @@ impl Spot { /// in any other queue and additionally only exclusively used by this queue /// now. unsafe fn push(&mut self, mut waiter: SendSyncPtr) { - assert!(waiter.as_ref().next.is_none()); - assert!(waiter.as_ref().prev.is_none()); + unsafe { + assert!(waiter.as_ref().next.is_none()); + assert!(waiter.as_ref().prev.is_none()); - waiter.as_mut().prev = self.tail; - match self.tail { - Some(mut tail) => tail.as_mut().next = Some(waiter), - None => self.head = Some(waiter), + waiter.as_mut().prev = self.tail; + match self.tail { + Some(mut tail) => tail.as_mut().next = Some(waiter), + None => self.head = Some(waiter), + } + self.tail = Some(waiter); } - self.tail = Some(waiter); } /// Removes `waiter` from the queue. @@ -265,17 +267,19 @@ impl Spot { /// spot's mutex. Additionally `waiter` must be a valid pointer in this /// queue. unsafe fn remove(&mut self, mut waiter: SendSyncPtr) { - let w = waiter.as_mut(); - match w.prev { - Some(mut prev) => prev.as_mut().next = w.next, - None => self.head = w.next, - } - match w.next { - Some(mut next) => next.as_mut().prev = w.prev, - None => self.tail = w.prev, + unsafe { + let w = waiter.as_mut(); + match w.prev { + Some(mut prev) => prev.as_mut().next = w.next, + None => self.head = w.next, + } + match w.next { + Some(mut next) => next.as_mut().prev = w.prev, + None => self.tail = w.prev, + } + w.prev = None; + w.next = None; } - w.prev = None; - w.next = None; } /// Pops the head of the queue from this linked list to wake up a waiter. @@ -286,7 +290,9 @@ impl Spot { /// spot's mutex. unsafe fn pop(&mut self) -> Option> { let ret = self.head?; - self.remove(ret); + unsafe { + self.remove(ret); + } Some(ret) } diff --git a/crates/wasmtime/src/runtime/vm/send_sync_ptr.rs b/crates/wasmtime/src/runtime/vm/send_sync_ptr.rs index f2bb45a7a4..706594c6e5 100644 --- a/crates/wasmtime/src/runtime/vm/send_sync_ptr.rs +++ b/crates/wasmtime/src/runtime/vm/send_sync_ptr.rs @@ -28,14 +28,14 @@ impl SendSyncPtr { /// valid to get a shared reference to it at this time. #[inline] pub unsafe fn as_ref<'a>(&self) -> &'a T { - self.0.as_ref() + unsafe { self.0.as_ref() } } /// Unsafely assert that this is a pointer to valid contents and it's also /// valid to get a mutable reference to it at this time. #[inline] pub unsafe fn as_mut<'a>(&mut self) -> &'a mut T { - self.0.as_mut() + unsafe { self.0.as_mut() } } /// Returns the underlying `NonNull` wrapper. diff --git a/crates/wasmtime/src/runtime/vm/stack_switching/stack.rs b/crates/wasmtime/src/runtime/vm/stack_switching/stack.rs index 5865a17e0d..831a60e8d3 100644 --- a/crates/wasmtime/src/runtime/vm/stack_switching/stack.rs +++ b/crates/wasmtime/src/runtime/vm/stack_switching/stack.rs @@ -51,9 +51,9 @@ impl VMContinuationStack { /// The caller must properly allocate the stack space with a guard page and /// make the pages accessible for correct behavior. pub unsafe fn from_raw_parts(bottom: *mut u8, guard_size: usize, len: usize) -> Result { - Ok(Self(imp::VMContinuationStack::from_raw_parts( - bottom, guard_size, len, - )?)) + Ok(Self(unsafe { + imp::VMContinuationStack::from_raw_parts(bottom, guard_size, len)? + })) } /// Is this a manually-managed stack created from raw parts? If so, it is up diff --git a/crates/wasmtime/src/runtime/vm/stack_switching/stack/unix.rs b/crates/wasmtime/src/runtime/vm/stack_switching/stack/unix.rs index ec1e06d695..074a43d0fb 100644 --- a/crates/wasmtime/src/runtime/vm/stack_switching/stack/unix.rs +++ b/crates/wasmtime/src/runtime/vm/stack_switching/stack/unix.rs @@ -138,7 +138,7 @@ impl VMContinuationStack { len: usize, ) -> io::Result { Ok(Self { - top: base.add(len), + top: unsafe { base.add(len) }, len, allocator: Allocator::Custom, }) diff --git a/crates/wasmtime/src/runtime/vm/sys/custom/mmap.rs b/crates/wasmtime/src/runtime/vm/sys/custom/mmap.rs index 7548f301d8..19db107b80 100644 --- a/crates/wasmtime/src/runtime/vm/sys/custom/mmap.rs +++ b/crates/wasmtime/src/runtime/vm/sys/custom/mmap.rs @@ -83,25 +83,29 @@ impl Mmap { range: Range, enable_branch_protection: bool, ) -> Result<()> { - let base = self.memory.as_ptr().byte_add(range.start).cast(); - let len = range.end - range.start; + unsafe { + let base = self.memory.as_ptr().byte_add(range.start).cast(); + let len = range.end - range.start; - // not mapped into the C API at this time. - let _ = enable_branch_protection; + // not mapped into the C API at this time. + let _ = enable_branch_protection; - cvt(capi::wasmtime_mprotect( - base, - len, - capi::PROT_READ | capi::PROT_EXEC, - ))?; + cvt(capi::wasmtime_mprotect( + base, + len, + capi::PROT_READ | capi::PROT_EXEC, + ))?; + } Ok(()) } pub unsafe fn make_readonly(&self, range: Range) -> Result<()> { - let base = self.memory.as_ptr().byte_add(range.start).cast(); - let len = range.end - range.start; + unsafe { + let base = self.memory.as_ptr().byte_add(range.start).cast(); + let len = range.end - range.start; - cvt(capi::wasmtime_mprotect(base, len, capi::PROT_READ))?; + cvt(capi::wasmtime_mprotect(base, len, capi::PROT_READ))?; + } Ok(()) } @@ -113,16 +117,18 @@ impl Mmap { memory_len: HostAlignedByteCount, ) -> Result<()> { assert_eq!(source_offset, 0); - let base = self - .memory - .as_ptr() - .byte_add(memory_offset.byte_count()) - .cast(); - cvt(capi::wasmtime_memory_image_map_at( - image_source.image_ptr().as_ptr(), - base, - memory_len.byte_count(), - )) + unsafe { + let base = self + .memory + .as_ptr() + .byte_add(memory_offset.byte_count()) + .cast(); + cvt(capi::wasmtime_memory_image_map_at( + image_source.image_ptr().as_ptr(), + base, + memory_len.byte_count(), + )) + } } } diff --git a/crates/wasmtime/src/runtime/vm/sys/custom/traphandlers.rs b/crates/wasmtime/src/runtime/vm/sys/custom/traphandlers.rs index 02f052fbd1..881e7163e5 100644 --- a/crates/wasmtime/src/runtime/vm/sys/custom/traphandlers.rs +++ b/crates/wasmtime/src/runtime/vm/sys/custom/traphandlers.rs @@ -16,11 +16,13 @@ pub unsafe fn wasmtime_setjmp( payload: *mut u8, callee: NonNull, ) -> bool { - let callback = mem::transmute::< - extern "C" fn(*mut u8, NonNull) -> bool, - extern "C" fn(*mut u8, *mut u8) -> bool, - >(callback); - capi::wasmtime_setjmp(jmp_buf, callback, payload, callee.as_ptr().cast()) + unsafe { + let callback = mem::transmute::< + extern "C" fn(*mut u8, NonNull) -> bool, + extern "C" fn(*mut u8, *mut u8) -> bool, + >(callback); + capi::wasmtime_setjmp(jmp_buf, callback, payload, callee.as_ptr().cast()) + } } #[cfg(has_native_signals)] @@ -29,7 +31,9 @@ pub struct TrapHandler; #[cfg(has_native_signals)] impl TrapHandler { pub unsafe fn new(_macos_use_mach_ports: bool) -> TrapHandler { - capi::wasmtime_init_traps(handle_trap); + unsafe { + capi::wasmtime_init_traps(handle_trap); + } TrapHandler } diff --git a/crates/wasmtime/src/runtime/vm/sys/custom/vm.rs b/crates/wasmtime/src/runtime/vm/sys/custom/vm.rs index 0762d4f8a2..f2303388ea 100644 --- a/crates/wasmtime/src/runtime/vm/sys/custom/vm.rs +++ b/crates/wasmtime/src/runtime/vm/sys/custom/vm.rs @@ -8,19 +8,21 @@ use core::ptr::{self, NonNull}; use std::{fs::File, sync::Arc}; pub unsafe fn expose_existing_mapping(ptr: *mut u8, len: usize) -> Result<()> { - cvt(capi::wasmtime_mprotect( - ptr.cast(), - len, - capi::PROT_READ | capi::PROT_WRITE, - )) + unsafe { + cvt(capi::wasmtime_mprotect( + ptr.cast(), + len, + capi::PROT_READ | capi::PROT_WRITE, + )) + } } pub unsafe fn hide_existing_mapping(ptr: *mut u8, len: usize) -> Result<()> { - cvt(capi::wasmtime_mprotect(ptr.cast(), len, 0)) + unsafe { cvt(capi::wasmtime_mprotect(ptr.cast(), len, 0)) } } pub unsafe fn erase_existing_mapping(ptr: *mut u8, len: usize) -> Result<()> { - cvt(capi::wasmtime_mmap_remap(ptr.cast(), len, 0)) + unsafe { cvt(capi::wasmtime_mmap_remap(ptr.cast(), len, 0)) } } #[cfg(feature = "pooling-allocator")] @@ -36,11 +38,13 @@ pub unsafe fn decommit_pages(addr: *mut u8, len: usize) -> Result<()> { return Ok(()); } - cvt(capi::wasmtime_mmap_remap( - addr, - len, - capi::PROT_READ | capi::PROT_WRITE, - )) + unsafe { + cvt(capi::wasmtime_mmap_remap( + addr, + len, + capi::PROT_READ | capi::PROT_WRITE, + )) + } } pub fn get_page_size() -> usize { @@ -85,11 +89,13 @@ impl MemoryImageSource { } pub unsafe fn remap_as_zeros_at(&self, base: *mut u8, len: usize) -> Result<()> { - cvt(capi::wasmtime_mmap_remap( - base.cast(), - len, - capi::PROT_READ | capi::PROT_WRITE, - )) + unsafe { + cvt(capi::wasmtime_mmap_remap( + base.cast(), + len, + capi::PROT_READ | capi::PROT_WRITE, + )) + } } } diff --git a/crates/wasmtime/src/runtime/vm/sys/miri/traphandlers.rs b/crates/wasmtime/src/runtime/vm/sys/miri/traphandlers.rs index 0f268474e9..22d79878db 100644 --- a/crates/wasmtime/src/runtime/vm/sys/miri/traphandlers.rs +++ b/crates/wasmtime/src/runtime/vm/sys/miri/traphandlers.rs @@ -13,7 +13,7 @@ use crate::prelude::*; use crate::runtime::vm::VMContext; use core::ptr::NonNull; -pub fn wasmtime_setjmp( +pub unsafe fn wasmtime_setjmp( _jmp_buf: *mut *const u8, callback: extern "C" fn(*mut u8, NonNull) -> bool, payload: *mut u8, @@ -22,7 +22,7 @@ pub fn wasmtime_setjmp( callback(payload, callee) } -pub fn wasmtime_longjmp(_jmp_buf: *const u8) -> ! { +pub unsafe fn wasmtime_longjmp(_jmp_buf: *const u8) -> ! { unsafe { libc::abort(); } diff --git a/crates/wasmtime/src/runtime/vm/sys/miri/vm.rs b/crates/wasmtime/src/runtime/vm/sys/miri/vm.rs index ccdb6529aa..d2e066b7f9 100644 --- a/crates/wasmtime/src/runtime/vm/sys/miri/vm.rs +++ b/crates/wasmtime/src/runtime/vm/sys/miri/vm.rs @@ -4,28 +4,38 @@ use std::io; use std::sync::Arc; pub unsafe fn expose_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> { - std::ptr::write_bytes(ptr, 0u8, len); + unsafe { + std::ptr::write_bytes(ptr, 0u8, len); + } Ok(()) } pub unsafe fn hide_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> { - std::ptr::write_bytes(ptr, 0, len); + unsafe { + std::ptr::write_bytes(ptr, 0, len); + } Ok(()) } pub unsafe fn erase_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> { - std::ptr::write_bytes(ptr, 0, len); + unsafe { + std::ptr::write_bytes(ptr, 0, len); + } Ok(()) } #[cfg(feature = "pooling-allocator")] pub unsafe fn commit_pages(ptr: *mut u8, len: usize) -> io::Result<()> { - std::ptr::write_bytes(ptr, 0, len); + unsafe { + std::ptr::write_bytes(ptr, 0, len); + } Ok(()) } pub unsafe fn decommit_pages(ptr: *mut u8, len: usize) -> io::Result<()> { - std::ptr::write_bytes(ptr, 0, len); + unsafe { + std::ptr::write_bytes(ptr, 0, len); + } Ok(()) } diff --git a/crates/wasmtime/src/runtime/vm/sys/unix/machports.rs b/crates/wasmtime/src/runtime/vm/sys/unix/machports.rs index 8bf45c8a54..586430f470 100644 --- a/crates/wasmtime/src/runtime/vm/sys/unix/machports.rs +++ b/crates/wasmtime/src/runtime/vm/sys/unix/machports.rs @@ -76,28 +76,31 @@ impl TrapHandler { // panic in `lazy_per_thread_init` if the child attempts to invoke // WebAssembly. unsafe extern "C" fn child() { - CHILD_OF_FORKED_PROCESS = true; + unsafe { + CHILD_OF_FORKED_PROCESS = true; + } } - let rc = libc::pthread_atfork(None, None, Some(child)); - assert_eq!(rc, 0, "failed to configure `pthread_atfork` handler"); - - // Allocate our WASMTIME_PORT and make sure that it can be sent to so we - // can receive exceptions. - let me = mach_task_self(); - let kret = mach_port_allocate(me, MACH_PORT_RIGHT_RECEIVE, &raw mut WASMTIME_PORT); - assert_eq!(kret, KERN_SUCCESS, "failed to allocate port"); - let kret = - mach_port_insert_right(me, WASMTIME_PORT, WASMTIME_PORT, MACH_MSG_TYPE_MAKE_SEND); - assert_eq!(kret, KERN_SUCCESS, "failed to insert right"); - - // Spin up our handler thread which will solely exist to service exceptions - // generated by other threads. Note that this is a background thread that - // we're not very interested in so it's detached here. - let thread = thread::spawn(|| handler_thread()); - - // Setup a SIGBUS handler which is used for printing that the stack was - // overflowed when a host overflows its fiber stack. + let thread; unsafe { + let rc = libc::pthread_atfork(None, None, Some(child)); + assert_eq!(rc, 0, "failed to configure `pthread_atfork` handler"); + + // Allocate our WASMTIME_PORT and make sure that it can be sent to so we + // can receive exceptions. + let me = mach_task_self(); + let kret = mach_port_allocate(me, MACH_PORT_RIGHT_RECEIVE, &raw mut WASMTIME_PORT); + assert_eq!(kret, KERN_SUCCESS, "failed to allocate port"); + let kret = + mach_port_insert_right(me, WASMTIME_PORT, WASMTIME_PORT, MACH_MSG_TYPE_MAKE_SEND); + assert_eq!(kret, KERN_SUCCESS, "failed to insert right"); + + // Spin up our handler thread which will solely exist to service exceptions + // generated by other threads. Note that this is a background thread that + // we're not very interested in so it's detached here. + thread = thread::spawn(|| handler_thread()); + + // Setup a SIGBUS handler which is used for printing that the stack was + // overflowed when a host overflows its fiber stack. let mut handler: libc::sigaction = mem::zeroed(); handler.sa_flags = libc::SA_SIGINFO | libc::SA_ONSTACK; handler.sa_sigaction = sigbus_handler as usize; @@ -139,19 +142,23 @@ unsafe extern "C" fn sigbus_handler( Some(info) => info, None => return, }; - let faulting_addr = (*siginfo).si_addr() as usize; - let range = &info.vm_store_context.as_ref().async_guard_range; - if range.start.addr() <= faulting_addr && faulting_addr < range.end.addr() { - super::signals::abort_stack_overflow(); + unsafe { + let faulting_addr = (*siginfo).si_addr() as usize; + let range = &info.vm_store_context.as_ref().async_guard_range; + if range.start.addr() <= faulting_addr && faulting_addr < range.end.addr() { + super::signals::abort_stack_overflow(); + } } }); - super::signals::delegate_signal_to_previous_handler( - &raw mut PREV_SIGBUS, - signum, - siginfo, - context, - ) + unsafe { + super::signals::delegate_signal_to_previous_handler( + &raw mut PREV_SIGBUS, + signum, + siginfo, + context, + ) + } } // Note that this is copied from Gecko at @@ -203,27 +210,33 @@ unsafe fn handler_thread() { // Block this thread reading a message from our port. This will block // until some thread throws an exception. Note that messages are all // expected to be exceptions here. - let mut request: ExceptionRequest = mem::zeroed(); - let kret = mach_msg( - &mut request.body.Head, - MACH_RCV_MSG | MACH_RCV_INTERRUPT, - 0, - mem::size_of_val(&request) as u32, - WASMTIME_PORT, - MACH_MSG_TIMEOUT_NONE, - MACH_PORT_NULL, - ); + let mut request: ExceptionRequest = unsafe { mem::zeroed() }; + let kret = unsafe { + mach_msg( + &mut request.body.Head, + MACH_RCV_MSG | MACH_RCV_INTERRUPT, + 0, + mem::size_of_val(&request) as u32, + WASMTIME_PORT, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL, + ) + }; if kret == MACH_RCV_PORT_CHANGED { // this port has been closed; break; } if kret != KERN_SUCCESS { eprintln!("mach_msg failed with {kret} ({kret:x})"); - libc::abort(); + unsafe { + libc::abort(); + } } if request.body.Head.msgh_id != EXCEPTION_MSG_ID { eprintln!("unexpected msg header id {}", request.body.Head.msgh_id); - libc::abort(); + unsafe { + libc::abort(); + } } // Attempt to handle the exception below which will process the state @@ -232,7 +245,7 @@ unsafe fn handler_thread() { // We unconditionally need to send a message back on our port after // this exception is received, and our reply code here dictates whether // the thread crashes or whether we continue execution of the thread. - let reply_code = if handle_exception(&mut request) { + let reply_code = if unsafe { handle_exception(&mut request) } { KERN_SUCCESS } else { KERN_FAILURE @@ -241,24 +254,26 @@ unsafe fn handler_thread() { // This magic incantation to send a reply back to the kernel was // derived from the exc_server generated by // 'mig -v /usr/include/mach/mach_exc.defs'. - let mut reply: __Reply__exception_raise_t = mem::zeroed(); + let mut reply: __Reply__exception_raise_t = unsafe { mem::zeroed() }; reply.Head.msgh_bits = MACH_MSGH_BITS(request.body.Head.msgh_bits & MACH_MSGH_BITS_REMOTE_MASK, 0); reply.Head.msgh_size = mem::size_of_val(&reply) as u32; reply.Head.msgh_remote_port = request.body.Head.msgh_remote_port; reply.Head.msgh_local_port = MACH_PORT_NULL; reply.Head.msgh_id = request.body.Head.msgh_id + 100; - reply.NDR = NDR_record; + reply.NDR = unsafe { NDR_record }; reply.RetCode = reply_code; - mach_msg( - &mut reply.Head, - MACH_SEND_MSG, - mem::size_of_val(&reply) as u32, - 0, - MACH_PORT_NULL, - MACH_MSG_TIMEOUT_NONE, - MACH_PORT_NULL, - ); + unsafe { + mach_msg( + &mut reply.Head, + MACH_SEND_MSG, + mem::size_of_val(&reply) as u32, + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL, + ); + } } } @@ -333,7 +348,9 @@ unsafe fn handle_exception(request: &mut ExceptionRequest) -> bool { // not the end of the world anyway. if state.__rsp % 16 == 0 { state.__rsp -= 8; - *(state.__rsp as *mut u64) = state.__rip; + unsafe { + *(state.__rsp as *mut u64) = state.__rip; + } } state.__rip = unwind as u64; state.__rdi = pc as u64; @@ -369,7 +386,7 @@ unsafe fn handle_exception(request: &mut ExceptionRequest) -> bool { state.__x[4] = trap as u64; state.__pc = unwind as u64; }; - let mut thread_state = mem::zeroed::(); + let mut thread_state = unsafe { mem::zeroed::() }; } else { compile_error!("unsupported target architecture"); } @@ -378,12 +395,14 @@ unsafe fn handle_exception(request: &mut ExceptionRequest) -> bool { // First up read our origin thread's state into the area defined above. let origin_thread = request.body.thread.name; let mut thread_state_count = ThreadState::count(); - let kret = thread_get_state( - origin_thread, - thread_state_flavor, - &mut thread_state as *mut ThreadState as *mut u32, - &mut thread_state_count, - ); + let kret = unsafe { + thread_get_state( + origin_thread, + thread_state_flavor, + &mut thread_state as *mut ThreadState as *mut u32, + &mut thread_state_count, + ) + }; if kret != KERN_SUCCESS { return false; } @@ -420,12 +439,14 @@ unsafe fn handle_exception(request: &mut ExceptionRequest) -> bool { Some(addr) => (1, addr), }; resume(&mut thread_state, pc as usize, fp, fault1, fault2, trap); - let kret = thread_set_state( - origin_thread, - thread_state_flavor, - &mut thread_state as *mut ThreadState as *mut u32, - thread_state_count, - ); + let kret = unsafe { + thread_set_state( + origin_thread, + thread_state_flavor, + &mut thread_state as *mut ThreadState as *mut u32, + thread_state_count, + ) + }; kret == KERN_SUCCESS } @@ -449,7 +470,9 @@ unsafe extern "C" fn unwind(pc: usize, fp: usize, fault1: usize, fault2: usize, state.take_jmp_buf() }); debug_assert!(!jmp_buf.is_null()); - wasmtime_longjmp(jmp_buf); + unsafe { + wasmtime_longjmp(jmp_buf); + } } /// Exceptions on macOS can be delivered to either thread-level or task-level diff --git a/crates/wasmtime/src/runtime/vm/sys/unix/mmap.rs b/crates/wasmtime/src/runtime/vm/sys/unix/mmap.rs index f548f498ef..3a618255c6 100644 --- a/crates/wasmtime/src/runtime/vm/sys/unix/mmap.rs +++ b/crates/wasmtime/src/runtime/vm/sys/unix/mmap.rs @@ -141,7 +141,7 @@ impl Mmap { range: Range, enable_branch_protection: bool, ) -> Result<()> { - let base = self.memory.as_ptr().byte_add(range.start).cast(); + let base = unsafe { self.memory.as_ptr().byte_add(range.start).cast() }; let len = range.end - range.start; let flags = MprotectFlags::READ | MprotectFlags::EXEC; @@ -159,16 +159,20 @@ impl Mmap { flags }; - mprotect(base, len, flags)?; + unsafe { + mprotect(base, len, flags)?; + } Ok(()) } pub unsafe fn make_readonly(&self, range: Range) -> Result<()> { - let base = self.memory.as_ptr().byte_add(range.start).cast(); + let base = unsafe { self.memory.as_ptr().byte_add(range.start).cast() }; let len = range.end - range.start; - mprotect(base, len, MprotectFlags::READ)?; + unsafe { + mprotect(base, len, MprotectFlags::READ)?; + } Ok(()) } diff --git a/crates/wasmtime/src/runtime/vm/sys/unix/signals.rs b/crates/wasmtime/src/runtime/vm/sys/unix/signals.rs index ef39974a73..f563d7b00e 100644 --- a/crates/wasmtime/src/runtime/vm/sys/unix/signals.rs +++ b/crates/wasmtime/src/runtime/vm/sys/unix/signals.rs @@ -34,7 +34,7 @@ impl TrapHandler { assert!(!macos_use_mach_ports || !cfg!(target_vendor = "apple")); foreach_handler(|slot, signal| { - let mut handler: libc::sigaction = mem::zeroed(); + let mut handler: libc::sigaction = unsafe { mem::zeroed() }; // The flags here are relatively careful, and they are... // // SA_SIGINFO gives us access to information like the program @@ -50,12 +50,14 @@ impl TrapHandler { // Breakpad handler by testing handlingSegFault. handler.sa_flags = libc::SA_SIGINFO | libc::SA_NODEFER | libc::SA_ONSTACK; handler.sa_sigaction = trap_handler as usize; - libc::sigemptyset(&mut handler.sa_mask); - if libc::sigaction(signal, &handler, slot) != 0 { - panic!( - "unable to install signal handler: {}", - io::Error::last_os_error(), - ); + unsafe { + libc::sigemptyset(&mut handler.sa_mask); + if libc::sigaction(signal, &handler, slot) != 0 { + panic!( + "unable to install signal handler: {}", + io::Error::last_os_error(), + ); + } } }); @@ -154,10 +156,10 @@ unsafe extern "C" fn trap_handler( // handling, and reset our trap handling flag. Then we figure // out what to do based on the result of the trap handling. let faulting_addr = match signum { - libc::SIGSEGV | libc::SIGBUS => Some((*siginfo).si_addr() as usize), + libc::SIGSEGV | libc::SIGBUS => unsafe { Some((*siginfo).si_addr() as usize) }, _ => None, }; - let regs = get_trap_registers(context, signum); + let regs = unsafe { get_trap_registers(context, signum) }; let test = info.test_if_trap(regs, faulting_addr, |handler| { handler(signum, siginfo, context) }); @@ -169,7 +171,7 @@ unsafe extern "C" fn trap_handler( let jmp_buf = match test { TrapTest::NotWasm => { if let Some(faulting_addr) = faulting_addr { - let range = &info.vm_store_context.as_ref().async_guard_range; + let range = unsafe { &info.vm_store_context.as_ref().async_guard_range }; if range.start.addr() <= faulting_addr && faulting_addr < range.end.addr() { abort_stack_overflow(); } @@ -210,19 +212,21 @@ unsafe extern "C" fn trap_handler( // code we blow away the stack anyway with a longjmp. if cfg!(target_vendor = "apple") { unsafe extern "C" fn wasmtime_longjmp_shim(jmp_buf: *const u8) { - wasmtime_longjmp(jmp_buf) + unsafe { wasmtime_longjmp(jmp_buf) } + } + unsafe { + set_pc(context, wasmtime_longjmp_shim as usize, jmp_buf as usize); } - set_pc(context, wasmtime_longjmp_shim as usize, jmp_buf as usize); return true; } - wasmtime_longjmp(jmp_buf) + unsafe { wasmtime_longjmp(jmp_buf) } }); if handled { return; } - delegate_signal_to_previous_handler(previous, signum, siginfo, context) + unsafe { delegate_signal_to_previous_handler(previous, signum, siginfo, context) } } pub unsafe fn delegate_signal_to_previous_handler( @@ -240,15 +244,18 @@ pub unsafe fn delegate_signal_to_previous_handler( // it. It will either crash synchronously, fix up the instruction // so that execution can continue and return, or trigger a crash by // returning the signal to it's original disposition and returning. - let previous = *previous; - if previous.sa_flags & libc::SA_SIGINFO != 0 { - mem::transmute::( - previous.sa_sigaction, - )(signum, siginfo, context) - } else if previous.sa_sigaction == libc::SIG_DFL || previous.sa_sigaction == libc::SIG_IGN { - libc::sigaction(signum, &previous as *const _, ptr::null_mut()); - } else { - mem::transmute::(previous.sa_sigaction)(signum) + unsafe { + let previous = *previous; + if previous.sa_flags & libc::SA_SIGINFO != 0 { + mem::transmute::< + usize, + extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void), + >(previous.sa_sigaction)(signum, siginfo, context) + } else if previous.sa_sigaction == libc::SIG_DFL || previous.sa_sigaction == libc::SIG_IGN { + libc::sigaction(signum, &previous as *const _, ptr::null_mut()); + } else { + mem::transmute::(previous.sa_sigaction)(signum) + } } } @@ -267,19 +274,19 @@ pub fn abort_stack_overflow() -> ! { unsafe fn get_trap_registers(cx: *mut libc::c_void, _signum: libc::c_int) -> TrapRegisters { cfg_if::cfg_if! { if #[cfg(all(any(target_os = "linux", target_os = "android", target_os = "illumos"), target_arch = "x86_64"))] { - let cx = &*(cx as *const libc::ucontext_t); + let cx = unsafe { &*(cx as *const libc::ucontext_t) }; TrapRegisters { pc: cx.uc_mcontext.gregs[libc::REG_RIP as usize] as usize, fp: cx.uc_mcontext.gregs[libc::REG_RBP as usize] as usize, } } else if #[cfg(all(target_os = "linux", target_arch = "x86"))] { - let cx = &*(cx as *const libc::ucontext_t); + let cx = unsafe { &*(cx as *const libc::ucontext_t) }; TrapRegisters { pc: cx.uc_mcontext.gregs[libc::REG_EIP as usize] as usize, fp: cx.uc_mcontext.gregs[libc::REG_EBP as usize] as usize, } } else if #[cfg(all(any(target_os = "linux", target_os = "android"), target_arch = "aarch64"))] { - let cx = &*(cx as *const libc::ucontext_t); + let cx = unsafe { &*(cx as *const libc::ucontext_t) }; TrapRegisters { pc: cx.uc_mcontext.pc as usize, fp: cx.uc_mcontext.regs[29] as usize, @@ -299,49 +306,55 @@ unsafe fn get_trap_registers(cx: *mut libc::c_void, _signum: libc::c_int) -> Tra libc::SIGILL | libc::SIGFPE => 1, _ => 0, }; - let cx = &*(cx as *const libc::ucontext_t); - TrapRegisters { - pc: (cx.uc_mcontext.psw.addr - trap_offset) as usize, - fp: *(cx.uc_mcontext.gregs[15] as *const usize), + unsafe { + let cx = &*(cx as *const libc::ucontext_t); + TrapRegisters { + pc: (cx.uc_mcontext.psw.addr - trap_offset) as usize, + fp: *(cx.uc_mcontext.gregs[15] as *const usize), + } } } else if #[cfg(all(target_vendor = "apple", target_arch = "x86_64"))] { - let cx = &*(cx as *const libc::ucontext_t); - TrapRegisters { - pc: (*cx.uc_mcontext).__ss.__rip as usize, - fp: (*cx.uc_mcontext).__ss.__rbp as usize, + unsafe { + let cx = &*(cx as *const libc::ucontext_t); + TrapRegisters { + pc: (*cx.uc_mcontext).__ss.__rip as usize, + fp: (*cx.uc_mcontext).__ss.__rbp as usize, + } } } else if #[cfg(all(target_vendor = "apple", target_arch = "aarch64"))] { - let cx = &*(cx as *const libc::ucontext_t); - TrapRegisters { - pc: (*cx.uc_mcontext).__ss.__pc as usize, - fp: (*cx.uc_mcontext).__ss.__fp as usize, + unsafe { + let cx = &*(cx as *const libc::ucontext_t); + TrapRegisters { + pc: (*cx.uc_mcontext).__ss.__pc as usize, + fp: (*cx.uc_mcontext).__ss.__fp as usize, + } } } else if #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] { - let cx = &*(cx as *const libc::ucontext_t); + let cx = unsafe { &*(cx as *const libc::ucontext_t) }; TrapRegisters { pc: cx.uc_mcontext.mc_rip as usize, fp: cx.uc_mcontext.mc_rbp as usize, } } else if #[cfg(all(target_os = "linux", target_arch = "riscv64"))] { - let cx = &*(cx as *const libc::ucontext_t); + let cx = unsafe { &*(cx as *const libc::ucontext_t) }; TrapRegisters { pc: cx.uc_mcontext.__gregs[libc::REG_PC] as usize, fp: cx.uc_mcontext.__gregs[libc::REG_S0] as usize, } } else if #[cfg(all(target_os = "freebsd", target_arch = "aarch64"))] { - let cx = &*(cx as *const libc::mcontext_t); + let cx = unsafe { &*(cx as *const libc::mcontext_t) }; TrapRegisters { pc: cx.mc_gpregs.gp_elr as usize, fp: cx.mc_gpregs.gp_x[29] as usize, } } else if #[cfg(all(target_os = "openbsd", target_arch = "x86_64"))] { - let cx = &*(cx as *const libc::ucontext_t); + let cx = unsafe { &*(cx as *const libc::ucontext_t) }; TrapRegisters { pc: cx.sc_rip as usize, fp: cx.sc_rbp as usize, } } else if #[cfg(all(target_os = "linux", target_arch = "arm"))] { - let cx = &*(cx as *const libc::ucontext_t); + let cx = unsafe { &*(cx as *const libc::ucontext_t) }; TrapRegisters { pc: cx.uc_mcontext.arm_pc as usize, fp: cx.uc_mcontext.arm_fp as usize, @@ -363,25 +376,29 @@ unsafe fn set_pc(cx: *mut libc::c_void, pc: usize, arg1: usize) { let _ = (cx, pc, arg1); unreachable!(); // not used on these platforms } else if #[cfg(target_arch = "x86_64")] { - let cx = &mut *(cx as *mut libc::ucontext_t); - (*cx.uc_mcontext).__ss.__rip = pc as u64; - (*cx.uc_mcontext).__ss.__rdi = arg1 as u64; - // We're simulating a "pseudo-call" so we need to ensure - // stack alignment is properly respected, notably that on a - // `call` instruction the stack is 8/16-byte aligned, then - // the function adjusts itself to be 16-byte aligned. - // - // Most of the time the stack pointer is 16-byte aligned at - // the time of the trap but for more robust-ness with JIT - // code where it may ud2 in a prologue check before the - // stack is aligned we double-check here. - if (*cx.uc_mcontext).__ss.__rsp % 16 == 0 { - (*cx.uc_mcontext).__ss.__rsp -= 8; + unsafe { + let cx = &mut *(cx as *mut libc::ucontext_t); + (*cx.uc_mcontext).__ss.__rip = pc as u64; + (*cx.uc_mcontext).__ss.__rdi = arg1 as u64; + // We're simulating a "pseudo-call" so we need to ensure + // stack alignment is properly respected, notably that on a + // `call` instruction the stack is 8/16-byte aligned, then + // the function adjusts itself to be 16-byte aligned. + // + // Most of the time the stack pointer is 16-byte aligned at + // the time of the trap but for more robust-ness with JIT + // code where it may ud2 in a prologue check before the + // stack is aligned we double-check here. + if (*cx.uc_mcontext).__ss.__rsp % 16 == 0 { + (*cx.uc_mcontext).__ss.__rsp -= 8; + } } } else if #[cfg(target_arch = "aarch64")] { - let cx = &mut *(cx as *mut libc::ucontext_t); - (*cx.uc_mcontext).__ss.__pc = pc as u64; - (*cx.uc_mcontext).__ss.__x[0] = arg1 as u64; + unsafe { + let cx = &mut *(cx as *mut libc::ucontext_t); + (*cx.uc_mcontext).__ss.__pc = pc as u64; + (*cx.uc_mcontext).__ss.__x[0] = arg1 as u64; + } } else { compile_error!("unsupported apple target architecture"); } @@ -479,8 +496,8 @@ pub fn lazy_per_thread_init() { unsafe fn allocate_sigaltstack() -> Option { // Check to see if the existing sigaltstack, if it exists, is big // enough. If so we don't need to allocate our own. - let mut old_stack = mem::zeroed(); - let r = libc::sigaltstack(ptr::null(), &mut old_stack); + let mut old_stack = unsafe { mem::zeroed() }; + let r = unsafe { libc::sigaltstack(ptr::null(), &mut old_stack) }; assert_eq!( r, 0, @@ -497,29 +514,33 @@ pub fn lazy_per_thread_init() { let guard_size = page_size; let alloc_size = guard_size + MIN_STACK_SIZE; - let ptr = rustix::mm::mmap_anonymous( - null_mut(), - alloc_size, - rustix::mm::ProtFlags::empty(), - rustix::mm::MapFlags::PRIVATE, - ) - .expect("failed to allocate memory for sigaltstack"); + let ptr = unsafe { + rustix::mm::mmap_anonymous( + null_mut(), + alloc_size, + rustix::mm::ProtFlags::empty(), + rustix::mm::MapFlags::PRIVATE, + ) + .expect("failed to allocate memory for sigaltstack") + }; // Prepare the stack with readable/writable memory and then register it // with `sigaltstack`. let stack_ptr = (ptr as usize + guard_size) as *mut std::ffi::c_void; - rustix::mm::mprotect( - stack_ptr, - MIN_STACK_SIZE, - rustix::mm::MprotectFlags::READ | rustix::mm::MprotectFlags::WRITE, - ) - .expect("mprotect to configure memory for sigaltstack failed"); + unsafe { + rustix::mm::mprotect( + stack_ptr, + MIN_STACK_SIZE, + rustix::mm::MprotectFlags::READ | rustix::mm::MprotectFlags::WRITE, + ) + .expect("mprotect to configure memory for sigaltstack failed"); + } let new_stack = libc::stack_t { ss_sp: stack_ptr, ss_flags: 0, ss_size: MIN_STACK_SIZE, }; - let r = libc::sigaltstack(&new_stack, ptr::null_mut()); + let r = unsafe { libc::sigaltstack(&new_stack, ptr::null_mut()) }; assert_eq!( r, 0, diff --git a/crates/wasmtime/src/runtime/vm/sys/unix/traphandlers.rs b/crates/wasmtime/src/runtime/vm/sys/unix/traphandlers.rs index c62c1f1593..1e3c733a6d 100644 --- a/crates/wasmtime/src/runtime/vm/sys/unix/traphandlers.rs +++ b/crates/wasmtime/src/runtime/vm/sys/unix/traphandlers.rs @@ -44,11 +44,13 @@ cfg_if::cfg_if! { impl TrapHandler { pub unsafe fn new(macos_use_mach_ports: bool) -> TrapHandler { - USE_MACH_PORTS = macos_use_mach_ports; - if macos_use_mach_ports { - TrapHandler::MachPorts(super::machports::TrapHandler::new()) - } else { - TrapHandler::Signals(super::signals::TrapHandler::new(false)) + unsafe { + USE_MACH_PORTS = macos_use_mach_ports; + if macos_use_mach_ports { + TrapHandler::MachPorts(super::machports::TrapHandler::new()) + } else { + TrapHandler::Signals(super::signals::TrapHandler::new(false)) + } } } diff --git a/crates/wasmtime/src/runtime/vm/sys/unix/unwind.rs b/crates/wasmtime/src/runtime/vm/sys/unix/unwind.rs index ed4f32f700..aa8ad107bb 100644 --- a/crates/wasmtime/src/runtime/vm/sys/unix/unwind.rs +++ b/crates/wasmtime/src/runtime/vm/sys/unix/unwind.rs @@ -79,36 +79,38 @@ impl UnwindRegistration { ); let mut registrations = Vec::new(); - if using_libunwind() { - // For libunwind, `__register_frame` takes a pointer to a single - // FDE. Note that we subtract 4 from the length of unwind info since - // wasmtime-encode .eh_frame sections always have a trailing 32-bit - // zero for the platforms above. - let start = unwind_info; - let end = start.add(unwind_len - 4); - let mut current = start; + unsafe { + if using_libunwind() { + // For libunwind, `__register_frame` takes a pointer to a single + // FDE. Note that we subtract 4 from the length of unwind info since + // wasmtime-encode .eh_frame sections always have a trailing 32-bit + // zero for the platforms above. + let start = unwind_info; + let end = start.add(unwind_len - 4); + let mut current = start; - // Walk all of the entries in the frame table and register them - while current < end { - let len = current.cast::().read_unaligned() as usize; + // Walk all of the entries in the frame table and register them + while current < end { + let len = current.cast::().read_unaligned() as usize; - // Skip over the CIE - if current != start { - __register_frame(current); - let cur = NonNull::new(current.cast_mut()).unwrap(); - registrations.push(SendSyncPtr::new(cur)); - } + // Skip over the CIE + if current != start { + __register_frame(current); + let cur = NonNull::new(current.cast_mut()).unwrap(); + registrations.push(SendSyncPtr::new(cur)); + } - // Move to the next table entry (+4 because the length itself is - // not inclusive) - current = current.add(len + 4); + // Move to the next table entry (+4 because the length itself is + // not inclusive) + current = current.add(len + 4); + } + } else { + // On gnu (libgcc), `__register_frame` will walk the FDEs until an + // entry of length 0 + __register_frame(unwind_info); + let info = NonNull::new(unwind_info.cast_mut()).unwrap(); + registrations.push(SendSyncPtr::new(info)); } - } else { - // On gnu (libgcc), `__register_frame` will walk the FDEs until an - // entry of length 0 - __register_frame(unwind_info); - let info = NonNull::new(unwind_info.cast_mut()).unwrap(); - registrations.push(SendSyncPtr::new(info)); } Ok(UnwindRegistration { registrations }) diff --git a/crates/wasmtime/src/runtime/vm/sys/unix/vm.rs b/crates/wasmtime/src/runtime/vm/sys/unix/vm.rs index bef62c0328..f11af94c4c 100644 --- a/crates/wasmtime/src/runtime/vm/sys/unix/vm.rs +++ b/crates/wasmtime/src/runtime/vm/sys/unix/vm.rs @@ -7,22 +7,28 @@ use std::io; use std::sync::Arc; pub unsafe fn expose_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> { - mprotect(ptr.cast(), len, MprotectFlags::READ | MprotectFlags::WRITE)?; + unsafe { + mprotect(ptr.cast(), len, MprotectFlags::READ | MprotectFlags::WRITE)?; + } Ok(()) } pub unsafe fn hide_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> { - mprotect(ptr.cast(), len, MprotectFlags::empty())?; + unsafe { + mprotect(ptr.cast(), len, MprotectFlags::empty())?; + } Ok(()) } pub unsafe fn erase_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> { - let ret = mmap_anonymous( - ptr.cast(), - len, - ProtFlags::empty(), - MapFlags::PRIVATE | super::mmap::MMAP_NORESERVE_FLAG | MapFlags::FIXED, - )?; + let ret = unsafe { + mmap_anonymous( + ptr.cast(), + len, + ProtFlags::empty(), + MapFlags::PRIVATE | super::mmap::MMAP_NORESERVE_FLAG | MapFlags::FIXED, + )? + }; assert_eq!(ptr, ret.cast()); Ok(()) } @@ -159,12 +165,14 @@ impl MemoryImageSource { } pub unsafe fn remap_as_zeros_at(&self, base: *mut u8, len: usize) -> io::Result<()> { - let ptr = mmap_anonymous( - base.cast(), - len, - ProtFlags::READ | ProtFlags::WRITE, - MapFlags::PRIVATE | super::mmap::MMAP_NORESERVE_FLAG | MapFlags::FIXED, - )?; + let ptr = unsafe { + mmap_anonymous( + base.cast(), + len, + ProtFlags::READ | ProtFlags::WRITE, + MapFlags::PRIVATE | super::mmap::MMAP_NORESERVE_FLAG | MapFlags::FIXED, + )? + }; assert_eq!(base, ptr.cast()); Ok(()) } diff --git a/crates/wasmtime/src/runtime/vm/sys/windows/mmap.rs b/crates/wasmtime/src/runtime/vm/sys/windows/mmap.rs index e5df06b6ce..dee78c9f5c 100644 --- a/crates/wasmtime/src/runtime/vm/sys/windows/mmap.rs +++ b/crates/wasmtime/src/runtime/vm/sys/windows/mmap.rs @@ -196,20 +196,24 @@ impl Mmap { PAGE_EXECUTE_READ }; let mut old = 0; - let base = self.as_send_sync_ptr().as_ptr().add(range.start).cast(); - let result = VirtualProtect(base, range.end - range.start, flags, &mut old); - if result == 0 { - bail!(io::Error::last_os_error()); + unsafe { + let base = self.as_send_sync_ptr().as_ptr().add(range.start).cast(); + let result = VirtualProtect(base, range.end - range.start, flags, &mut old); + if result == 0 { + bail!(io::Error::last_os_error()); + } } Ok(()) } pub unsafe fn make_readonly(&self, range: Range) -> Result<()> { let mut old = 0; - let base = self.as_send_sync_ptr().as_ptr().add(range.start).cast(); - let result = VirtualProtect(base, range.end - range.start, PAGE_READONLY, &mut old); - if result == 0 { - bail!(io::Error::last_os_error()); + unsafe { + let base = self.as_send_sync_ptr().as_ptr().add(range.start).cast(); + let result = VirtualProtect(base, range.end - range.start, PAGE_READONLY, &mut old); + if result == 0 { + bail!(io::Error::last_os_error()); + } } Ok(()) } diff --git a/crates/wasmtime/src/runtime/vm/sys/windows/unwind64.rs b/crates/wasmtime/src/runtime/vm/sys/windows/unwind64.rs index 9ac2ac1c13..e149f56ddf 100644 --- a/crates/wasmtime/src/runtime/vm/sys/windows/unwind64.rs +++ b/crates/wasmtime/src/runtime/vm/sys/windows/unwind64.rs @@ -25,12 +25,14 @@ impl UnwindRegistration { assert!(unwind_info as usize % 4 == 0); let unit_len = mem::size_of::(); assert!(unwind_len % unit_len == 0); - if !RtlAddFunctionTable( - unwind_info as *mut Entry, - (unwind_len / unit_len).try_into().unwrap(), - base_address as _, - ) { - bail!("failed to register function table"); + unsafe { + if !RtlAddFunctionTable( + unwind_info as *mut Entry, + (unwind_len / unit_len).try_into().unwrap(), + base_address as _, + ) { + bail!("failed to register function table"); + } } Ok(UnwindRegistration { diff --git a/crates/wasmtime/src/runtime/vm/sys/windows/vectored_exceptions.rs b/crates/wasmtime/src/runtime/vm/sys/windows/vectored_exceptions.rs index 8774ce3d53..cf896477d8 100644 --- a/crates/wasmtime/src/runtime/vm/sys/windows/vectored_exceptions.rs +++ b/crates/wasmtime/src/runtime/vm/sys/windows/vectored_exceptions.rs @@ -21,7 +21,7 @@ impl TrapHandler { // our trap handler needs to go first, so that we can recover from // wasm faults and continue execution, so pass `1` as a true value // here. - let handle = AddVectoredExceptionHandler(1, Some(exception_handler)); + let handle = unsafe { AddVectoredExceptionHandler(1, Some(exception_handler)) }; if handle.is_null() { panic!( "failed to add exception handler: {}", @@ -57,7 +57,7 @@ unsafe extern "system" fn exception_handler(exception_info: *mut EXCEPTION_POINT // Check the kind of exception, since we only handle a subset within // wasm code. If anything else happens we want to defer to whatever // the rest of the system wants to do for this exception. - let record = &*(*exception_info).ExceptionRecord; + let record = unsafe { &*(*exception_info).ExceptionRecord }; if record.ExceptionCode != EXCEPTION_ACCESS_VIOLATION && record.ExceptionCode != EXCEPTION_ILLEGAL_INSTRUCTION && record.ExceptionCode != EXCEPTION_INT_DIVIDE_BY_ZERO @@ -85,7 +85,7 @@ unsafe extern "system" fn exception_handler(exception_info: *mut EXCEPTION_POINT Some(info) => info, None => return ExceptionContinueSearch, }; - let context = &*(*exception_info).ContextRecord; + let context = unsafe { &*(*exception_info).ContextRecord }; cfg_if::cfg_if! { if #[cfg(target_arch = "x86_64")] { let regs = TrapRegisters { @@ -95,7 +95,7 @@ unsafe extern "system" fn exception_handler(exception_info: *mut EXCEPTION_POINT } else if #[cfg(target_arch = "aarch64")] { let regs = TrapRegisters { pc: context.Pc as usize, - fp: context.Anonymous.Anonymous.Fp as usize, + fp: unsafe { context.Anonymous.Anonymous.Fp as usize }, }; } else if #[cfg(target_arch = "x86")] { let regs = TrapRegisters { @@ -119,7 +119,7 @@ unsafe extern "system" fn exception_handler(exception_info: *mut EXCEPTION_POINT match info.test_if_trap(regs, faulting_addr, |handler| handler(exception_info)) { TrapTest::NotWasm => ExceptionContinueSearch, TrapTest::HandledByEmbedder => ExceptionContinueExecution, - TrapTest::Trap { jmp_buf } => super::traphandlers::wasmtime_longjmp(jmp_buf), + TrapTest::Trap { jmp_buf } => unsafe { super::traphandlers::wasmtime_longjmp(jmp_buf) }, } }) } diff --git a/crates/wasmtime/src/runtime/vm/sys/windows/vm.rs b/crates/wasmtime/src/runtime/vm/sys/windows/vm.rs index 0b70742db8..af69e15b56 100644 --- a/crates/wasmtime/src/runtime/vm/sys/windows/vm.rs +++ b/crates/wasmtime/src/runtime/vm/sys/windows/vm.rs @@ -10,7 +10,7 @@ pub unsafe fn expose_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<() if len == 0 { return Ok(()); } - if VirtualAlloc(ptr.cast(), len, MEM_COMMIT, PAGE_READWRITE).is_null() { + if unsafe { VirtualAlloc(ptr.cast(), len, MEM_COMMIT, PAGE_READWRITE).is_null() } { Err(std::io::Error::last_os_error()) } else { Ok(()) @@ -18,14 +18,14 @@ pub unsafe fn expose_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<() } pub unsafe fn hide_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> { - erase_existing_mapping(ptr, len) + unsafe { erase_existing_mapping(ptr, len) } } pub unsafe fn erase_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> { if len == 0 { return Ok(()); } - if VirtualFree(ptr.cast(), len, MEM_DECOMMIT) == 0 { + if unsafe { VirtualFree(ptr.cast(), len, MEM_DECOMMIT) == 0 } { Err(std::io::Error::last_os_error()) } else { Ok(()) @@ -34,12 +34,12 @@ pub unsafe fn erase_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> #[cfg(feature = "pooling-allocator")] pub unsafe fn commit_pages(addr: *mut u8, len: usize) -> io::Result<()> { - expose_existing_mapping(addr, len) + unsafe { expose_existing_mapping(addr, len) } } #[cfg(feature = "pooling-allocator")] pub unsafe fn decommit_pages(addr: *mut u8, len: usize) -> io::Result<()> { - erase_existing_mapping(addr, len) + unsafe { erase_existing_mapping(addr, len) } } pub fn get_page_size() -> usize { diff --git a/crates/wasmtime/src/runtime/vm/table.rs b/crates/wasmtime/src/runtime/vm/table.rs index 25c8495c4f..50d01a1b4d 100644 --- a/crates/wasmtime/src/runtime/vm/table.rs +++ b/crates/wasmtime/src/runtime/vm/table.rs @@ -392,9 +392,11 @@ pub(crate) fn wasm_to_table_type(ty: WasmRefType) -> TableElementType { /// `Option`'s `None` variant is represented with zero. unsafe fn alloc_dynamic_table_elements(len: usize) -> Result>> { debug_assert!( - core::mem::MaybeUninit::>::zeroed() - .assume_init() - .is_none(), + unsafe { + core::mem::MaybeUninit::>::zeroed() + .assume_init() + .is_none() + }, "null table elements are represented with zeroed memory" ); @@ -410,10 +412,10 @@ unsafe fn alloc_dynamic_table_elements(len: usize) -> Result>> let layout = Layout::from_size_align(size, align)?; - let ptr = alloc::alloc::alloc_zeroed(layout); + let ptr = unsafe { alloc::alloc::alloc_zeroed(layout) }; ensure!(!ptr.is_null(), "failed to allocate memory for table"); - let elems = Vec::>::from_raw_parts(ptr.cast(), len, len); + let elems = unsafe { Vec::>::from_raw_parts(ptr.cast(), len, len) }; debug_assert!(elems.iter().all(|e| e.is_none())); Ok(elems) @@ -458,8 +460,10 @@ impl Table { match wasm_to_table_type(ty.ref_type) { TableElementType::Func => { let len = { - let data = data.as_non_null().as_ref(); - let (before, data, after) = data.align_to::(); + let (before, data, after) = unsafe { + let data = data.as_non_null().as_ref(); + data.align_to::() + }; assert!(before.is_empty()); assert!(after.is_empty()); data.len() @@ -482,8 +486,10 @@ impl Table { } TableElementType::GcRef => { let len = { - let data = data.as_non_null().as_ref(); - let (before, data, after) = data.align_to::>(); + let (before, data, after) = unsafe { + let data = data.as_non_null().as_ref(); + data.align_to::>() + }; assert!(before.is_empty()); assert!(after.is_empty()); data.len() @@ -502,8 +508,10 @@ impl Table { } TableElementType::Cont => { let len = { - let data = data.as_non_null().as_ref(); - let (before, data, after) = data.align_to::(); + let (before, data, after) = unsafe { + let data = data.as_non_null().as_ref(); + data.align_to::() + }; assert!(before.is_empty()); assert!(after.is_empty()); data.len() @@ -901,52 +909,79 @@ impl Table { Ok(()) } - /// Copy `len` elements from `src_table[src_index..]` into `dst_table[dst_index..]`. + /// Copy `len` elements from `self[src_index..][..len]` into + /// `dst_table[dst_index..][..len]`. + /// + /// # Errors + /// + /// Returns an error if the range is out of bounds of either the source or + /// destination tables. + pub fn copy_to( + &self, + dst: &mut Table, + gc_store: Option<&mut GcStore>, + dst_index: u64, + src_index: u64, + len: u64, + ) -> Result<(), Trap> { + let (src_range, dst_range) = Table::validate_copy(self, dst, dst_index, src_index, len)?; + Self::copy_elements(gc_store, dst, self, dst_range, src_range); + Ok(()) + } + + /// Copy `len` elements from `self[src_index..][..len]` into + /// `self[dst_index..][..len]`. /// /// # Errors /// /// Returns an error if the range is out of bounds of either the source or /// destination tables. - pub unsafe fn copy( + pub fn copy_within( + &mut self, gc_store: Option<&mut GcStore>, - dst_table: *mut Self, - src_table: *mut Self, dst_index: u64, src_index: u64, len: u64, ) -> Result<(), Trap> { + let (src_range, dst_range) = Table::validate_copy(self, self, dst_index, src_index, len)?; + self.copy_elements_within(gc_store, dst_range, src_range); + Ok(()) + } + + /// Copy `len` elements from `src_table[src_index..]` into `dst_table[dst_index..]`. + /// + /// # Errors + /// + /// Returns an error if the range is out of bounds of either the source or + /// destination tables. + fn validate_copy( + src: &Table, + dst: &Table, + dst_index: u64, + src_index: u64, + len: u64, + ) -> Result<(Range, Range), Trap> { // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-copy let src_index = usize::try_from(src_index).map_err(|_| Trap::TableOutOfBounds)?; let dst_index = usize::try_from(dst_index).map_err(|_| Trap::TableOutOfBounds)?; let len = usize::try_from(len).map_err(|_| Trap::TableOutOfBounds)?; - if src_index - .checked_add(len) - .map_or(true, |n| n > (*src_table).size()) - || dst_index - .checked_add(len) - .map_or(true, |m| m > (*dst_table).size()) + if src_index.checked_add(len).map_or(true, |n| n > src.size()) + || dst_index.checked_add(len).map_or(true, |m| m > dst.size()) { return Err(Trap::TableOutOfBounds); } debug_assert!( - (*dst_table).element_type() == (*src_table).element_type(), + dst.element_type() == src.element_type(), "table element type mismatch" ); let src_range = src_index..src_index + len; let dst_range = dst_index..dst_index + len; - // Check if the tables are the same as we cannot mutably borrow and also borrow the same `RefCell` - if ptr::eq(dst_table, src_table) { - (*dst_table).copy_elements_within(gc_store, dst_range, src_range); - } else { - Self::copy_elements(gc_store, &mut *dst_table, &*src_table, dst_range, src_range); - } - - Ok(()) + Ok((src_range, dst_range)) } /// Return a `VMTableDefinition` for exposing the table to compiled wasm code. diff --git a/crates/wasmtime/src/runtime/vm/traphandlers.rs b/crates/wasmtime/src/runtime/vm/traphandlers.rs index d1dad2f4ff..4e9867aebf 100644 --- a/crates/wasmtime/src/runtime/vm/traphandlers.rs +++ b/crates/wasmtime/src/runtime/vm/traphandlers.rs @@ -84,7 +84,7 @@ fn lazy_per_thread_init() { /// stack. They will be skipped and not executed. #[cfg(has_host_compiler_backend)] pub(super) unsafe fn raise_preexisting_trap() -> ! { - tls::with(|info| info.unwrap().unwind()) + tls::with(|info| unsafe { info.unwrap().unwind() }) } /// Invokes the closure `f` and returns a `bool` if it succeeded. @@ -406,21 +406,26 @@ where // interpreter since this branch is only ever taken if the interpreter // isn't present. #[cfg(has_host_compiler_backend)] - ExecutorRef::Native => traphandlers::wasmtime_setjmp( - cx.jmp_buf.as_ptr(), - { - extern "C" fn call_closure(payload: *mut u8, caller: NonNull) -> bool - where - F: FnMut(NonNull, Option>) -> bool, + ExecutorRef::Native => unsafe { + traphandlers::wasmtime_setjmp( + cx.jmp_buf.as_ptr(), { - unsafe { (*(payload as *mut F))(caller, None) } - } + extern "C" fn call_closure( + payload: *mut u8, + caller: NonNull, + ) -> bool + where + F: FnMut(NonNull, Option>) -> bool, + { + unsafe { (*(payload as *mut F))(caller, None) } + } - call_closure:: - }, - &mut closure as *mut F as *mut u8, - caller, - ), + call_closure:: + }, + &mut closure as *mut F as *mut u8, + caller, + ) + }, }); return match result { @@ -521,22 +526,22 @@ mod call_thread_state { /// Get the saved FP upon exit from Wasm for the previous `CallThreadState`. pub unsafe fn old_last_wasm_exit_fp(&self) -> usize { - (&*self.old_state).last_wasm_exit_fp + unsafe { (&*self.old_state).last_wasm_exit_fp } } /// Get the saved PC upon exit from Wasm for the previous `CallThreadState`. pub unsafe fn old_last_wasm_exit_pc(&self) -> usize { - (&*self.old_state).last_wasm_exit_pc + unsafe { (&*self.old_state).last_wasm_exit_pc } } /// Get the saved FP upon entry into Wasm for the previous `CallThreadState`. pub unsafe fn old_last_wasm_entry_fp(&self) -> usize { - (&*self.old_state).last_wasm_entry_fp + unsafe { (&*self.old_state).last_wasm_entry_fp } } /// Get the saved `VMStackChain` for the previous `CallThreadState`. pub unsafe fn old_stack_chain(&self) -> VMStackChain { - (&*self.old_state).stack_chain.clone() + unsafe { (&*self.old_state).stack_chain.clone() } } /// Get the previous `CallThreadState`. @@ -589,23 +594,25 @@ mod call_thread_state { #[cfg(feature = "async")] pub(super) unsafe fn swap(&self) { unsafe fn swap(a: &core::cell::UnsafeCell, b: &mut T) { - core::mem::swap(&mut *a.get(), b) + unsafe { core::mem::swap(&mut *a.get(), b) } } - let cx = self.vm_store_context.as_ref(); - swap( - &cx.last_wasm_exit_fp, - &mut (*self.old_state).last_wasm_exit_fp, - ); - swap( - &cx.last_wasm_exit_pc, - &mut (*self.old_state).last_wasm_exit_pc, - ); - swap( - &cx.last_wasm_entry_fp, - &mut (*self.old_state).last_wasm_entry_fp, - ); - swap(&cx.stack_chain, &mut (*self.old_state).stack_chain); + unsafe { + let cx = self.vm_store_context.as_ref(); + swap( + &cx.last_wasm_exit_fp, + &mut (*self.old_state).last_wasm_exit_fp, + ); + swap( + &cx.last_wasm_exit_pc, + &mut (*self.old_state).last_wasm_exit_pc, + ); + swap( + &cx.last_wasm_entry_fp, + &mut (*self.old_state).last_wasm_entry_fp, + ); + swap(&cx.stack_chain, &mut (*self.old_state).stack_chain); + } } } } @@ -695,7 +702,9 @@ impl CallThreadState { unsafe fn unwind(&self) -> ! { debug_assert!(!self.jmp_buf.get().is_null()); debug_assert!(self.jmp_buf.get() != CallThreadState::JMP_BUF_INTERPRETER_SENTINEL); - traphandlers::wasmtime_longjmp(self.jmp_buf.get()); + unsafe { + traphandlers::wasmtime_longjmp(self.jmp_buf.get()); + } } fn capture_backtrace( @@ -1024,17 +1033,21 @@ pub(crate) mod tls { // saved in the oldest activation's state on the stack. The store's // current state then describes the youngest activation which is // restored via the loop below. - if let Some(state) = self.state.as_ref() { - state.swap(); + unsafe { + if let Some(state) = self.state.as_ref() { + state.swap(); + } } // Our `state` pointer is a linked list of oldest-to-youngest so by // pushing in order of the list we restore the youngest-to-oldest // list as stored in the state of this current thread. let mut ptr = self.state; - while let Some(state) = ptr.as_ref() { - ptr = state.prev.replace(core::ptr::null_mut()); - state.push(); + unsafe { + while let Some(state) = ptr.as_ref() { + ptr = state.prev.replace(core::ptr::null_mut()); + state.push(); + } } ret } @@ -1108,8 +1121,10 @@ pub(crate) mod tls { // the store to hook things up again. let ptr = raw::get(); if ptr == thread_head { - if let Some(state) = ret.state.as_ref() { - state.swap(); + unsafe { + if let Some(state) = ret.state.as_ref() { + state.swap(); + } } break ret; @@ -1121,9 +1136,11 @@ pub(crate) mod tls { // `AsyncWasmCallState` is stored in reverse order so a // subsequent `push` later on pushes everything in the right // order. - (*ptr).pop(); - if let Some(state) = ret.state.as_ref() { - (*ptr).prev.set(state); + unsafe { + (*ptr).pop(); + if let Some(state) = ret.state.as_ref() { + (*ptr).prev.set(state); + } } ret.state = ptr; } diff --git a/crates/wasmtime/src/runtime/vm/traphandlers/backtrace.rs b/crates/wasmtime/src/runtime/vm/traphandlers/backtrace.rs index ab6c801522..29fdb5dd6a 100644 --- a/crates/wasmtime/src/runtime/vm/traphandlers/backtrace.rs +++ b/crates/wasmtime/src/runtime/vm/traphandlers/backtrace.rs @@ -67,10 +67,12 @@ impl Backtrace { trap_pc_and_fp: Option<(usize, usize)>, ) -> Backtrace { let mut frames = vec![]; - Self::trace_with_trap_state(vm_store_context, unwind, state, trap_pc_and_fp, |frame| { - frames.push(frame); - ControlFlow::Continue(()) - }); + unsafe { + Self::trace_with_trap_state(vm_store_context, unwind, state, trap_pc_and_fp, |frame| { + frames.push(frame); + ControlFlow::Continue(()) + }); + } Backtrace(frames) } @@ -156,14 +158,14 @@ impl Backtrace { } // Either there is no Wasm currently on the stack, or we exited Wasm // through the Wasm-to-host trampoline. - None => { + None => unsafe { let pc = *(*vm_store_context).last_wasm_exit_pc.get(); let fp = *(*vm_store_context).last_wasm_exit_fp.get(); (pc, fp) - } + }, }; - let stack_chain = (*(*vm_store_context).stack_chain.get()).clone(); + let stack_chain = unsafe { (*(*vm_store_context).stack_chain.get()).clone() }; // The first value in `activations` is for the most recently running // wasm. We thus provide the stack chain of `first_wasm_state` to @@ -172,40 +174,40 @@ impl Backtrace { // chain. This is justified because only the most recent execution of // wasm may execute off the initial stack (see comments in // `wasmtime::invoke_wasm_and_catch_traps` for details). - let activations = core::iter::once(( - stack_chain, - last_wasm_exit_pc, - last_wasm_exit_fp, - *(*vm_store_context).last_wasm_entry_fp.get(), - )) - .chain( - state - .iter() - .flat_map(|state| state.iter()) - .filter(|state| core::ptr::eq(vm_store_context, state.vm_store_context.as_ptr())) - .map(|state| { - ( - state.old_stack_chain(), - state.old_last_wasm_exit_pc(), - state.old_last_wasm_exit_fp(), - state.old_last_wasm_entry_fp(), - ) - }), - ) - .take_while(|(chain, pc, fp, sp)| { - if *pc == 0 { - debug_assert_eq!(*fp, 0); - debug_assert_eq!(*sp, 0); - } else { - debug_assert_ne!(chain.clone(), VMStackChain::Absent) - } - *pc != 0 - }); + let activations = + core::iter::once((stack_chain, last_wasm_exit_pc, last_wasm_exit_fp, unsafe { + *(*vm_store_context).last_wasm_entry_fp.get() + })) + .chain( + state + .iter() + .flat_map(|state| state.iter()) + .filter(|state| { + core::ptr::eq(vm_store_context, state.vm_store_context.as_ptr()) + }) + .map(|state| unsafe { + ( + state.old_stack_chain(), + state.old_last_wasm_exit_pc(), + state.old_last_wasm_exit_fp(), + state.old_last_wasm_entry_fp(), + ) + }), + ) + .take_while(|(chain, pc, fp, sp)| { + if *pc == 0 { + debug_assert_eq!(*fp, 0); + debug_assert_eq!(*sp, 0); + } else { + debug_assert_ne!(chain.clone(), VMStackChain::Absent) + } + *pc != 0 + }); for (chain, pc, fp, sp) in activations { - if let ControlFlow::Break(()) = - Self::trace_through_continuations(unwind, chain, pc, fp, sp, &mut f) - { + let res = + unsafe { Self::trace_through_continuations(unwind, chain, pc, fp, sp, &mut f) }; + if let ControlFlow::Break(()) = res { log::trace!("====== Done Capturing Backtrace (closure break) ======"); return; } @@ -234,7 +236,9 @@ impl Backtrace { // Handle the stack that is currently running (which may be a // continuation or the initial stack). - wasmtime_unwinder::visit_frames(unwind, pc, fp, trampoline_fp, &mut f)?; + unsafe { + wasmtime_unwinder::visit_frames(unwind, pc, fp, trampoline_fp, &mut f)?; + } // Note that the rest of this function has no effect if `chain` is // `Some(VMStackChain::InitialStack(_))` (i.e., there is only one stack to @@ -242,9 +246,9 @@ impl Backtrace { assert_ne!(chain, VMStackChain::Absent); let stack_limits_vec: Vec<*mut VMStackLimits> = - chain.clone().into_stack_limits_iter().collect(); + unsafe { chain.clone().into_stack_limits_iter().collect() }; let continuations_vec: Vec<*mut VMContRef> = - chain.clone().into_continuation_iter().collect(); + unsafe { chain.clone().into_continuation_iter().collect() }; // The VMStackLimits of the currently running stack (whether that's a // continuation or the initial stack) contains undefined data, the @@ -288,13 +292,15 @@ impl Backtrace { debug_assert!(parent_stack_range.contains(&parent_limits.stack_limit)); }); - wasmtime_unwinder::visit_frames( - unwind, - resume_pc, - resume_fp, - parent_limits.last_wasm_entry_fp, - &mut f, - )? + unsafe { + wasmtime_unwinder::visit_frames( + unwind, + resume_pc, + resume_fp, + parent_limits.last_wasm_entry_fp, + &mut f, + )? + } } ControlFlow::Continue(()) } diff --git a/crates/wasmtime/src/runtime/vm/vmcontext.rs b/crates/wasmtime/src/runtime/vm/vmcontext.rs index 67aa41e208..ff2ba0141a 100644 --- a/crates/wasmtime/src/runtime/vm/vmcontext.rs +++ b/crates/wasmtime/src/runtime/vm/vmcontext.rs @@ -397,7 +397,7 @@ impl VMMemoryDefinition { /// `current_length`; see [`VMMemoryDefinition::current_length()`]. #[inline] pub unsafe fn load(ptr: *mut Self) -> Self { - let other = &*ptr; + let other = unsafe { &*ptr }; VMMemoryDefinition { base: other.base, current_length: other.current_length().into(), @@ -550,28 +550,30 @@ impl VMGlobalDefinition { raw: ValRaw, ) -> Result { let mut global = Self::new(); - match wasm_ty { - WasmValType::I32 => *global.as_i32_mut() = raw.get_i32(), - WasmValType::I64 => *global.as_i64_mut() = raw.get_i64(), - WasmValType::F32 => *global.as_f32_bits_mut() = raw.get_f32(), - WasmValType::F64 => *global.as_f64_bits_mut() = raw.get_f64(), - WasmValType::V128 => global.set_u128(raw.get_v128()), - WasmValType::Ref(r) => match r.heap_type.top() { - WasmHeapTopType::Extern => { - let r = VMGcRef::from_raw_u32(raw.get_externref()); - global.init_gc_ref(store.gc_store_mut()?, r.as_ref()) - } - WasmHeapTopType::Any => { - let r = VMGcRef::from_raw_u32(raw.get_anyref()); - global.init_gc_ref(store.gc_store_mut()?, r.as_ref()) - } - WasmHeapTopType::Func => *global.as_func_ref_mut() = raw.get_funcref().cast(), - WasmHeapTopType::Cont => *global.as_func_ref_mut() = raw.get_funcref().cast(), // TODO(#10248): temporary hack. - WasmHeapTopType::Exn => { - let r = VMGcRef::from_raw_u32(raw.get_exnref()); - global.init_gc_ref(store.gc_store_mut()?, r.as_ref()) - } - }, + unsafe { + match wasm_ty { + WasmValType::I32 => *global.as_i32_mut() = raw.get_i32(), + WasmValType::I64 => *global.as_i64_mut() = raw.get_i64(), + WasmValType::F32 => *global.as_f32_bits_mut() = raw.get_f32(), + WasmValType::F64 => *global.as_f64_bits_mut() = raw.get_f64(), + WasmValType::V128 => global.set_u128(raw.get_v128()), + WasmValType::Ref(r) => match r.heap_type.top() { + WasmHeapTopType::Extern => { + let r = VMGcRef::from_raw_u32(raw.get_externref()); + global.init_gc_ref(store.gc_store_mut()?, r.as_ref()) + } + WasmHeapTopType::Any => { + let r = VMGcRef::from_raw_u32(raw.get_anyref()); + global.init_gc_ref(store.gc_store_mut()?, r.as_ref()) + } + WasmHeapTopType::Func => *global.as_func_ref_mut() = raw.get_funcref().cast(), + WasmHeapTopType::Cont => *global.as_func_ref_mut() = raw.get_funcref().cast(), // TODO(#10248): temporary hack. + WasmHeapTopType::Exn => { + let r = VMGcRef::from_raw_u32(raw.get_exnref()); + global.init_gc_ref(store.gc_store_mut()?, r.as_ref()) + } + }, + } } Ok(global) } @@ -586,113 +588,115 @@ impl VMGlobalDefinition { store: &mut StoreOpaque, wasm_ty: WasmValType, ) -> Result { - Ok(match wasm_ty { - WasmValType::I32 => ValRaw::i32(*self.as_i32()), - WasmValType::I64 => ValRaw::i64(*self.as_i64()), - WasmValType::F32 => ValRaw::f32(*self.as_f32_bits()), - WasmValType::F64 => ValRaw::f64(*self.as_f64_bits()), - WasmValType::V128 => ValRaw::v128(self.get_u128()), - WasmValType::Ref(r) => match r.heap_type.top() { - WasmHeapTopType::Extern => ValRaw::externref(match self.as_gc_ref() { - Some(r) => store.gc_store_mut()?.clone_gc_ref(r).as_raw_u32(), - None => 0, - }), - WasmHeapTopType::Any => ValRaw::anyref({ - match self.as_gc_ref() { - Some(r) => store.gc_store_mut()?.clone_gc_ref(r).as_raw_u32(), - None => 0, - } - }), - WasmHeapTopType::Exn => ValRaw::exnref({ - match self.as_gc_ref() { + unsafe { + Ok(match wasm_ty { + WasmValType::I32 => ValRaw::i32(*self.as_i32()), + WasmValType::I64 => ValRaw::i64(*self.as_i64()), + WasmValType::F32 => ValRaw::f32(*self.as_f32_bits()), + WasmValType::F64 => ValRaw::f64(*self.as_f64_bits()), + WasmValType::V128 => ValRaw::v128(self.get_u128()), + WasmValType::Ref(r) => match r.heap_type.top() { + WasmHeapTopType::Extern => ValRaw::externref(match self.as_gc_ref() { Some(r) => store.gc_store_mut()?.clone_gc_ref(r).as_raw_u32(), None => 0, - } - }), - WasmHeapTopType::Func => ValRaw::funcref(self.as_func_ref().cast()), - WasmHeapTopType::Cont => todo!(), // FIXME: #10248 stack switching support. - }, - }) + }), + WasmHeapTopType::Any => ValRaw::anyref({ + match self.as_gc_ref() { + Some(r) => store.gc_store_mut()?.clone_gc_ref(r).as_raw_u32(), + None => 0, + } + }), + WasmHeapTopType::Exn => ValRaw::exnref({ + match self.as_gc_ref() { + Some(r) => store.gc_store_mut()?.clone_gc_ref(r).as_raw_u32(), + None => 0, + } + }), + WasmHeapTopType::Func => ValRaw::funcref(self.as_func_ref().cast()), + WasmHeapTopType::Cont => todo!(), // FIXME: #10248 stack switching support. + }, + }) + } } /// Return a reference to the value as an i32. pub unsafe fn as_i32(&self) -> &i32 { - &*(self.storage.as_ref().as_ptr().cast::()) + unsafe { &*(self.storage.as_ref().as_ptr().cast::()) } } /// Return a mutable reference to the value as an i32. pub unsafe fn as_i32_mut(&mut self) -> &mut i32 { - &mut *(self.storage.as_mut().as_mut_ptr().cast::()) + unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::()) } } /// Return a reference to the value as a u32. pub unsafe fn as_u32(&self) -> &u32 { - &*(self.storage.as_ref().as_ptr().cast::()) + unsafe { &*(self.storage.as_ref().as_ptr().cast::()) } } /// Return a mutable reference to the value as an u32. pub unsafe fn as_u32_mut(&mut self) -> &mut u32 { - &mut *(self.storage.as_mut().as_mut_ptr().cast::()) + unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::()) } } /// Return a reference to the value as an i64. pub unsafe fn as_i64(&self) -> &i64 { - &*(self.storage.as_ref().as_ptr().cast::()) + unsafe { &*(self.storage.as_ref().as_ptr().cast::()) } } /// Return a mutable reference to the value as an i64. pub unsafe fn as_i64_mut(&mut self) -> &mut i64 { - &mut *(self.storage.as_mut().as_mut_ptr().cast::()) + unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::()) } } /// Return a reference to the value as an u64. pub unsafe fn as_u64(&self) -> &u64 { - &*(self.storage.as_ref().as_ptr().cast::()) + unsafe { &*(self.storage.as_ref().as_ptr().cast::()) } } /// Return a mutable reference to the value as an u64. pub unsafe fn as_u64_mut(&mut self) -> &mut u64 { - &mut *(self.storage.as_mut().as_mut_ptr().cast::()) + unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::()) } } /// Return a reference to the value as an f32. pub unsafe fn as_f32(&self) -> &f32 { - &*(self.storage.as_ref().as_ptr().cast::()) + unsafe { &*(self.storage.as_ref().as_ptr().cast::()) } } /// Return a mutable reference to the value as an f32. pub unsafe fn as_f32_mut(&mut self) -> &mut f32 { - &mut *(self.storage.as_mut().as_mut_ptr().cast::()) + unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::()) } } /// Return a reference to the value as f32 bits. pub unsafe fn as_f32_bits(&self) -> &u32 { - &*(self.storage.as_ref().as_ptr().cast::()) + unsafe { &*(self.storage.as_ref().as_ptr().cast::()) } } /// Return a mutable reference to the value as f32 bits. pub unsafe fn as_f32_bits_mut(&mut self) -> &mut u32 { - &mut *(self.storage.as_mut().as_mut_ptr().cast::()) + unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::()) } } /// Return a reference to the value as an f64. pub unsafe fn as_f64(&self) -> &f64 { - &*(self.storage.as_ref().as_ptr().cast::()) + unsafe { &*(self.storage.as_ref().as_ptr().cast::()) } } /// Return a mutable reference to the value as an f64. pub unsafe fn as_f64_mut(&mut self) -> &mut f64 { - &mut *(self.storage.as_mut().as_mut_ptr().cast::()) + unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::()) } } /// Return a reference to the value as f64 bits. pub unsafe fn as_f64_bits(&self) -> &u64 { - &*(self.storage.as_ref().as_ptr().cast::()) + unsafe { &*(self.storage.as_ref().as_ptr().cast::()) } } /// Return a mutable reference to the value as f64 bits. pub unsafe fn as_f64_bits_mut(&mut self) -> &mut u64 { - &mut *(self.storage.as_mut().as_mut_ptr().cast::()) + unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::()) } } /// Gets the underlying 128-bit vector value. @@ -700,7 +704,7 @@ impl VMGlobalDefinition { // Note that vectors are stored in little-endian format while other types // are stored in native-endian format. pub unsafe fn get_u128(&self) -> u128 { - u128::from_le(*(self.storage.as_ref().as_ptr().cast::())) + unsafe { u128::from_le(*(self.storage.as_ref().as_ptr().cast::())) } } /// Sets the 128-bit vector values. @@ -708,23 +712,25 @@ impl VMGlobalDefinition { // Note that vectors are stored in little-endian format while other types // are stored in native-endian format. pub unsafe fn set_u128(&mut self, val: u128) { - *self.storage.as_mut().as_mut_ptr().cast::() = val.to_le(); + unsafe { + *self.storage.as_mut().as_mut_ptr().cast::() = val.to_le(); + } } /// Return a reference to the value as u128 bits. pub unsafe fn as_u128_bits(&self) -> &[u8; 16] { - &*(self.storage.as_ref().as_ptr().cast::<[u8; 16]>()) + unsafe { &*(self.storage.as_ref().as_ptr().cast::<[u8; 16]>()) } } /// Return a mutable reference to the value as u128 bits. pub unsafe fn as_u128_bits_mut(&mut self) -> &mut [u8; 16] { - &mut *(self.storage.as_mut().as_mut_ptr().cast::<[u8; 16]>()) + unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<[u8; 16]>()) } } /// Return a reference to the global value as a borrowed GC reference. pub unsafe fn as_gc_ref(&self) -> Option<&VMGcRef> { let raw_ptr = self.storage.as_ref().as_ptr().cast::>(); - let ret = (*raw_ptr).as_ref(); + let ret = unsafe { (*raw_ptr).as_ref() }; assert!(cfg!(feature = "gc") || ret.is_none()); ret } @@ -733,11 +739,13 @@ impl VMGlobalDefinition { pub unsafe fn init_gc_ref(&mut self, gc_store: &mut GcStore, gc_ref: Option<&VMGcRef>) { assert!(cfg!(feature = "gc") || gc_ref.is_none()); - let dest = &mut *(self - .storage - .as_mut() - .as_mut_ptr() - .cast::>>()); + let dest = unsafe { + &mut *(self + .storage + .as_mut() + .as_mut_ptr() + .cast::>>()) + }; gc_store.init_gc_ref(dest, gc_ref) } @@ -746,7 +754,7 @@ impl VMGlobalDefinition { pub unsafe fn write_gc_ref(&mut self, gc_store: &mut GcStore, gc_ref: Option<&VMGcRef>) { assert!(cfg!(feature = "gc") || gc_ref.is_none()); - let dest = &mut *(self.storage.as_mut().as_mut_ptr().cast::>()); + let dest = unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::>()) }; assert!(cfg!(feature = "gc") || dest.is_none()); gc_store.write_gc_ref(dest, gc_ref) @@ -754,12 +762,12 @@ impl VMGlobalDefinition { /// Return a reference to the value as a `VMFuncRef`. pub unsafe fn as_func_ref(&self) -> *mut VMFuncRef { - *(self.storage.as_ref().as_ptr().cast::<*mut VMFuncRef>()) + unsafe { *(self.storage.as_ref().as_ptr().cast::<*mut VMFuncRef>()) } } /// Return a mutable reference to the value as a `VMFuncRef`. pub unsafe fn as_func_ref_mut(&mut self) -> &mut *mut VMFuncRef { - &mut *(self.storage.as_mut().as_mut_ptr().cast::<*mut VMFuncRef>()) + unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<*mut VMFuncRef>()) } } } @@ -904,8 +912,8 @@ impl VMFuncRef { args_and_results: NonNull<[ValRaw]>, ) -> bool { match pulley { - Some(vm) => Self::array_call_interpreted(me, vm, caller, args_and_results), - None => Self::array_call_native(me, caller, args_and_results), + Some(vm) => unsafe { Self::array_call_interpreted(me, vm, caller, args_and_results) }, + None => unsafe { Self::array_call_native(me, caller, args_and_results) }, } } @@ -918,17 +926,19 @@ impl VMFuncRef { // If `caller` is actually a `VMArrayCallHostFuncContext` then skip the // interpreter, even though it's available, as `array_call` will be // native code. - if me.as_ref().vmctx.as_non_null().as_ref().magic - == wasmtime_environ::VM_ARRAY_CALL_HOST_FUNC_MAGIC - { - return Self::array_call_native(me, caller, args_and_results); + unsafe { + if me.as_ref().vmctx.as_non_null().as_ref().magic + == wasmtime_environ::VM_ARRAY_CALL_HOST_FUNC_MAGIC + { + return Self::array_call_native(me, caller, args_and_results); + } + vm.call( + me.as_ref().array_call.as_non_null().cast(), + me.as_ref().vmctx.as_non_null(), + caller, + args_and_results, + ) } - vm.call( - me.as_ref().array_call.as_non_null().cast(), - me.as_ref().vmctx.as_non_null(), - caller, - args_and_results, - ) } #[inline] @@ -937,20 +947,22 @@ impl VMFuncRef { caller: NonNull, args_and_results: NonNull<[ValRaw]>, ) -> bool { - union GetNativePointer { - native: VMArrayCallNative, - ptr: NonNull, - } - let native = GetNativePointer { - ptr: me.as_ref().array_call.as_non_null(), + unsafe { + union GetNativePointer { + native: VMArrayCallNative, + ptr: NonNull, + } + let native = GetNativePointer { + ptr: me.as_ref().array_call.as_non_null(), + } + .native; + native( + me.as_ref().vmctx.as_non_null(), + caller, + args_and_results.cast(), + args_and_results.len(), + ) } - .native; - native( - me.as_ref().vmctx.as_non_null(), - caller, - args_and_results.cast(), - args_and_results.len(), - ) } } @@ -1269,7 +1281,9 @@ impl VMContext { // bugs, meaning we don't actually read the magic and act differently // at runtime depending what it is, so this is a debug assertion as // opposed to a regular assertion. - debug_assert_eq!(opaque.as_ref().magic, VMCONTEXT_MAGIC); + unsafe { + debug_assert_eq!(opaque.as_ref().magic, VMCONTEXT_MAGIC); + } opaque.cast() } } diff --git a/crates/wasmtime/src/runtime/vm/vmcontext/vm_host_func_context.rs b/crates/wasmtime/src/runtime/vm/vmcontext/vm_host_func_context.rs index ca2ae8b47b..47ea2abf2a 100644 --- a/crates/wasmtime/src/runtime/vm/vmcontext/vm_host_func_context.rs +++ b/crates/wasmtime/src/runtime/vm/vmcontext/vm_host_func_context.rs @@ -71,7 +71,9 @@ impl VMArrayCallHostFuncContext { opaque: NonNull, ) -> NonNull { // See comments in `VMContext::from_opaque` for this debug assert - debug_assert_eq!(opaque.as_ref().magic, VM_ARRAY_CALL_HOST_FUNC_MAGIC); + unsafe { + debug_assert_eq!(opaque.as_ref().magic, VM_ARRAY_CALL_HOST_FUNC_MAGIC); + } opaque.cast() } } diff --git a/crates/wast/Cargo.toml b/crates/wast/Cargo.toml index 55b358eb50..8c87dd4afa 100644 --- a/crates/wast/Cargo.toml +++ b/crates/wast/Cargo.toml @@ -19,6 +19,10 @@ wasmtime = { workspace = true, features = ['cranelift', 'wat', 'runtime', 'gc', wast = { workspace = true, features = ['dwarf'] } log = { workspace = true } tokio = { workspace = true, features = ['rt'] } +json-from-wast = { workspace = true } +wasmparser = { workspace = true } +serde_json = { workspace = true } +object = { workspace = true, features = ['unaligned'] } [features] component-model = ['wasmtime/component-model'] diff --git a/crates/wast/src/component.rs b/crates/wast/src/component.rs index f9a1478f7e..c2fcc932f8 100644 --- a/crates/wast/src/component.rs +++ b/crates/wast/src/component.rs @@ -1,120 +1,123 @@ use crate::core; use anyhow::{Context, Result, bail}; +use json_from_wast::{ComponentConst, FloatConst}; use std::collections::BTreeSet; use std::fmt::Debug; -use wast::component::WastVal; -use wast::core::NanPattern; pub use wasmtime::component::*; -pub fn val(v: &WastVal<'_>) -> Result { +pub fn val(v: &ComponentConst<'_>) -> Result { Ok(match v { - WastVal::Bool(b) => Val::Bool(*b), - WastVal::U8(b) => Val::U8(*b), - WastVal::S8(b) => Val::S8(*b), - WastVal::U16(b) => Val::U16(*b), - WastVal::S16(b) => Val::S16(*b), - WastVal::U32(b) => Val::U32(*b), - WastVal::S32(b) => Val::S32(*b), - WastVal::U64(b) => Val::U64(*b), - WastVal::S64(b) => Val::S64(*b), - WastVal::F32(b) => Val::Float32(f32::from_bits(b.bits)), - WastVal::F64(b) => Val::Float64(f64::from_bits(b.bits)), - WastVal::Char(b) => Val::Char(*b), - WastVal::String(s) => Val::String(s.to_string()), - WastVal::List(vals) => { + ComponentConst::Bool(b) => Val::Bool(*b), + ComponentConst::U8(b) => Val::U8(b.0), + ComponentConst::S8(b) => Val::S8(b.0), + ComponentConst::U16(b) => Val::U16(b.0), + ComponentConst::S16(b) => Val::S16(b.0), + ComponentConst::U32(b) => Val::U32(b.0), + ComponentConst::S32(b) => Val::S32(b.0), + ComponentConst::U64(b) => Val::U64(b.0), + ComponentConst::S64(b) => Val::S64(b.0), + ComponentConst::F32(b) => Val::Float32(f32::from_bits(b.0)), + ComponentConst::F64(b) => Val::Float64(f64::from_bits(b.0)), + ComponentConst::Char(b) => Val::Char(*b), + ComponentConst::String(s) => Val::String(s.to_string()), + ComponentConst::List(vals) => { let vals = vals.iter().map(|v| val(v)).collect::>>()?; Val::List(vals) } - WastVal::Record(vals) => { + ComponentConst::Record(vals) => { let mut fields = Vec::new(); for (name, v) in vals { fields.push((name.to_string(), val(v)?)); } Val::Record(fields) } - WastVal::Tuple(vals) => { + ComponentConst::Tuple(vals) => { Val::Tuple(vals.iter().map(|v| val(v)).collect::>>()?) } - WastVal::Enum(name) => Val::Enum(name.to_string()), - WastVal::Variant(name, payload) => { + ComponentConst::Enum(name) => Val::Enum(name.to_string()), + ComponentConst::Variant { case, payload } => { let payload = payload_val(payload.as_deref())?; - Val::Variant(name.to_string(), payload) + Val::Variant(case.to_string(), payload) } - WastVal::Option(v) => Val::Option(match v { + ComponentConst::Option(v) => Val::Option(match v { Some(v) => Some(Box::new(val(v)?)), None => None, }), - WastVal::Result(v) => Val::Result(match v { + ComponentConst::Result(v) => Val::Result(match v { Ok(v) => Ok(payload_val(v.as_deref())?), Err(v) => Err(payload_val(v.as_deref())?), }), - WastVal::Flags(v) => Val::Flags(v.iter().map(|s| s.to_string()).collect()), + ComponentConst::Flags(v) => Val::Flags(v.iter().map(|s| s.to_string()).collect()), }) } -fn payload_val(v: Option<&WastVal<'_>>) -> Result>> { +fn payload_val(v: Option<&ComponentConst<'_>>) -> Result>> { match v { Some(v) => Ok(Some(Box::new(val(v)?))), None => Ok(None), } } -pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> { +pub fn match_val(expected: &ComponentConst<'_>, actual: &Val) -> Result<()> { match expected { - WastVal::Bool(e) => match actual { + ComponentConst::Bool(e) => match actual { Val::Bool(a) => match_debug(a, e), _ => mismatch(expected, actual), }, - WastVal::U8(e) => match actual { - Val::U8(a) => core::match_int(a, e), + ComponentConst::U8(e) => match actual { + Val::U8(a) => core::match_int(a, &e.0), _ => mismatch(expected, actual), }, - WastVal::S8(e) => match actual { - Val::S8(a) => core::match_int(a, e), + ComponentConst::S8(e) => match actual { + Val::S8(a) => core::match_int(a, &e.0), _ => mismatch(expected, actual), }, - WastVal::U16(e) => match actual { - Val::U16(a) => core::match_int(a, e), + ComponentConst::U16(e) => match actual { + Val::U16(a) => core::match_int(a, &e.0), _ => mismatch(expected, actual), }, - WastVal::S16(e) => match actual { - Val::S16(a) => core::match_int(a, e), + ComponentConst::S16(e) => match actual { + Val::S16(a) => core::match_int(a, &e.0), _ => mismatch(expected, actual), }, - WastVal::U32(e) => match actual { - Val::U32(a) => core::match_int(a, e), + ComponentConst::U32(e) => match actual { + Val::U32(a) => core::match_int(a, &e.0), _ => mismatch(expected, actual), }, - WastVal::S32(e) => match actual { - Val::S32(a) => core::match_int(a, e), + ComponentConst::S32(e) => match actual { + Val::S32(a) => core::match_int(a, &e.0), _ => mismatch(expected, actual), }, - WastVal::U64(e) => match actual { - Val::U64(a) => core::match_int(a, e), + ComponentConst::U64(e) => match actual { + Val::U64(a) => core::match_int(a, &e.0), _ => mismatch(expected, actual), }, - WastVal::S64(e) => match actual { - Val::S64(a) => core::match_int(a, e), + ComponentConst::S64(e) => match actual { + Val::S64(a) => core::match_int(a, &e.0), _ => mismatch(expected, actual), }, - WastVal::F32(e) => match actual { - Val::Float32(a) => core::match_f32(a.to_bits(), &NanPattern::Value(*e)), + ComponentConst::F32(e) => match actual { + Val::Float32(a) => { + core::match_f32(a.to_bits(), &FloatConst::Value(f32::from_bits(e.0))) + } _ => mismatch(expected, actual), }, - WastVal::F64(e) => match actual { - Val::Float64(a) => core::match_f64(a.to_bits(), &NanPattern::Value(*e)), + ComponentConst::F64(e) => match actual { + Val::Float64(a) => { + core::match_f64(a.to_bits(), &FloatConst::Value(f64::from_bits(e.0))) + } _ => mismatch(expected, actual), }, - WastVal::Char(e) => match actual { + ComponentConst::Char(e) => match actual { Val::Char(a) => match_debug(a, e), _ => mismatch(expected, actual), }, - WastVal::String(e) => match actual { - Val::String(a) => match_debug(&a[..], *e), + ComponentConst::String(e) => match actual { + Val::String(a) => match_debug(&a[..], e), _ => mismatch(expected, actual), }, - WastVal::List(e) => match actual { + ComponentConst::List(e) => match actual { Val::List(a) => { if e.len() != a.len() { bail!("expected {} values got {}", e.len(), a.len()); @@ -127,7 +130,7 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> { } _ => mismatch(expected, actual), }, - WastVal::Record(e) => match actual { + ComponentConst::Record(e) => match actual { Val::Record(a) => { if e.len() != e.len() { bail!("mismatched number of record fields"); @@ -143,7 +146,7 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> { } _ => mismatch(expected, actual), }, - WastVal::Tuple(e) => match actual { + ComponentConst::Tuple(e) => match actual { Val::Tuple(a) => { if e.len() != a.len() { bail!("expected {}-tuple, found {}-tuple", e.len(), a.len()); @@ -156,7 +159,10 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> { } _ => mismatch(expected, actual), }, - WastVal::Variant(name, e) => match actual { + ComponentConst::Variant { + case: name, + payload: e, + } => match actual { Val::Variant(discr, payload) => { if *discr != *name { bail!("expected discriminant `{name}` got `{discr}`"); @@ -165,7 +171,7 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> { } _ => mismatch(expected, actual), }, - WastVal::Enum(name) => match actual { + ComponentConst::Enum(name) => match actual { Val::Enum(a) => { if *a != *name { bail!("expected discriminant `{name}` got `{a}`"); @@ -175,7 +181,7 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> { } _ => mismatch(expected, actual), }, - WastVal::Option(e) => match actual { + ComponentConst::Option(e) => match actual { Val::Option(a) => match (e, a) { (None, None) => Ok(()), (Some(expected), Some(actual)) => match_val(expected, actual), @@ -184,7 +190,7 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> { }, _ => mismatch(expected, actual), }, - WastVal::Result(e) => match actual { + ComponentConst::Result(e) => match actual { Val::Result(a) => match (e, a) { (Ok(_), Err(_)) => bail!("expected `ok`, found `err`"), (Err(_), Ok(_)) => bail!("expected `err`, found `ok`"), @@ -193,9 +199,9 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> { }, _ => mismatch(expected, actual), }, - WastVal::Flags(e) => match actual { + ComponentConst::Flags(e) => match actual { Val::Flags(a) => { - let expected = e.iter().copied().collect::>(); + let expected = e.iter().map(|s| &s[..]).collect::>(); let actual = a.iter().map(|s| s.as_str()).collect::>(); match_debug(&actual, &expected) } @@ -206,7 +212,7 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> { fn match_payload_val( name: &str, - expected: Option<&WastVal<'_>>, + expected: Option<&ComponentConst<'_>>, actual: Option<&Val>, ) -> Result<()> { match (expected, actual) { @@ -234,29 +240,29 @@ where } } -fn mismatch(expected: &WastVal<'_>, actual: &Val) -> Result<()> { +fn mismatch(expected: &ComponentConst<'_>, actual: &Val) -> Result<()> { let expected = match expected { - WastVal::Bool(..) => "bool", - WastVal::U8(..) => "u8", - WastVal::S8(..) => "s8", - WastVal::U16(..) => "u16", - WastVal::S16(..) => "s16", - WastVal::U32(..) => "u32", - WastVal::S32(..) => "s32", - WastVal::U64(..) => "u64", - WastVal::S64(..) => "s64", - WastVal::F32(..) => "f32", - WastVal::F64(..) => "f64", - WastVal::Char(..) => "char", - WastVal::String(..) => "string", - WastVal::List(..) => "list", - WastVal::Record(..) => "record", - WastVal::Tuple(..) => "tuple", - WastVal::Enum(..) => "enum", - WastVal::Variant(..) => "variant", - WastVal::Option(..) => "option", - WastVal::Result(..) => "result", - WastVal::Flags(..) => "flags", + ComponentConst::Bool(..) => "bool", + ComponentConst::U8(..) => "u8", + ComponentConst::S8(..) => "s8", + ComponentConst::U16(..) => "u16", + ComponentConst::S16(..) => "s16", + ComponentConst::U32(..) => "u32", + ComponentConst::S32(..) => "s32", + ComponentConst::U64(..) => "u64", + ComponentConst::S64(..) => "s64", + ComponentConst::F32(..) => "f32", + ComponentConst::F64(..) => "f64", + ComponentConst::Char(..) => "char", + ComponentConst::String(..) => "string", + ComponentConst::List(..) => "list", + ComponentConst::Record(..) => "record", + ComponentConst::Tuple(..) => "tuple", + ComponentConst::Enum(..) => "enum", + ComponentConst::Variant { .. } => "variant", + ComponentConst::Option(..) => "option", + ComponentConst::Result(..) => "result", + ComponentConst::Flags(..) => "flags", }; let actual = match actual { Val::Bool(..) => "bool", diff --git a/crates/wast/src/core.rs b/crates/wast/src/core.rs index fd4761bda2..c8edec98c3 100644 --- a/crates/wast/src/core.rs +++ b/crates/wast/src/core.rs @@ -1,50 +1,49 @@ use crate::WastContext; use anyhow::{Context, Result, anyhow, bail}; +use json_from_wast::{CoreConst, FloatConst, V128}; use std::fmt::{Display, LowerHex}; -use wasmtime::{AnyRef, ExternRef, Store, Val}; -use wast::core::{AbstractHeapType, HeapType, NanPattern, V128Pattern, WastArgCore, WastRetCore}; -use wast::token::{F32, F64}; +use wasmtime::{Store, Val}; /// Translate from a `script::Value` to a `RuntimeValue`. -pub fn val(ctx: &mut WastContext, v: &WastArgCore<'_>) -> Result { - use wast::core::WastArgCore::*; +pub fn val(ctx: &mut WastContext, v: &CoreConst) -> Result { + use CoreConst::*; Ok(match v { - I32(x) => Val::I32(*x), - I64(x) => Val::I64(*x), - F32(x) => Val::F32(x.bits), - F64(x) => Val::F64(x.bits), - V128(x) => Val::V128(u128::from_le_bytes(x.to_le_bytes()).into()), - RefNull(HeapType::Abstract { - ty: AbstractHeapType::Extern, - shared: false, - }) => Val::ExternRef(None), - RefNull(HeapType::Abstract { - ty: AbstractHeapType::Func, - shared: false, - }) => Val::FuncRef(None), - RefNull(HeapType::Abstract { - ty: AbstractHeapType::Any, - shared: false, - }) => Val::AnyRef(None), - RefNull(HeapType::Abstract { - shared: false, - ty: AbstractHeapType::None, - }) => Val::AnyRef(None), - RefExtern(x) => Val::ExternRef(if let Some(rt) = ctx.async_runtime.as_ref() { - Some(rt.block_on(ExternRef::new_async(&mut ctx.store, *x))?) + I32 { value } => Val::I32(value.0), + I64 { value } => Val::I64(value.0), + F32 { value } => Val::F32(value.to_bits()), + F64 { value } => Val::F64(value.to_bits()), + V128(value) => Val::V128(value.to_u128().into()), + FuncRef { + value: None | Some(json_from_wast::FuncRef::Null), + } => Val::FuncRef(None), + + ExternRef { + value: None | Some(json_from_wast::ExternRef::Null), + } => Val::ExternRef(None), + ExternRef { + value: Some(json_from_wast::ExternRef::Host(x)), + } => Val::ExternRef(if let Some(rt) = ctx.async_runtime.as_ref() { + Some(rt.block_on(wasmtime::ExternRef::new_async(&mut ctx.store, x.0))?) } else { - Some(ExternRef::new(&mut ctx.store, *x)?) + Some(wasmtime::ExternRef::new(&mut ctx.store, x.0)?) }), - RefHost(x) => { + + AnyRef { + value: None | Some(json_from_wast::AnyRef::Null), + } => Val::AnyRef(None), + AnyRef { + value: Some(json_from_wast::AnyRef::Host(x)), + } => { let x = if let Some(rt) = ctx.async_runtime.as_ref() { - rt.block_on(ExternRef::new_async(&mut ctx.store, *x))? + rt.block_on(wasmtime::ExternRef::new_async(&mut ctx.store, x.0))? } else { - ExternRef::new(&mut ctx.store, *x)? + wasmtime::ExternRef::new(&mut ctx.store, x.0)? }; - let x = AnyRef::convert_extern(&mut ctx.store, x)?; + let x = wasmtime::AnyRef::convert_extern(&mut ctx.store, x)?; Val::AnyRef(Some(x)) } + NullRef => Val::AnyRef(None), other => bail!("couldn't convert {:?} to a runtime value", other), }) } @@ -65,62 +64,68 @@ fn extract_lane_as_i64(bytes: u128, lane: usize) -> i64 { (bytes >> (lane * 64)) as i64 } -pub fn match_val(store: &mut Store, actual: &Val, expected: &WastRetCore) -> Result<()> { +pub fn match_val(store: &mut Store, actual: &Val, expected: &CoreConst) -> Result<()> { match (actual, expected) { - (_, WastRetCore::Either(expected)) => { - for expected in expected { + (_, CoreConst::Either { values }) => { + for expected in values { if match_val(store, actual, expected).is_ok() { return Ok(()); } } - match_val(store, actual, &expected[0]) + match_val(store, actual, &values[0]) } - (Val::I32(a), WastRetCore::I32(b)) => match_int(a, b), - (Val::I64(a), WastRetCore::I64(b)) => match_int(a, b), + (Val::I32(a), CoreConst::I32 { value }) => match_int(a, &value.0), + (Val::I64(a), CoreConst::I64 { value }) => match_int(a, &value.0), // Note that these float comparisons are comparing bits, not float // values, so we're testing for bit-for-bit equivalence - (Val::F32(a), WastRetCore::F32(b)) => match_f32(*a, b), - (Val::F64(a), WastRetCore::F64(b)) => match_f64(*a, b), - (Val::V128(a), WastRetCore::V128(b)) => match_v128(a.as_u128(), b), + (Val::F32(a), CoreConst::F32 { value }) => match_f32(*a, value), + (Val::F64(a), CoreConst::F64 { value }) => match_f64(*a, value), + (Val::V128(a), CoreConst::V128(value)) => match_v128(a.as_u128(), value), - // Null references. - ( - Val::FuncRef(None) | Val::ExternRef(None) | Val::AnyRef(None), - WastRetCore::RefNull(_), + // Null references, or blanket "any reference" assertions + (Val::FuncRef(None) | Val::ExternRef(None) | Val::AnyRef(None), CoreConst::RefNull) + | (Val::FuncRef(_), CoreConst::FuncRef { value: None }) + | (Val::AnyRef(_), CoreConst::AnyRef { value: None }) + | (Val::ExternRef(_), CoreConst::ExternRef { value: None }) + | (Val::AnyRef(None), CoreConst::NullRef) + | (Val::FuncRef(None), CoreConst::NullFuncRef) + | (Val::ExternRef(None), CoreConst::NullExternRef) + | ( + Val::FuncRef(None), + CoreConst::FuncRef { + value: Some(json_from_wast::FuncRef::Null), + }, + ) + | ( + Val::AnyRef(None), + CoreConst::AnyRef { + value: Some(json_from_wast::AnyRef::Null), + }, ) - | (Val::ExternRef(None), WastRetCore::RefExtern(None)) => Ok(()), + | ( + Val::ExternRef(None), + CoreConst::ExternRef { + value: Some(json_from_wast::ExternRef::Null), + }, + ) => Ok(()), + + // Ideally we'd compare the actual index, but Wasmtime doesn't expose + // the raw index a function in the embedder API. + ( + Val::FuncRef(Some(_)), + CoreConst::FuncRef { + value: Some(json_from_wast::FuncRef::Index(_)), + }, + ) => Ok(()), - // Null and non-null mismatches. - (Val::ExternRef(None), WastRetCore::RefExtern(Some(_))) => { - bail!("expected non-null reference, found null") - } ( Val::ExternRef(Some(x)), - WastRetCore::RefNull(Some(HeapType::Abstract { - ty: AbstractHeapType::Extern, - shared: false, - })), + CoreConst::ExternRef { + value: Some(json_from_wast::ExternRef::Host(y)), + }, ) => { - match x.data(store)?.map(|x| { - x.downcast_ref::() - .expect("only u32 externrefs created in wast test suites") - }) { - None => { - bail!("expected null externref, found non-null externref without host data") - } - Some(x) => bail!("expected null externref, found non-null externref of {x}"), - } - } - (Val::ExternRef(Some(_)) | Val::FuncRef(Some(_)), WastRetCore::RefNull(_)) => { - bail!("expected null, found non-null reference: {actual:?}") - } - - // Non-null references. - (Val::FuncRef(Some(_)), WastRetCore::RefFunc(_)) => Ok(()), - (Val::ExternRef(Some(_)), WastRetCore::RefExtern(None)) => Ok(()), - (Val::ExternRef(Some(x)), WastRetCore::RefExtern(Some(y))) => { let x = x .data(store)? .ok_or_else(|| { @@ -128,44 +133,48 @@ pub fn match_val(store: &mut Store, actual: &Val, expected: &WastRetCore) })? .downcast_ref::() .expect("only u32 externrefs created in wast test suites"); - if x == y { + if *x == y.0 { Ok(()) } else { - bail!("expected {} found {}", y, x); + bail!("expected {} found {x}", y.0); } } - (Val::AnyRef(Some(_)), WastRetCore::RefAny) => Ok(()), - (Val::AnyRef(Some(x)), WastRetCore::RefEq) => { + (Val::AnyRef(Some(x)), CoreConst::EqRef) => { if x.is_eqref(store)? { Ok(()) } else { bail!("expected an eqref, found {x:?}"); } } - (Val::AnyRef(Some(x)), WastRetCore::RefI31) => { + (Val::AnyRef(Some(x)), CoreConst::I31Ref) => { if x.is_i31(store)? { Ok(()) } else { bail!("expected a `(ref i31)`, found {x:?}"); } } - (Val::AnyRef(Some(x)), WastRetCore::RefStruct) => { + (Val::AnyRef(Some(x)), CoreConst::StructRef) => { if x.is_struct(store)? { Ok(()) } else { bail!("expected a struct reference, found {x:?}") } } - (Val::AnyRef(Some(x)), WastRetCore::RefArray) => { + (Val::AnyRef(Some(x)), CoreConst::ArrayRef) => { if x.is_array(store)? { Ok(()) } else { bail!("expected a array reference, found {x:?}") } } - (Val::AnyRef(Some(x)), WastRetCore::RefHost(y)) => { - let x = ExternRef::convert_any(&mut *store, *x)?; + ( + Val::AnyRef(Some(x)), + CoreConst::AnyRef { + value: Some(json_from_wast::AnyRef::Host(y)), + }, + ) => { + let x = wasmtime::ExternRef::convert_any(&mut *store, *x)?; let x = x .data(&mut *store)? .ok_or_else(|| { @@ -176,18 +185,17 @@ pub fn match_val(store: &mut Store, actual: &Val, expected: &WastRetCore) })? .downcast_ref::() .expect("only u32 externrefs created in wast test suites"); - if x == y { + if *x == y.0 { Ok(()) } else { - bail!("expected anyref of externref of {y}, found anyref of externref of {x}") + bail!( + "expected anyref of externref of {}, found anyref of externref of {x}", + y.0 + ) } } - _ => bail!( - "don't know how to compare {:?} and {:?} yet", - actual, - expected - ), + _ => bail!("expected {expected:?} got {actual:?}"), } } @@ -207,7 +215,7 @@ where } } -pub fn match_f32(actual: u32, expected: &NanPattern) -> Result<()> { +pub fn match_f32(actual: u32, expected: &FloatConst) -> Result<()> { match expected { // Check if an f32 (as u32 bits to avoid possible quieting when moving values in registers, e.g. // https://developer.arm.com/documentation/ddi0344/i/neon-and-vfp-programmers-model/modes-of-operation/default-nan-mode?lang=en) @@ -216,7 +224,7 @@ pub fn match_f32(actual: u32, expected: &NanPattern) -> Result<()> { // - the 8-bit exponent is set to all 1s // - the MSB of the payload is set to 1 (a quieted NaN) and all others to 0. // See https://webassembly.github.io/spec/core/syntax/values.html#floating-point. - NanPattern::CanonicalNan => { + FloatConst::CanonicalNan => { let canon_nan = 0x7fc0_0000; if (actual & 0x7fff_ffff) == canon_nan { Ok(()) @@ -237,7 +245,7 @@ pub fn match_f32(actual: u32, expected: &NanPattern) -> Result<()> { // set to 1, but one or more of the remaining payload bits MAY BE set to // 1 (a canonical NaN specifies all 0s). See // https://webassembly.github.io/spec/core/syntax/values.html#floating-point. - NanPattern::ArithmeticNan => { + FloatConst::ArithmeticNan => { const AF32_NAN: u32 = 0x7f80_0000; let is_nan = actual & AF32_NAN == AF32_NAN; const AF32_PAYLOAD_MSB: u32 = 0x0040_0000; @@ -255,15 +263,15 @@ pub fn match_f32(actual: u32, expected: &NanPattern) -> Result<()> { ) } } - NanPattern::Value(expected_value) => { - if actual == expected_value.bits { + FloatConst::Value(expected_value) => { + if actual == expected_value.to_bits() { Ok(()) } else { bail!( "expected {:10} / {:#010x}\n\ actual {:10} / {:#010x}", - f32::from_bits(expected_value.bits), - expected_value.bits, + expected_value, + expected_value.to_bits(), f32::from_bits(actual), actual, ) @@ -272,7 +280,7 @@ pub fn match_f32(actual: u32, expected: &NanPattern) -> Result<()> { } } -pub fn match_f64(actual: u64, expected: &NanPattern) -> Result<()> { +pub fn match_f64(actual: u64, expected: &FloatConst) -> Result<()> { match expected { // Check if an f64 (as u64 bits to avoid possible quieting when moving values in registers, e.g. // https://developer.arm.com/documentation/ddi0344/i/neon-and-vfp-programmers-model/modes-of-operation/default-nan-mode?lang=en) @@ -281,7 +289,7 @@ pub fn match_f64(actual: u64, expected: &NanPattern) -> Result<()> { // - the 11-bit exponent is set to all 1s // - the MSB of the payload is set to 1 (a quieted NaN) and all others to 0. // See https://webassembly.github.io/spec/core/syntax/values.html#floating-point. - NanPattern::CanonicalNan => { + FloatConst::CanonicalNan => { let canon_nan = 0x7ff8_0000_0000_0000; if (actual & 0x7fff_ffff_ffff_ffff) == canon_nan { Ok(()) @@ -301,7 +309,7 @@ pub fn match_f64(actual: u64, expected: &NanPattern) -> Result<()> { // canonical NaN including that the payload MSB is set to 1, but one or more of the remaining // payload bits MAY BE set to 1 (a canonical NaN specifies all 0s). See // https://webassembly.github.io/spec/core/syntax/values.html#floating-point. - NanPattern::ArithmeticNan => { + FloatConst::ArithmeticNan => { const AF64_NAN: u64 = 0x7ff0_0000_0000_0000; let is_nan = actual & AF64_NAN == AF64_NAN; const AF64_PAYLOAD_MSB: u64 = 0x0008_0000_0000_0000; @@ -319,15 +327,15 @@ pub fn match_f64(actual: u64, expected: &NanPattern) -> Result<()> { ) } } - NanPattern::Value(expected_value) => { - if actual == expected_value.bits { + FloatConst::Value(expected_value) => { + if actual == expected_value.to_bits() { Ok(()) } else { bail!( "expected {:18} / {:#018x}\n\ actual {:18} / {:#018x}", - f64::from_bits(expected_value.bits), - expected_value.bits, + expected_value, + expected_value.to_bits(), f64::from_bits(actual), actual, ) @@ -336,9 +344,9 @@ pub fn match_f64(actual: u64, expected: &NanPattern) -> Result<()> { } } -fn match_v128(actual: u128, expected: &V128Pattern) -> Result<()> { +fn match_v128(actual: u128, expected: &V128) -> Result<()> { match expected { - V128Pattern::I8x16(expected) => { + V128::I8 { value } => { let actual = [ extract_lane_as_i8(actual, 0), extract_lane_as_i8(actual, 1), @@ -357,7 +365,7 @@ fn match_v128(actual: u128, expected: &V128Pattern) -> Result<()> { extract_lane_as_i8(actual, 14), extract_lane_as_i8(actual, 15), ]; - if actual == *expected { + if actual == value.map(|i| i.0) { return Ok(()); } bail!( @@ -370,7 +378,7 @@ fn match_v128(actual: u128, expected: &V128Pattern) -> Result<()> { actual, ) } - V128Pattern::I16x8(expected) => { + V128::I16 { value } => { let actual = [ extract_lane_as_i16(actual, 0), extract_lane_as_i16(actual, 1), @@ -381,7 +389,7 @@ fn match_v128(actual: u128, expected: &V128Pattern) -> Result<()> { extract_lane_as_i16(actual, 6), extract_lane_as_i16(actual, 7), ]; - if actual == *expected { + if actual == value.map(|i| i.0) { return Ok(()); } bail!( @@ -394,14 +402,14 @@ fn match_v128(actual: u128, expected: &V128Pattern) -> Result<()> { actual, ) } - V128Pattern::I32x4(expected) => { + V128::I32 { value } => { let actual = [ extract_lane_as_i32(actual, 0), extract_lane_as_i32(actual, 1), extract_lane_as_i32(actual, 2), extract_lane_as_i32(actual, 3), ]; - if actual == *expected { + if actual == value.map(|i| i.0) { return Ok(()); } bail!( @@ -414,12 +422,12 @@ fn match_v128(actual: u128, expected: &V128Pattern) -> Result<()> { actual, ) } - V128Pattern::I64x2(expected) => { + V128::I64 { value } => { let actual = [ extract_lane_as_i64(actual, 0), extract_lane_as_i64(actual, 1), ]; - if actual == *expected { + if actual == value.map(|i| i.0) { return Ok(()); } bail!( @@ -432,15 +440,15 @@ fn match_v128(actual: u128, expected: &V128Pattern) -> Result<()> { actual, ) } - V128Pattern::F32x4(expected) => { - for (i, expected) in expected.iter().enumerate() { + V128::F32 { value } => { + for (i, expected) in value.iter().enumerate() { let a = extract_lane_as_i32(actual, i) as u32; match_f32(a, expected).with_context(|| format!("difference in lane {i}"))?; } Ok(()) } - V128Pattern::F64x2(expected) => { - for (i, expected) in expected.iter().enumerate() { + V128::F64 { value } => { + for (i, expected) in value.iter().enumerate() { let a = extract_lane_as_i64(actual, i) as u64; match_f64(a, expected).with_context(|| format!("difference in lane {i}"))?; } diff --git a/crates/wast/src/spectest.rs b/crates/wast/src/spectest.rs index 38ca1bc733..f38356ac84 100644 --- a/crates/wast/src/spectest.rs +++ b/crates/wast/src/spectest.rs @@ -105,16 +105,18 @@ pub fn link_component_spectest(linker: &mut component::Linker) -> Result<( i.instance("nested")? .func_wrap("return-four", |_, _: ()| Ok((4u32,)))?; - let module = Module::new( - &engine, - r#" - (module - (global (export "g") i32 i32.const 100) - (func (export "f") (result i32) i32.const 101) - ) - "#, - )?; - i.module("simple-module", &module)?; + if !cfg!(miri) { + let module = Module::new( + &engine, + r#" + (module + (global (export "g") i32 i32.const 100) + (func (export "f") (result i32) i32.const 101) + ) + "#, + )?; + i.module("simple-module", &module)?; + } struct Resource1; struct Resource2; diff --git a/crates/wast/src/wast.rs b/crates/wast/src/wast.rs index 82bdb6193b..35eee9ea99 100644 --- a/crates/wast/src/wast.rs +++ b/crates/wast/src/wast.rs @@ -3,15 +3,15 @@ use crate::component; use crate::core; use crate::spectest::*; use anyhow::{Context as _, anyhow, bail}; +use json_from_wast::{Action, Command, Const, WasmFile, WasmFileType}; use std::collections::HashMap; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::str; +use std::sync::Arc; use std::thread; use wasmtime::*; -use wast::core::{EncodeOptions, GenerateDwarf}; use wast::lexer::Lexer; use wast::parser::{self, ParseBuffer}; -use wast::{QuoteWat, Wast, WastArg, WastDirective, WastExecute, WastInvoke, WastRet, Wat}; /// The wast test script language allows modules to be defined and actions /// to be performed on them. @@ -26,6 +26,10 @@ pub struct WastContext { pub(crate) store: Store, pub(crate) async_runtime: Option, generate_dwarf: bool, + precompile_save: Option, + precompile_load: Option, + + modules_by_filename: Arc>>, } enum Outcome { @@ -121,9 +125,26 @@ where None }, generate_dwarf: true, + precompile_save: None, + precompile_load: None, + modules_by_filename: Arc::default(), } } + /// Saves precompiled modules/components into `path` instead of executing + /// test directives. + pub fn precompile_save(&mut self, path: impl AsRef) -> &mut Self { + self.precompile_save = Some(path.as_ref().into()); + self + } + + /// Loads precompiled modules/components from `path` instead of compiling + /// natively. + pub fn precompile_load(&mut self, path: impl AsRef) -> &mut Self { + self.precompile_load = Some(path.as_ref().into()); + self + } + fn get_export(&mut self, module: Option<&str>, name: &str) -> Result { if let Some(module) = module { return Ok(Export::Core( @@ -190,88 +211,71 @@ where } /// Perform the action portion of a command. - fn perform_execute( - &mut self, - exec: WastExecute<'_>, - filename: &str, - wast: &str, - ) -> Result { - match exec { - WastExecute::Invoke(invoke) => self.perform_invoke(invoke), - WastExecute::Wat(module) => Ok( - match self.module_definition(QuoteWat::Wat(module), filename, wast)? { - (_, ModuleKind::Core(module)) => self - .instantiate_module(&module)? - .map(|_| Results::Core(Vec::new())), - #[cfg(feature = "component-model")] - (_, ModuleKind::Component(component)) => self - .instantiate_component(&component)? - .map(|_| Results::Component(Vec::new())), - }, - ), - WastExecute::Get { module, global, .. } => self.get(module.map(|s| s.name()), global), - } - } + fn perform_action(&mut self, action: &Action<'_>) -> Result { + match action { + Action::Invoke { + module, + field, + args, + } => match self.get_export(module.as_deref(), field)? { + Export::Core(export) => { + let func = export + .into_func() + .ok_or_else(|| anyhow!("no function named `{field}`"))?; + let values = args + .iter() + .map(|v| match v { + Const::Core(v) => core::val(self, v), + _ => bail!("expected core function, found other other argument {v:?}"), + }) + .collect::>>()?; + + let mut results = + vec![Val::null_func_ref(); func.ty(&self.store).results().len()]; + let result = match &self.async_runtime { + Some(rt) => { + rt.block_on(func.call_async(&mut self.store, &values, &mut results)) + } + None => func.call(&mut self.store, &values, &mut results), + }; - fn perform_invoke(&mut self, exec: WastInvoke<'_>) -> Result { - match self.get_export(exec.module.map(|i| i.name()), exec.name)? { - Export::Core(export) => { - let func = export - .into_func() - .ok_or_else(|| anyhow!("no function named `{}`", exec.name))?; - let values = exec - .args - .iter() - .map(|v| match v { - WastArg::Core(v) => core::val(self, v), - _ => bail!("expected core function, found other other argument {v:?}"), + Ok(match result { + Ok(()) => Outcome::Ok(Results::Core(results)), + Err(e) => Outcome::Trap(e), }) - .collect::>>()?; - - let mut results = vec![Val::null_func_ref(); func.ty(&self.store).results().len()]; - let result = match &self.async_runtime { - Some(rt) => { - rt.block_on(func.call_async(&mut self.store, &values, &mut results)) - } - None => func.call(&mut self.store, &values, &mut results), - }; - - Ok(match result { - Ok(()) => Outcome::Ok(Results::Core(results)), - Err(e) => Outcome::Trap(e), - }) - } - #[cfg(feature = "component-model")] - Export::Component(func) => { - let values = exec - .args - .iter() - .map(|v| match v { - WastArg::Component(v) => component::val(v), - _ => bail!("expected component function, found other argument {v:?}"), - }) - .collect::>>()?; - - let mut results = - vec![component::Val::Bool(false); func.results(&self.store).len()]; - let result = match &self.async_runtime { - Some(rt) => { - rt.block_on(func.call_async(&mut self.store, &values, &mut results)) - } - None => func.call(&mut self.store, &values, &mut results), - }; - Ok(match result { - Ok(()) => { - match &self.async_runtime { - Some(rt) => rt.block_on(func.post_return_async(&mut self.store))?, - None => func.post_return(&mut self.store)?, + } + #[cfg(feature = "component-model")] + Export::Component(func) => { + let values = args + .iter() + .map(|v| match v { + Const::Component(v) => component::val(v), + _ => bail!("expected component function, found other argument {v:?}"), + }) + .collect::>>()?; + + let mut results = + vec![component::Val::Bool(false); func.results(&self.store).len()]; + let result = match &self.async_runtime { + Some(rt) => { + rt.block_on(func.call_async(&mut self.store, &values, &mut results)) } + None => func.call(&mut self.store, &values, &mut results), + }; + Ok(match result { + Ok(()) => { + match &self.async_runtime { + Some(rt) => rt.block_on(func.post_return_async(&mut self.store))?, + None => func.post_return(&mut self.store)?, + } - Outcome::Ok(Results::Component(results)) - } - Err(e) => Outcome::Trap(e), - }) - } + Outcome::Ok(Results::Component(results)) + } + Err(e) => Outcome::Trap(e), + }) + } + }, + Action::Get { module, field, .. } => self.get(module.as_deref(), field), } } @@ -330,39 +334,56 @@ where /// it, if any. /// /// This will not register the name within `self.modules`. - fn module_definition<'a>( - &mut self, - mut wat: QuoteWat<'a>, - filename: &str, - wast: &str, - ) -> Result<(Option<&'a str>, ModuleKind)> { - let (is_module, name) = match &wat { - QuoteWat::Wat(Wat::Module(m)) => (true, m.id), - QuoteWat::QuoteModule(..) => (true, None), - QuoteWat::Wat(Wat::Component(m)) => (false, m.id), - QuoteWat::QuoteComponent(..) => (false, None), + fn module_definition(&mut self, file: &WasmFile) -> Result { + let name = match file.module_type { + WasmFileType::Text => file + .binary_filename + .as_ref() + .ok_or_else(|| anyhow!("cannot compile module that isn't a valid binary"))?, + WasmFileType::Binary => &file.filename, }; - let bytes = match &mut wat { - QuoteWat::Wat(wat) => { - let mut opts = EncodeOptions::new(); - if self.generate_dwarf { - opts.dwarf(filename.as_ref(), wast, GenerateDwarf::Lines); + + match &self.precompile_load { + Some(path) => { + let cwasm = path.join(&name[..]).with_extension("cwasm"); + match Engine::detect_precompiled_file(&cwasm) + .with_context(|| format!("failed to read {cwasm:?}"))? + { + Some(Precompiled::Module) => { + let module = + unsafe { Module::deserialize_file(self.store.engine(), &cwasm)? }; + Ok(ModuleKind::Core(module)) + } + #[cfg(feature = "component-model")] + Some(Precompiled::Component) => { + let component = unsafe { + component::Component::deserialize_file(self.store.engine(), &cwasm)? + }; + Ok(ModuleKind::Component(component)) + } + #[cfg(not(feature = "component-model"))] + Some(Precompiled::Component) => { + bail!("support for components disabled at compile time") + } + None => bail!("expected a cwasm file"), } - opts.encode_wat(wat)? } - _ => wat.encode()?, - }; - if is_module { - let module = Module::new(self.store.engine(), &bytes)?; - Ok((name.map(|n| n.name()), ModuleKind::Core(module))) - } else { - #[cfg(feature = "component-model")] - { - let component = component::Component::new(self.store.engine(), &bytes)?; - Ok((name.map(|n| n.name()), ModuleKind::Component(component))) + None => { + let bytes = &self.modules_by_filename[&name[..]]; + + if wasmparser::Parser::is_core_wasm(&bytes) { + let module = Module::new(self.store.engine(), &bytes)?; + Ok(ModuleKind::Core(module)) + } else { + #[cfg(feature = "component-model")] + { + let component = component::Component::new(self.store.engine(), &bytes)?; + Ok(ModuleKind::Component(component)) + } + #[cfg(not(feature = "component-model"))] + bail!("component-model support not enabled"); + } } - #[cfg(not(feature = "component-model"))] - bail!("component-model support not enabled"); } } @@ -404,7 +425,7 @@ where ]))) } - fn assert_return(&mut self, result: Outcome, results: &[WastRet<'_>]) -> Result<()> { + fn assert_return(&mut self, result: Outcome, results: &[Const]) -> Result<()> { match result.into_result()? { Results::Core(values) => { if values.len() != results.len() { @@ -412,7 +433,7 @@ where } for (i, (v, e)) in values.iter().zip(results).enumerate() { let e = match e { - WastRet::Core(core) => core, + Const::Core(core) => core, _ => bail!("expected core value found other value {e:?}"), }; core::match_val(&mut self.store, v, e) @@ -426,7 +447,7 @@ where } for (i, (v, e)) in values.iter().zip(results).enumerate() { let e = match e { - WastRet::Component(val) => val, + Const::Component(val) => val, _ => bail!("expected component value found other value {e:?}"), }; component::match_val(e, v) @@ -459,7 +480,7 @@ where } /// Run a wast script from a byte buffer. - pub fn run_buffer(&mut self, filename: &str, wast: &[u8]) -> Result<()> { + pub fn run_wast(&mut self, filename: &str, wast: &[u8]) -> Result<()> { let wast = str::from_utf8(wast)?; let adjust_wast = |mut err: wast::Error| { @@ -472,40 +493,56 @@ where lexer.allow_confusing_unicode(filename.ends_with("names.wast")); let mut buf = ParseBuffer::new_with_lexer(lexer).map_err(adjust_wast)?; buf.track_instr_spans(self.generate_dwarf); - let ast = parser::parse::(&buf).map_err(adjust_wast)?; + let ast = parser::parse::(&buf).map_err(adjust_wast)?; + + let mut ast = json_from_wast::Opts::default() + .dwarf(self.generate_dwarf) + .convert(filename, wast, ast)?; + let modules_by_filename = Arc::get_mut(&mut self.modules_by_filename).unwrap(); + for (name, bytes) in ast.wasms.drain(..) { + let prev = modules_by_filename.insert(name, bytes); + assert!(prev.is_none()); + } - self.run_directives(ast.directives, filename, wast) + match &self.precompile_save { + Some(path) => { + let json_path = path + .join(Path::new(filename).file_name().unwrap()) + .with_extension("json"); + let json = serde_json::to_string(&ast)?; + std::fs::write(&json_path, json) + .with_context(|| format!("failed to write {json_path:?}"))?; + for (name, bytes) in self.modules_by_filename.iter() { + let cwasm_path = path.join(name).with_extension("cwasm"); + let cwasm = if wasmparser::Parser::is_core_wasm(&bytes) { + self.store.engine().precompile_module(bytes) + } else { + #[cfg(feature = "component-model")] + { + self.store.engine().precompile_component(bytes) + } + #[cfg(not(feature = "component-model"))] + bail!("component-model support not enabled"); + }; + if let Ok(cwasm) = cwasm { + std::fs::write(&cwasm_path, cwasm) + .with_context(|| format!("failed to write {cwasm_path:?}"))?; + } + } + Ok(()) + } + None => self.run_directives(ast.commands, filename), + } } - fn run_directives( - &mut self, - directives: Vec>, - filename: &str, - wast: &str, - ) -> Result<()> { - let adjust_wast = |mut err: wast::Error| { - err.set_path(filename.as_ref()); - err.set_text(wast); - err - }; - + fn run_directives(&mut self, directives: Vec>, filename: &str) -> Result<()> { thread::scope(|scope| { let mut threads = HashMap::new(); for directive in directives { - let sp = directive.span(); - if log::log_enabled!(log::Level::Debug) { - let (line, col) = sp.linecol_in(wast); - log::debug!("running directive on {}:{}:{}", filename, line + 1, col); - } - self.run_directive(directive, filename, wast, &scope, &mut threads) - .map_err(|e| match e.downcast() { - Ok(err) => adjust_wast(err).into(), - Err(e) => e, - }) - .with_context(|| { - let (line, col) = sp.linecol_in(wast); - format!("failed directive on {}:{}:{}", filename, line + 1, col) - })?; + let line = directive.line(); + log::debug!("running directive on {filename}:{line}"); + self.run_directive(directive, filename, &scope, &mut threads) + .with_context(|| format!("failed directive on {filename}:{line}"))?; } Ok(()) }) @@ -513,129 +550,146 @@ where fn run_directive<'a>( &mut self, - directive: WastDirective<'a>, + directive: Command<'a>, filename: &'a str, - wast: &'a str, + // wast: &'a str, scope: &'a thread::Scope<'a, '_>, - threads: &mut HashMap<&'a str, thread::ScopedJoinHandle<'a, Result<()>>>, + threads: &mut HashMap>>, ) -> Result<()> where T: 'a, { - use wast::WastDirective::*; + use Command::*; match directive { - Module(module) => { - let (name, module) = self.module_definition(module, filename, wast)?; - self.module(name, &module)?; + Module { + name, + file, + line: _, + } => { + let module = self.module_definition(&file)?; + self.module(name.as_deref(), &module)?; } - ModuleDefinition(module) => { - let (name, module) = self.module_definition(module, filename, wast)?; + ModuleDefinition { + name, + file, + line: _, + } => { + let module = self.module_definition(&file)?; if let Some(name) = name { - self.modules.insert(name.to_string(), module.clone()); + self.modules.insert(name.to_string(), module); } } ModuleInstance { instance, module, - span: _, + line: _, } => { let module = module - .and_then(|n| self.modules.get(n.name())) + .as_deref() + .and_then(|n| self.modules.get(n)) .cloned() .ok_or_else(|| anyhow!("no module named {module:?}"))?; - self.module(instance.map(|n| n.name()), &module)?; + self.module(instance.as_deref(), &module)?; } - Register { - span: _, - name, - module, - } => { - self.register(module.map(|s| s.name()), name)?; + Register { line: _, name, as_ } => { + self.register(name.as_deref(), &as_)?; } - Invoke(i) => { - self.perform_invoke(i)?; + Action { action, line: _ } => { + self.perform_action(&action)?; } AssertReturn { - span: _, - exec, - results, + action, + expected, + line: _, } => { - let result = self.perform_execute(exec, filename, wast)?; - self.assert_return(result, &results)?; + let result = self.perform_action(&action)?; + self.assert_return(result, &expected)?; } AssertTrap { - span: _, - exec, - message, + action, + text, + line: _, } => { - let result = self.perform_execute(exec, filename, wast)?; - self.assert_trap(result, message)?; + let result = self.perform_action(&action)?; + self.assert_trap(result, &text)?; + } + AssertUninstantiable { + file, + text, + line: _, + } => { + let result = match self.module_definition(&file)? { + ModuleKind::Core(module) => self + .instantiate_module(&module)? + .map(|_| Results::Core(Vec::new())), + #[cfg(feature = "component-model")] + ModuleKind::Component(component) => self + .instantiate_component(&component)? + .map(|_| Results::Component(Vec::new())), + }; + self.assert_trap(result, &text)?; } AssertExhaustion { - span: _, - call, - message, + action, + text, + line: _, } => { - let result = self.perform_invoke(call)?; - self.assert_trap(result, message)?; + let result = self.perform_action(&action)?; + self.assert_trap(result, &text)?; } AssertInvalid { - span: _, - module, - message, + file, + text, + line: _, } => { - let err = match self.module_definition(module, filename, wast) { + let err = match self.module_definition(&file) { Ok(_) => bail!("expected module to fail to build"), Err(e) => e, }; let error_message = format!("{err:?}"); - if !is_matching_assert_invalid_error_message(filename, &message, &error_message) { - bail!( - "assert_invalid: expected \"{}\", got \"{}\"", - message, - error_message - ) + if !is_matching_assert_invalid_error_message(filename, &text, &error_message) { + bail!("assert_invalid: expected \"{text}\", got \"{error_message}\"",) } } AssertMalformed { - module, - span: _, - message: _, + file, + text: _, + line: _, } => { - if let Ok(_) = self.module_definition(module, filename, wast) { + if let Ok(_) = self.module_definition(&file) { bail!("expected malformed module to fail to instantiate"); } } AssertUnlinkable { - span: _, - module, - message, + file, + text, + line: _, } => { - let (name, module) = - self.module_definition(QuoteWat::Wat(module), filename, wast)?; - let err = match self.module(name, &module) { + let module = self.module_definition(&file)?; + let err = match self.module(None, &module) { Ok(_) => bail!("expected module to fail to link"), Err(e) => e, }; let error_message = format!("{err:?}"); - if !error_message.contains(&message) { - bail!( - "assert_unlinkable: expected {}, got {}", - message, - error_message - ) + if !error_message.contains(&text[..]) { + bail!("assert_unlinkable: expected {text}, got {error_message}",) } } AssertException { .. } => bail!("unimplemented assert_exception"), - Thread(thread) => { + Thread { + name, + shared_module, + commands, + line: _, + } => { let mut core_linker = Linker::new(self.store.engine()); - if let Some(id) = thread.shared_module { + if let Some(id) = shared_module { let items = self .core_linker .iter(&mut self.store) - .filter(|(module, _, _)| *module == id.name()) + .filter(|(module, _, _)| *module == &id[..]) .collect::>(); for (module, name, item) in items { core_linker.define(&mut self.store, module, name, item)?; @@ -654,18 +708,17 @@ where .unwrap() }), generate_dwarf: self.generate_dwarf, + modules_by_filename: self.modules_by_filename.clone(), + precompile_load: self.precompile_load.clone(), + precompile_save: self.precompile_save.clone(), }; - let name = thread.name.name(); - let child = - scope.spawn(move || child_cx.run_directives(thread.directives, filename, wast)); - threads.insert(name, child); + let child = scope.spawn(move || child_cx.run_directives(commands, filename)); + threads.insert(name.to_string(), child); } - Wait { thread, .. } => { - let name = thread.name(); threads - .remove(name) - .ok_or_else(|| anyhow!("no thread named `{name}`"))? + .remove(&thread[..]) + .ok_or_else(|| anyhow!("no thread named `{thread}`"))? .join() .unwrap()?; } @@ -680,9 +733,22 @@ where /// Run a wast script from a file. pub fn run_file(&mut self, path: &Path) -> Result<()> { - let bytes = - std::fs::read(path).with_context(|| format!("failed to read `{}`", path.display()))?; - self.run_buffer(path.to_str().unwrap(), &bytes) + match &self.precompile_load { + Some(precompile) => { + let file = precompile + .join(path.file_name().unwrap()) + .with_extension("json"); + let json = std::fs::read_to_string(&file) + .with_context(|| format!("failed to read {file:?}"))?; + let wast = serde_json::from_str::>(&json)?; + self.run_directives(wast.commands, &wast.source_filename) + } + None => { + let bytes = std::fs::read(path) + .with_context(|| format!("failed to read `{}`", path.display()))?; + self.run_wast(path.to_str().unwrap(), &bytes) + } + } } /// Whether or not to generate DWARF debugging information in custom diff --git a/crates/wiggle/src/error.rs b/crates/wiggle/src/error.rs index e8dd0f5a3b..590ee16e8c 100644 --- a/crates/wiggle/src/error.rs +++ b/crates/wiggle/src/error.rs @@ -13,10 +13,6 @@ pub enum GuestError { PtrOutOfBounds(Region), #[error("Pointer not aligned to {1}: {0:?}")] PtrNotAligned(Region, u32), - #[error("Pointer already borrowed: {0:?}")] - PtrBorrowed(Region), - #[error("Borrow checker out of handles")] - BorrowCheckerOutOfHandles, #[error("Slice length mismatch")] SliceLengthsDiffer, #[error("In func {modulename}::{funcname} at {location}: {err}")] diff --git a/crates/winch/src/compiler.rs b/crates/winch/src/compiler.rs index 9a4a2b1cbd..1b27f96695 100644 --- a/crates/winch/src/compiler.rs +++ b/crates/winch/src/compiler.rs @@ -25,7 +25,7 @@ struct CompilationContext { pub(crate) struct Compiler { isa: Box, - trampolines: Box, + trampolines: NoInlineCompiler, contexts: Mutex>, tunables: Tunables, } @@ -38,7 +38,7 @@ impl Compiler { ) -> Self { Self { isa, - trampolines, + trampolines: NoInlineCompiler(trampolines), contexts: Mutex::new(Vec::new()), tunables, } @@ -89,6 +89,10 @@ impl Compiler { } impl wasmtime_environ::Compiler for Compiler { + fn inlining_compiler(&self) -> Option<&dyn wasmtime_environ::InliningCompiler> { + None + } + fn compile_function( &self, translation: &ModuleTranslation<'_>, @@ -163,7 +167,7 @@ impl wasmtime_environ::Compiler for Compiler { fn append_code( &self, obj: &mut Object<'static>, - funcs: &[(String, Box)], + funcs: &[(String, Box)], resolve_reloc: &dyn Fn(usize, wasmtime_environ::RelocationTarget) -> usize, ) -> Result> { self.trampolines.append_code(obj, funcs, resolve_reloc) @@ -197,7 +201,7 @@ impl wasmtime_environ::Compiler for Compiler { _get_func: &'a dyn Fn( StaticModuleIndex, DefinedFuncIndex, - ) -> (SymbolId, &'a (dyn Any + Send)), + ) -> (SymbolId, &'a (dyn Any + Send + Sync)), _dwarf_package_bytes: Option<&'a [u8]>, _tunables: &'a Tunables, ) -> Result<()> { @@ -223,3 +227,153 @@ impl wasmtime_environ::Compiler for Compiler { self.trampolines.compiled_function_relocation_targets(func) } } + +/// A wrapper around another `Compiler` implementation that may or may not be an +/// inlining compiler and turns it into a non-inlining compiler. +struct NoInlineCompiler(Box); + +impl wasmtime_environ::Compiler for NoInlineCompiler { + fn inlining_compiler(&self) -> Option<&dyn wasmtime_environ::InliningCompiler> { + None + } + + fn compile_function( + &self, + translation: &ModuleTranslation<'_>, + index: DefinedFuncIndex, + data: FunctionBodyData<'_>, + types: &ModuleTypesBuilder, + symbol: &str, + ) -> Result { + let input = data.body.clone(); + let mut body = self + .0 + .compile_function(translation, index, data, types, symbol)?; + if let Some(c) = self.0.inlining_compiler() { + c.finish_compiling(&mut body, Some(input), symbol) + .map_err(|e| CompileError::Codegen(e.to_string()))?; + } + Ok(body) + } + + fn compile_array_to_wasm_trampoline( + &self, + translation: &ModuleTranslation<'_>, + types: &ModuleTypesBuilder, + index: DefinedFuncIndex, + symbol: &str, + ) -> Result { + let mut body = + self.0 + .compile_array_to_wasm_trampoline(translation, types, index, symbol)?; + if let Some(c) = self.0.inlining_compiler() { + c.finish_compiling(&mut body, None, symbol) + .map_err(|e| CompileError::Codegen(e.to_string()))?; + } + Ok(body) + } + + fn compile_wasm_to_array_trampoline( + &self, + wasm_func_ty: &wasmtime_environ::WasmFuncType, + symbol: &str, + ) -> Result { + let mut body = self + .0 + .compile_wasm_to_array_trampoline(wasm_func_ty, symbol)?; + if let Some(c) = self.0.inlining_compiler() { + c.finish_compiling(&mut body, None, symbol) + .map_err(|e| CompileError::Codegen(e.to_string()))?; + } + Ok(body) + } + + fn compile_wasm_to_builtin( + &self, + index: BuiltinFunctionIndex, + symbol: &str, + ) -> Result { + let mut body = self.0.compile_wasm_to_builtin(index, symbol)?; + if let Some(c) = self.0.inlining_compiler() { + c.finish_compiling(&mut body, None, symbol) + .map_err(|e| CompileError::Codegen(e.to_string()))?; + } + Ok(body) + } + + fn compiled_function_relocation_targets<'a>( + &'a self, + func: &'a dyn Any, + ) -> Box + 'a> { + self.0.compiled_function_relocation_targets(func) + } + + fn append_code( + &self, + obj: &mut Object<'static>, + funcs: &[(String, Box)], + resolve_reloc: &dyn Fn(usize, RelocationTarget) -> usize, + ) -> Result> { + self.0.append_code(obj, funcs, resolve_reloc) + } + + fn triple(&self) -> &target_lexicon::Triple { + self.0.triple() + } + + fn flags(&self) -> Vec<(&'static str, wasmtime_environ::FlagValue<'static>)> { + self.0.flags() + } + + fn isa_flags(&self) -> Vec<(&'static str, wasmtime_environ::FlagValue<'static>)> { + self.0.isa_flags() + } + + fn is_branch_protection_enabled(&self) -> bool { + self.0.is_branch_protection_enabled() + } + + #[cfg(feature = "component-model")] + fn component_compiler(&self) -> &dyn wasmtime_environ::component::ComponentCompiler { + self + } + + fn append_dwarf<'a>( + &self, + obj: &mut Object<'_>, + translations: &'a PrimaryMap>, + get_func: &'a dyn Fn( + StaticModuleIndex, + DefinedFuncIndex, + ) -> (SymbolId, &'a (dyn Any + Send + Sync)), + dwarf_package_bytes: Option<&'a [u8]>, + tunables: &'a Tunables, + ) -> Result<()> { + self.0 + .append_dwarf(obj, translations, get_func, dwarf_package_bytes, tunables) + } +} + +#[cfg(feature = "component-model")] +impl wasmtime_environ::component::ComponentCompiler for NoInlineCompiler { + fn compile_trampoline( + &self, + component: &wasmtime_environ::component::ComponentTranslation, + types: &wasmtime_environ::component::ComponentTypesBuilder, + trampoline: wasmtime_environ::component::TrampolineIndex, + tunables: &Tunables, + symbol: &str, + ) -> Result> { + let mut body = self + .0 + .component_compiler() + .compile_trampoline(component, types, trampoline, tunables, symbol)?; + if let Some(c) = self.0.inlining_compiler() { + c.finish_compiling(&mut body.array_call, None, symbol) + .map_err(|e| CompileError::Codegen(e.to_string()))?; + c.finish_compiling(&mut body.wasm_call, None, symbol) + .map_err(|e| CompileError::Codegen(e.to_string()))?; + } + Ok(body) + } +} diff --git a/crates/wit-bindgen/Cargo.toml b/crates/wit-bindgen/Cargo.toml index d4ce1413a6..a2ab791ea3 100644 --- a/crates/wit-bindgen/Cargo.toml +++ b/crates/wit-bindgen/Cargo.toml @@ -17,6 +17,8 @@ anyhow = { workspace = true } heck = { workspace = true } wit-parser = { workspace = true } indexmap = { workspace = true } +bitflags = { workspace = true } [features] +async = [] component-model-async = [] diff --git a/crates/wit-bindgen/src/config.rs b/crates/wit-bindgen/src/config.rs new file mode 100644 index 0000000000..d4aeaec867 --- /dev/null +++ b/crates/wit-bindgen/src/config.rs @@ -0,0 +1,163 @@ +use crate::{LookupItem, lookup_keys}; +use anyhow::Result; +use wit_parser::{Function, FunctionKind, Resolve, WorldKey}; + +bitflags::bitflags! { + #[derive(Default, Copy, Clone, Debug)] + pub struct FunctionFlags: u8 { + const ASYNC = 1 << 0; + const TRAPPABLE = 1 << 1; + const STORE = 1 << 2; + const TRACING = 1 << 3; + const VERBOSE_TRACING = 1 << 4; + const IGNORE_WIT = 1 << 5; + const EXACT = 1 << 6; + } +} + +#[derive(Default, Debug, Clone)] +pub struct FunctionConfig { + rules: Vec, + default: FunctionFlags, +} + +#[derive(Debug, Clone)] +struct FunctionRule { + filter: String, + flags: FunctionFlags, + used: bool, +} + +#[derive(Debug, Clone)] +pub enum FunctionFilter { + Name(String), + Default, +} + +impl FunctionConfig { + /// Creates a blank set of configuration. + pub fn new() -> FunctionConfig { + FunctionConfig::default() + } + + /// Adds a new rule to this configuration. + /// + /// Note that the order rules are added is significant as only the first + /// matching rule is used for a function. + pub fn push(&mut self, filter: FunctionFilter, flags: FunctionFlags) { + match filter { + FunctionFilter::Name(filter) => { + self.rules.push(FunctionRule { + filter, + flags, + used: false, + }); + } + FunctionFilter::Default => { + self.default = flags; + } + } + } + + /// Returns the set of configuration flags associated with `func`. + /// + /// The `name` provided should include the full name of the function + /// including its interface. The `kind` is the classification of the + /// function in WIT which affects the default set of flags. + pub(crate) fn flags( + &mut self, + resolve: &Resolve, + ns: Option<&WorldKey>, + func: &Function, + ) -> FunctionFlags { + let mut wit_flags = FunctionFlags::empty(); + + // If the kind is async, then set the async/store flags as that's a + // concurrent function which requires access to both. + match &func.kind { + FunctionKind::Freestanding + | FunctionKind::Method(_) + | FunctionKind::Static(_) + | FunctionKind::Constructor(_) => {} + + FunctionKind::AsyncFreestanding + | FunctionKind::AsyncMethod(_) + | FunctionKind::AsyncStatic(_) => { + wit_flags |= FunctionFlags::ASYNC | FunctionFlags::STORE; + } + } + + let mut ret = FunctionFlags::empty(); + self.add_function_flags(resolve, ns, &func.name, &mut ret); + if !ret.contains(FunctionFlags::IGNORE_WIT) { + ret |= wit_flags; + } + ret + } + + pub(crate) fn resource_drop_flags( + &mut self, + resolve: &Resolve, + ns: Option<&WorldKey>, + resource_name: &str, + ) -> FunctionFlags { + let mut ret = FunctionFlags::empty(); + self.add_function_flags(resolve, ns, &format!("[drop]{resource_name}"), &mut ret); + ret + } + + fn add_function_flags( + &mut self, + resolve: &Resolve, + key: Option<&WorldKey>, + name: &str, + base: &mut FunctionFlags, + ) { + let mut apply_rules = |name: &str, is_exact: bool| { + for rule in self.rules.iter_mut() { + if name != rule.filter { + continue; + } + if !is_exact && rule.flags.contains(FunctionFlags::EXACT) { + continue; + } + rule.used = true; + *base |= rule.flags; + + // only the first fule is used. + return true; + } + + false + }; + match key { + Some(key) => { + for (lookup, projection) in lookup_keys(resolve, key, LookupItem::Name(name)) { + if apply_rules(&lookup, projection.is_empty()) { + return; + } + } + } + None => { + if apply_rules(name, true) { + return; + } + } + } + + *base |= self.default; + } + + pub(crate) fn assert_all_rules_used(&self, kind: &str) -> Result<()> { + let mut unused = Vec::new(); + for rule in self.rules.iter().filter(|r| !r.used) { + unused.push(format!("{:?}: {:?}", rule.filter, rule.flags)); + } + + if unused.is_empty() { + return Ok(()); + } + + anyhow::bail!("unused `{kind}` rules found: {unused:?}"); + } +} diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs index ee9da684d2..b654c21485 100644 --- a/crates/wit-bindgen/src/lib.rs +++ b/crates/wit-bindgen/src/lib.rs @@ -29,9 +29,12 @@ macro_rules! uwriteln { }; } +mod config; mod rust; mod source; mod types; + +pub use config::{FunctionConfig, FunctionFilter, FunctionFlags}; use source::Source; #[derive(Clone)] @@ -65,11 +68,7 @@ struct Wasmtime { src: Source, opts: Opts, /// A list of all interfaces which were imported by this world. - /// - /// The first two values identify the interface; the third is the contents of the - /// module that this interface generated. The fourth value is the name of the - /// interface as also present in `self.interface_names`. - import_interfaces: Vec<(WorldKey, InterfaceId, String, InterfaceName)>, + import_interfaces: Vec, import_functions: Vec, exports: Exports, types: Types, @@ -80,12 +79,17 @@ struct Wasmtime { // Track the with options that were used. Remapped interfaces provided via `with` // are required to be used. used_with_opts: HashSet, - // Track the imports that matched the `trappable_imports` spec. - used_trappable_imports_opts: HashSet, world_link_options: LinkOptionsBuilder, interface_link_options: HashMap, } +struct ImportInterface { + id: InterfaceId, + contents: String, + name: InterfaceName, + all_func_flags: FunctionFlags, +} + #[derive(Default)] struct Exports { fields: BTreeMap, @@ -124,31 +128,6 @@ pub struct Opts { /// Whether or not `rustfmt` is executed to format generated code. pub rustfmt: bool, - /// Whether or not to emit `tracing` macro calls on function entry/exit. - pub tracing: bool, - - /// Whether or not `tracing` macro calls should included argument and - /// return values which contain dynamically-sized `list` values. - pub verbose_tracing: bool, - - /// Whether or not to use async rust functions and traits. - pub async_: AsyncConfig, - - /// Whether or not to use `func_wrap_concurrent` when generating code for - /// async imports. - /// - /// Unlike `func_wrap_async`, `func_wrap_concurrent` allows host functions - /// to suspend without monopolizing the `Store`, meaning other guest tasks - /// can make progress concurrently. - pub concurrent_imports: bool, - - /// Whether or not to use `call_concurrent` when generating code for - /// async exports. - /// - /// Unlike `call_async`, `call_concurrent` allows the caller to make - /// multiple concurrent calls on the same component instance. - pub concurrent_exports: bool, - /// A list of "trappable errors" which are used to replace the `E` in /// `result` found in WIT. pub trappable_error_type: Vec, @@ -159,9 +138,6 @@ pub struct Opts { /// Whether or not to generate code for only the interfaces of this wit file or not. pub only_interfaces: bool, - /// Configuration of which imports are allowed to generate a trap. - pub trappable_imports: TrappableImports, - /// Remapping of interface names to rust module names. /// TODO: is there a better type to use for the value of this map? pub with: HashMap, @@ -196,9 +172,14 @@ pub struct Opts { /// /// This can also be toggled via the `WASMTIME_DEBUG_BINDGEN` environment /// variable, but that will affect _all_ `bindgen!` macro invocations (and - /// can sometimes lead to one invocation ovewriting another in unpredictable + /// can sometimes lead to one invocation overwriting another in unpredictable /// ways), whereas this option lets you specify it on a case-by-case basis. pub debug: bool, + + /// TODO + pub imports: FunctionConfig, + /// TODO + pub exports: FunctionConfig, } #[derive(Debug, Clone)] @@ -210,61 +191,6 @@ pub struct TrappableError { pub rust_type_name: String, } -/// Which imports should be generated as async functions. -/// -/// The imports should be declared in the following format: -/// - Regular functions: `"{function-name}"` -/// - Resource methods: `"[method]{resource-name}.{method-name}"` -/// - Resource destructors: `"[drop]{resource-name}"` -/// -/// Examples: -/// - Regular function: `"get-environment"` -/// - Resource method: `"[method]input-stream.read"` -/// - Resource destructor: `"[drop]input-stream"` -#[derive(Default, Debug, Clone)] -pub enum AsyncConfig { - /// No functions are `async`. - #[default] - None, - /// All generated functions should be `async`. - All, - /// These imported functions should not be async, but everything else is. - AllExceptImports(HashSet), - /// These functions are the only imports that are async, all other imports - /// are sync. - /// - /// Note that all exports are still async in this situation. - OnlyImports(HashSet), -} - -#[derive(PartialEq, Debug, Copy, Clone)] -pub enum CallStyle { - Sync, - Async, - Concurrent, -} - -#[derive(Default, Debug, Clone)] -pub enum TrappableImports { - /// No imports are allowed to trap. - #[default] - None, - /// All imports may trap. - All, - /// Only the specified set of functions may trap. - Only(HashSet), -} - -impl TrappableImports { - fn can_trap(&self, f: &Function) -> bool { - match self { - TrappableImports::None => false, - TrappableImports::All => true, - TrappableImports::Only(set) => set.contains(&f.name), - } - } -} - impl Opts { pub fn generate(&self, resolve: &Resolve, world: WorldId) -> anyhow::Result { // TODO: Should we refine this test to inspect only types reachable from @@ -287,44 +213,6 @@ impl Opts { r.populate_world_and_interface_options(resolve, world); r.generate(resolve, world) } - - fn is_store_data_send(&self) -> bool { - matches!(self.call_style(), CallStyle::Async | CallStyle::Concurrent) - || self.require_store_data_send - } - - pub fn import_call_style(&self, qualifier: Option<&str>, f: &str) -> CallStyle { - let matched = |names: &HashSet| { - names.contains(f) - || qualifier - .map(|v| names.contains(&format!("{v}#{f}"))) - .unwrap_or(false) - }; - - match &self.async_ { - AsyncConfig::AllExceptImports(names) if matched(names) => CallStyle::Sync, - AsyncConfig::OnlyImports(names) if !matched(names) => CallStyle::Sync, - _ => self.call_style(), - } - } - - pub fn drop_call_style(&self, qualifier: Option<&str>, r: &str) -> CallStyle { - self.import_call_style(qualifier, &format!("[drop]{r}")) - } - - pub fn call_style(&self) -> CallStyle { - match &self.async_ { - AsyncConfig::None => CallStyle::Sync, - - AsyncConfig::All | AsyncConfig::AllExceptImports(_) | AsyncConfig::OnlyImports(_) => { - if self.concurrent_imports { - CallStyle::Concurrent - } else { - CallStyle::Async - } - } - } - } } impl Wasmtime { @@ -569,12 +457,13 @@ impl Wasmtime { " ) }; - self.import_interfaces.push(( - name.clone(), - *id, - module, - self.interface_names[id].clone(), - )); + let all_func_flags = generator.all_func_flags; + self.import_interfaces.push(ImportInterface { + id: *id, + contents: module, + name: self.interface_names[id].clone(), + all_func_flags, + }); let interface_path = self.import_interface_path(id); self.interface_link_options[id] @@ -844,12 +733,6 @@ pub fn new<_T>( let wt = self.wasmtime_path(); let world_name = &resolve.worlds[world].name; let camel = to_rust_upper_camel_case(&world_name); - let (async_, async__, where_clause, await_) = match self.opts.call_style() { - CallStyle::Async | CallStyle::Concurrent => { - ("async", "_async", "where _T: Send", ".await") - } - CallStyle::Sync => ("", "", "", ""), - }; uwriteln!( self.src, " @@ -901,20 +784,37 @@ impl<_T: 'static> {camel}Pre<_T> {{ /// instance to perform instantiation. Afterwards the preloaded /// indices in `self` are used to lookup all exports on the /// resulting instance. - pub {async_} fn instantiate{async__}( + pub fn instantiate( &self, mut store: impl {wt}::AsContextMut, - ) -> {wt}::Result<{camel}> - {where_clause} - {{ + ) -> {wt}::Result<{camel}> {{ let mut store = store.as_context_mut(); - let instance = self.instance_pre.instantiate{async__}(&mut store){await_}?; + let instance = self.instance_pre.instantiate(&mut store)?; self.indices.load(&mut store, &instance) }} }} " ); + if cfg!(feature = "async") { + uwriteln!( + self.src, + " +impl<_T: Send + 'static> {camel}Pre<_T> {{ + /// Same as [`Self::instantiate`], except with `async`. + pub async fn instantiate_async( + &self, + mut store: impl {wt}::AsContextMut, + ) -> {wt}::Result<{camel}> {{ + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + }} +}} +" + ); + } + uwriteln!( self.src, " @@ -943,13 +843,13 @@ impl<_T: 'static> {camel}Pre<_T> {{ /// depending on your requirements and what you have on hand: /// /// * The most convenient way is to use - /// [`{camel}::instantiate{async__}`] which only needs a + /// [`{camel}::instantiate`] which only needs a /// [`Store`], [`Component`], and [`Linker`]. /// /// * Alternatively you can create a [`{camel}Pre`] ahead of /// time with a [`Component`] to front-load string lookups /// of exports once instead of per-instantiation. This - /// method then uses [`{camel}Pre::instantiate{async__}`] to + /// method then uses [`{camel}Pre::instantiate`] to /// create a [`{camel}`]. /// /// * If you've instantiated the instance yourself already @@ -1034,16 +934,14 @@ impl<_T: 'static> {camel}Pre<_T> {{ self.src, "impl {camel} {{ /// Convenience wrapper around [`{camel}Pre::new`] and - /// [`{camel}Pre::instantiate{async__}`]. - pub {async_} fn instantiate{async__}<_T>( + /// [`{camel}Pre::instantiate`]. + pub fn instantiate<_T>( store: impl {wt}::AsContextMut, component: &{wt}::component::Component, linker: &{wt}::component::Linker<_T>, - ) -> {wt}::Result<{camel}> - {where_clause} - {{ + ) -> {wt}::Result<{camel}> {{ let pre = linker.instantiate_pre(component)?; - {camel}Pre::new(pre)?.instantiate{async__}(store){await_} + {camel}Pre::new(pre)?.instantiate(store) }} /// Convenience wrapper around [`{camel}Indices::new`] and @@ -1057,6 +955,26 @@ impl<_T: 'static> {camel}Pre<_T> {{ }} ", ); + + if cfg!(feature = "async") { + uwriteln!( + self.src, + " + /// Convenience wrapper around [`{camel}Pre::new`] and + /// [`{camel}Pre::instantiate_async`]. + pub async fn instantiate_async<_T>( + store: impl {wt}::AsContextMut, + component: &{wt}::component::Component, + linker: &{wt}::component::Linker<_T>, + ) -> {wt}::Result<{camel}> + where _T: Send, + {{ + let pre = linker.instantiate_pre(component)?; + {camel}Pre::new(pre)?.instantiate_async(store).await + }} + ", + ); + } self.world_add_to_linker(resolve, world, world_trait.as_ref()); for func in self.exports.funcs.iter() { @@ -1088,25 +1006,14 @@ impl<_T: 'static> {camel}Pre<_T> {{ self.build_world_struct(resolve, world) } - if let TrappableImports::Only(only) = &self.opts.trappable_imports { - let mut unused_imports = Vec::from_iter( - only.difference(&self.used_trappable_imports_opts) - .map(|s| s.as_str()), - ); - - if !unused_imports.is_empty() { - unused_imports.sort(); - anyhow::bail!( - "names specified in the `trappable_imports` config option but are not referenced in the target world: {unused_imports:?}" - ); - } - } + self.opts.imports.assert_all_rules_used("imports")?; + self.opts.exports.assert_all_rules_used("exports")?; let imports = mem::take(&mut self.import_interfaces); self.emit_modules( imports .into_iter() - .map(|(_, id, module, path)| (id, module, path)) + .map(|i| (i.id, i.contents, i.name)) .collect(), ); @@ -1394,12 +1301,12 @@ impl Wasmtime { fn import_interface_paths(&self) -> Vec<(InterfaceId, String)> { self.import_interfaces .iter() - .map(|(_, id, _, name)| { - let path = match name { + .map(|i| { + let path = match &i.name { InterfaceName::Path(path) => path.join("::"), InterfaceName::Remapped { name_at_root, .. } => name_at_root.clone(), }; - (*id, path) + (i.id, path) }) .collect() } @@ -1411,49 +1318,56 @@ impl Wasmtime { } } - fn import_interface_any_concurrent(&self, resolve: &Resolve, id: InterfaceId) -> bool { - for (key, id2, ..) in self.import_interfaces.iter() { - if id != *id2 { + fn import_interface_all_func_flags(&self, id: InterfaceId) -> FunctionFlags { + for i in self.import_interfaces.iter() { + if id != i.id { continue; } - let key = resolve.name_world_key(key); - return resolve.interfaces[id].functions.iter().any(|(name, _)| { - self.opts.import_call_style(Some(&key), name) == CallStyle::Concurrent - }) || get_resources(resolve, id).any(|(_, name)| { - matches!( - self.opts.drop_call_style(Some(&key), name), - CallStyle::Concurrent - ) - }); + return i.all_func_flags; } unreachable!() } fn world_host_traits( &self, - resolve: &Resolve, world_trait: Option<&GeneratedTrait>, ) -> (Vec, Vec) { - let mut sync = Vec::new(); - let mut concurrent = Vec::new(); + let mut without_store = Vec::new(); + let mut without_store_async = false; + let mut with_store = Vec::new(); + let mut with_store_async = false; for (id, path) in self.import_interface_paths() { - sync.push(format!("{path}::Host")); - if self.import_interface_any_concurrent(resolve, id) { - concurrent.push(format!("{path}::HostConcurrent")); - } + without_store.push(format!("{path}::Host")); + let flags = self.import_interface_all_func_flags(id); + without_store_async = without_store_async || flags.contains(FunctionFlags::ASYNC); + + // Note that the requirement of `HostWithStore` is technically + // dependent on `FunctionFlags::STORE`, but when `with` is in use we + // don't necessarily know whether the other bindings generation + // specified this flag or not. To handle that always assume that a + // `HostWithStore` bound is needed. + with_store.push(format!("{path}::HostWithStore")); + with_store_async = with_store_async || flags.contains(FunctionFlags::ASYNC); } if let Some(world_trait) = world_trait { - sync.push(world_trait.name.clone()); - concurrent.extend(world_trait.concurrent_name.clone()); - } - if let CallStyle::Async | CallStyle::Concurrent = self.opts.call_style() { - sync.push("Send".to_string()); - if !concurrent.is_empty() { - concurrent.push("Send".to_string()); + without_store.push(world_trait.name.clone()); + without_store_async = + without_store_async || world_trait.all_func_flags.contains(FunctionFlags::ASYNC); + + if world_trait.with_store_name.is_some() { + with_store.extend(world_trait.with_store_name.clone()); + with_store_async = + with_store_async || world_trait.all_func_flags.contains(FunctionFlags::ASYNC); } } - (sync, concurrent) + if without_store_async { + without_store.push("Send".to_string()); + } + if with_store_async { + with_store.push("Send".to_string()); + } + (without_store, with_store) } fn world_add_to_linker( @@ -1473,15 +1387,24 @@ impl Wasmtime { ("", "") }; - let opt_t_send_bound = if self.opts.is_store_data_send() { - "+ Send" - } else { - "" - }; + let mut all_func_flags = FunctionFlags::empty(); + if let Some(world_trait) = world_trait { + all_func_flags |= world_trait.all_func_flags; + } + for i in self.import_interfaces.iter() { + all_func_flags |= i.all_func_flags; + } + + let opt_t_send_bound = + if all_func_flags.contains(FunctionFlags::ASYNC) || self.opts.require_store_data_send { + "+ Send" + } else { + "" + }; let wt = self.wasmtime_path(); if let Some(world_trait) = world_trait { - let d_bound = match &world_trait.concurrent_name { + let d_bound = match &world_trait.with_store_name { Some(name) => name.clone(), None => format!("{wt}::component::HasData"), }; @@ -1503,16 +1426,8 @@ impl Wasmtime { name = world_trait.name, ); let gate = FeatureGate::open(&mut self.src, &resolve.worlds[world].stability); - for (ty, name) in get_world_resources(resolve, world) { - Self::generate_add_resource_to_linker( - None, - &mut self.src, - &self.opts, - &wt, - "linker", - name, - &resolve.types[ty].stability, - ); + for (ty, _name) in get_world_resources(resolve, world) { + self.generate_add_resource_to_linker(None, None, "linker", resolve, ty); } for f in self.import_functions.clone() { let mut generator = InterfaceGenerator::new(self, resolve); @@ -1525,7 +1440,7 @@ impl Wasmtime { uwriteln!(self.src, "Ok(())\n}}"); } - let (sync_bounds, concurrent_bounds) = self.world_host_traits(resolve, world_trait); + let (sync_bounds, concurrent_bounds) = self.world_host_traits(world_trait); let sync_bounds = sync_bounds.join(" + "); let concurrent_bounds = concurrent_bounds.join(" + "); let d_bounds = if !concurrent_bounds.is_empty() { @@ -1587,18 +1502,24 @@ impl Wasmtime { } fn generate_add_resource_to_linker( - qualifier: Option<&str>, - src: &mut Source, - opts: &Opts, - wt: &str, + &mut self, + key: Option<&WorldKey>, + src: Option<&mut Source>, inst: &str, - name: &str, - stability: &Stability, + resolve: &Resolve, + ty: TypeId, ) { + let ty = &resolve.types[ty]; + let name = ty.name.as_ref().unwrap(); + let stability = &ty.stability; + let wt = self.wasmtime_path(); + let src = src.unwrap_or(&mut self.src); let gate = FeatureGate::open(src, stability); let camel = name.to_upper_camel_case(); - match opts.drop_call_style(qualifier, name) { - CallStyle::Concurrent => { + + let flags = self.opts.imports.resource_drop_flags(resolve, key, name); + if flags.contains(FunctionFlags::ASYNC) { + if flags.contains(FunctionFlags::STORE) { uwriteln!( src, "{inst}.resource_concurrent( @@ -1607,38 +1528,36 @@ impl Wasmtime { move |caller: &{wt}::component::Accessor::, rep| {{ {wt}::component::__internal::Box::pin(async move {{ let accessor = &caller.with_data(host_getter); - Host{camel}Concurrent::drop(accessor, {wt}::component::Resource::new_own(rep)).await - }}) - }}, - )?;" - ) - } - CallStyle::Async => { - uwriteln!( - src, - "{inst}.resource_async( - \"{name}\", - {wt}::component::ResourceType::host::<{camel}>(), - move |mut store, rep| {{ - {wt}::component::__internal::Box::new(async move {{ - Host{camel}::drop(&mut host_getter(store.data_mut()), {wt}::component::Resource::new_own(rep)).await + Host{camel}WithStore::drop(accessor, {wt}::component::Resource::new_own(rep)).await }}) }}, )?;" ) - } - CallStyle::Sync => { + } else { uwriteln!( - src, - "{inst}.resource( - \"{name}\", - {wt}::component::ResourceType::host::<{camel}>(), - move |mut store, rep| -> {wt}::Result<()> {{ - Host{camel}::drop(&mut host_getter(store.data_mut()), {wt}::component::Resource::new_own(rep)) - }}, - )?;" - ) + src, + "{inst}.resource_async( + \"{name}\", + {wt}::component::ResourceType::host::<{camel}>(), + move |mut store, rep| {{ + {wt}::component::__internal::Box::new(async move {{ + Host{camel}::drop(&mut host_getter(store.data_mut()), {wt}::component::Resource::new_own(rep)).await + }}) + }}, + )?;" + ) } + } else { + uwriteln!( + src, + "{inst}.resource( + \"{name}\", + {wt}::component::ResourceType::host::<{camel}>(), + move |mut store, rep| -> {wt}::Result<()> {{ + Host{camel}::drop(&mut host_getter(store.data_mut()), {wt}::component::Resource::new_own(rep)) + }}, + )?;" + ) } gate.close(src); } @@ -1649,6 +1568,7 @@ struct InterfaceGenerator<'a> { generator: &'a mut Wasmtime, resolve: &'a Resolve, current_interface: Option<(InterfaceId, &'a WorldKey, bool)>, + all_func_flags: FunctionFlags, } impl<'a> InterfaceGenerator<'a> { @@ -1658,6 +1578,7 @@ impl<'a> InterfaceGenerator<'a> { generator, resolve, current_interface: None, + all_func_flags: FunctionFlags::empty(), } } @@ -1737,12 +1658,13 @@ impl<'a> InterfaceGenerator<'a> { // Generate resource trait let functions = get_resource_functions(self.resolve, id); - self.generate_trait( + let trait_ = self.generate_trait( &format!("Host{camel}"), &functions, &[ExtraTraitMethod::ResourceDrop { name }], &[], ); + self.all_func_flags |= trait_.all_func_flags; } else { self.rustdoc(docs); uwriteln!( @@ -2290,10 +2212,6 @@ impl<'a> InterfaceGenerator<'a> { &mut self, func: &Function, ) -> Option<(&'a Result_, TypeId, String)> { - self.generator - .used_trappable_imports_opts - .insert(func.name.clone()); - let result = func.result?; // We fill in a special trappable error type in the case when a function has just one @@ -2385,9 +2303,13 @@ impl<'a> InterfaceGenerator<'a> { &get_resources(self.resolve, id).collect::>(), ); - let opt_t_send_bound = match self.generator.opts.call_style() { - CallStyle::Async | CallStyle::Concurrent => "+ Send", - CallStyle::Sync => "", + let opt_t_send_bound = if generated_trait + .all_func_flags + .contains(FunctionFlags::ASYNC) + { + "+ Send" + } else { + "" }; let mut sync_bounds = "Host".to_string(); @@ -2402,11 +2324,6 @@ impl<'a> InterfaceGenerator<'a> { "" }; - let d_bound = if generated_trait.any_concurrent { - "HostConcurrent".to_string() - } else { - format!("{wt}::component::HasData") - }; uwriteln!( self.src, " @@ -2416,7 +2333,7 @@ impl<'a> InterfaceGenerator<'a> { host_getter: fn(&mut T) -> D::Data<'_>, ) -> {wt}::Result<()> where - D: {d_bound}, + D: HostWithStore, for<'a> D::Data<'a>: {sync_bounds}, T: 'static {opt_t_send_bound}, {{ @@ -2426,15 +2343,13 @@ impl<'a> InterfaceGenerator<'a> { let gate = FeatureGate::open(&mut self.src, &iface.stability); uwriteln!(self.src, "let mut inst = linker.instance(\"{name}\")?;"); - for (ty, name) in get_resources(self.resolve, id) { - Wasmtime::generate_add_resource_to_linker( - self.qualifier().as_deref(), - &mut self.src, - &self.generator.opts, - &wt, + for (ty, _name) in get_resources(self.resolve, id) { + self.generator.generate_add_resource_to_linker( + self.current_interface.map(|p| p.1), + Some(&mut self.src), "inst", - name, - &self.resolve.types[ty].stability, + self.resolve, + ty, ); } @@ -2446,36 +2361,50 @@ impl<'a> InterfaceGenerator<'a> { uwriteln!(self.src, "}}"); } - fn qualifier(&self) -> Option { - self.current_interface - .map(|(_, key, _)| self.resolve.name_world_key(key)) + fn import_resource_drop_flags(&mut self, name: &str) -> FunctionFlags { + self.generator.opts.imports.resource_drop_flags( + self.resolve, + self.current_interface.map(|p| p.1), + name, + ) } fn generate_add_function_to_linker(&mut self, owner: TypeOwner, func: &Function, linker: &str) { + let flags = self.generator.opts.imports.flags( + self.resolve, + self.current_interface.map(|p| p.1), + func, + ); + self.all_func_flags |= flags; let gate = FeatureGate::open(&mut self.src, &func.stability); uwrite!( self.src, "{linker}.{}(\"{}\", ", - match self.import_call_style(func) { - CallStyle::Sync => "func_wrap", - CallStyle::Async => "func_wrap_async", - CallStyle::Concurrent => "func_wrap_concurrent", + if flags.contains(FunctionFlags::ASYNC | FunctionFlags::STORE) { + "func_wrap_concurrent" + } else if flags.contains(FunctionFlags::ASYNC) { + "func_wrap_async" + } else { + "func_wrap" }, func.name ); - self.generate_guest_import_closure(owner, func); + self.generate_guest_import_closure(owner, func, flags); uwriteln!(self.src, ")?;"); gate.close(&mut self.src); } - fn generate_guest_import_closure(&mut self, owner: TypeOwner, func: &Function) { + fn generate_guest_import_closure( + &mut self, + owner: TypeOwner, + func: &Function, + flags: FunctionFlags, + ) { // Generate the closure that's passed to a `Linker`, the final piece of // codegen here. - let style = self.import_call_style(func); - let wt = self.generator.wasmtime_path(); - if let CallStyle::Concurrent = style { + if flags.contains(FunctionFlags::ASYNC | FunctionFlags::STORE) { uwrite!(self.src, "move |caller: &{wt}::component::Accessor::, ("); } else { uwrite!( @@ -2496,8 +2425,8 @@ impl<'a> InterfaceGenerator<'a> { } self.src.push_str(")| {\n"); - if self.generator.opts.tracing { - if let CallStyle::Async | CallStyle::Concurrent = style { + if flags.contains(FunctionFlags::TRACING) { + if flags.contains(FunctionFlags::ASYNC) { self.src.push_str("use tracing::Instrument;\n"); } @@ -2523,39 +2452,33 @@ impl<'a> InterfaceGenerator<'a> { ); } - match &style { - CallStyle::Async => { - uwriteln!( - self.src, - "{wt}::component::__internal::Box::new(async move {{" - ); - } - CallStyle::Concurrent => { - uwriteln!( - self.src, - "{wt}::component::__internal::Box::pin(async move {{ - let accessor = &caller.with_data(host_getter); - " - ); - } - CallStyle::Sync => { - // Only directly enter the span if the function is sync. Otherwise - // we use tracing::Instrument to ensure that the span is not entered - // across an await point. - if self.generator.opts.tracing { - self.push_str("let _enter = span.enter();\n"); - } + if flags.contains(FunctionFlags::ASYNC) { + let ctor = if flags.contains(FunctionFlags::STORE) { + "pin" + } else { + "new" + }; + uwriteln!( + self.src, + "{wt}::component::__internal::Box::{ctor}(async move {{" + ); + } else { + // Only directly enter the span if the function is sync. Otherwise + // we use tracing::Instrument to ensure that the span is not entered + // across an await point. + if flags.contains(FunctionFlags::TRACING) { + self.push_str("let _enter = span.enter();\n"); } } - if self.generator.opts.tracing { + if flags.contains(FunctionFlags::TRACING) { let mut event_fields = func .params .iter() .enumerate() .map(|(i, (name, ty))| { let name = to_rust_ident(&name); - formatting_for_arg(&name, i, *ty, &self.generator.opts, &self.resolve) + formatting_for_arg(&name, i, *ty, &self.resolve, flags) }) .collect::>(); event_fields.push(format!("\"call\"")); @@ -2566,7 +2489,9 @@ impl<'a> InterfaceGenerator<'a> { ); } - if !matches!(style, CallStyle::Concurrent) { + if flags.contains(FunctionFlags::STORE) { + uwriteln!(self.src, "let accessor = &caller.with_data(host_getter);"); + } else { self.src .push_str("let host = &mut host_getter(caller.data_mut());\n"); } @@ -2589,10 +2514,10 @@ impl<'a> InterfaceGenerator<'a> { } }; - if let CallStyle::Concurrent = &style { + if flags.contains(FunctionFlags::STORE) { uwrite!( self.src, - "let r = ::{func_name}(accessor, " + "let r = ::{func_name}(accessor, " ); } else { uwrite!(self.src, "let r = {host_trait}::{func_name}(host, "); @@ -2602,20 +2527,21 @@ impl<'a> InterfaceGenerator<'a> { uwrite!(self.src, "arg{},", i); } - self.src.push_str(match &style { - CallStyle::Sync => ");\n", - CallStyle::Async | CallStyle::Concurrent => ").await;\n", + self.src.push_str(if flags.contains(FunctionFlags::ASYNC) { + ").await;\n" + } else { + ");\n" }); - if self.generator.opts.tracing { + if flags.contains(FunctionFlags::TRACING) { uwrite!( self.src, "tracing::event!(tracing::Level::TRACE, {}, \"return\");", - formatting_for_results(func.result, &self.generator.opts, &self.resolve) + formatting_for_results(func.result, &self.resolve, flags) ); } - if !self.generator.opts.trappable_imports.can_trap(&func) { + if !flags.contains(FunctionFlags::TRAPPABLE) { if func.result.is_some() { uwrite!(self.src, "Ok((r,))\n"); } else { @@ -2646,49 +2572,43 @@ impl<'a> InterfaceGenerator<'a> { uwrite!(self.src, "r\n"); } - match &style { - CallStyle::Sync => (), - CallStyle::Async | CallStyle::Concurrent => { - if self.generator.opts.tracing { - self.src.push_str("}.instrument(span))\n"); - } else { - self.src.push_str("})\n"); - } + if flags.contains(FunctionFlags::ASYNC) { + if flags.contains(FunctionFlags::TRACING) { + self.src.push_str("}.instrument(span))\n"); + } else { + self.src.push_str("})\n"); } } self.src.push_str("}\n"); } - fn generate_function_trait_sig(&mut self, func: &Function, async_sugar: bool) { + fn generate_function_trait_sig(&mut self, func: &Function, flags: FunctionFlags) { let wt = self.generator.wasmtime_path(); self.rustdoc(&func.docs); - let style = self.import_call_style(func); - if let (CallStyle::Async, _) | (CallStyle::Concurrent, true) = (&style, async_sugar) { - self.push_str("async "); - } self.push_str("fn "); self.push_str(&rust_function_name(func)); - self.push_str(&if let CallStyle::Concurrent = &style { - format!("(accessor: &{wt}::component::Accessor, ") - } else { - "(&mut self, ".to_string() - }); + self.push_str( + &if flags.contains(FunctionFlags::STORE | FunctionFlags::ASYNC) { + format!("(accessor: &{wt}::component::Accessor, ") + } else { + "(&mut self, ".to_string() + }, + ); self.generate_function_params(func); self.push_str(")"); self.push_str(" -> "); - if let (CallStyle::Concurrent, false) = (&style, async_sugar) { + if flags.contains(FunctionFlags::ASYNC) { uwrite!(self.src, "impl ::core::future::Future + Send where Self: Sized,"); - } + if flags.contains(FunctionFlags::ASYNC) { + self.push_str("> + Send"); } } @@ -2702,8 +2622,8 @@ impl<'a> InterfaceGenerator<'a> { } } - fn generate_function_result(&mut self, func: &Function) { - if !self.generator.opts.trappable_imports.can_trap(func) { + fn generate_function_result(&mut self, func: &Function, flags: FunctionFlags) { + if !flags.contains(FunctionFlags::TRAPPABLE) { self.print_result_ty(func.result, TypeMode::Owned); } else if let Some((r, _id, error_typename)) = self.special_case_trappable_error(func) { // Functions which have a single result `result` get special @@ -2742,17 +2662,11 @@ impl<'a> InterfaceGenerator<'a> { ns: Option<&WorldKey>, func: &Function, ) { - // Exports must be async if anything could be async, it's just imports - // that get to be optionally async/sync. - let style = self.generator.opts.call_style(); - let (async_, async__, await_, concurrent) = match &style { - CallStyle::Async | CallStyle::Concurrent => ( - "async", - "_async", - ".await", - self.generator.opts.concurrent_exports, - ), - CallStyle::Sync => ("", "", "", false), + let flags = self.generator.opts.exports.flags(resolve, ns, func); + let (async_, async__, await_) = if flags.contains(FunctionFlags::ASYNC) { + ("async", "_async", ".await") + } else { + ("", "", "") }; self.rustdoc(&func.docs); @@ -2763,7 +2677,7 @@ impl<'a> InterfaceGenerator<'a> { "pub {async_} fn call_{}", func.item_name().to_snake_case(), ); - if concurrent { + if flags.contains(FunctionFlags::ASYNC | FunctionFlags::STORE) { uwrite!( self.src, "<_T, _D>(&self, accessor: &{wt}::component::Accessor<_T, _D>, ", @@ -2772,7 +2686,7 @@ impl<'a> InterfaceGenerator<'a> { uwrite!(self.src, "(&self, mut store: S, ",); } - let param_mode = if let CallStyle::Concurrent = &style { + let param_mode = if flags.contains(FunctionFlags::ASYNC | FunctionFlags::STORE) { TypeMode::Owned } else { TypeMode::AllBorrowed("'_") @@ -2788,20 +2702,15 @@ impl<'a> InterfaceGenerator<'a> { self.print_result_ty(func.result, TypeMode::Owned); uwrite!(self.src, ">"); - if concurrent { + if flags.contains(FunctionFlags::ASYNC | FunctionFlags::STORE) { uwrite!(self.src, " where _T: Send, _D: {wt}::component::HasData"); - } else { - match style { - CallStyle::Concurrent | CallStyle::Async => { - uwrite!(self.src, " where ::Data: Send"); - } - CallStyle::Sync => {} - } + } else if flags.contains(FunctionFlags::ASYNC) { + uwrite!(self.src, " where ::Data: Send"); } uwrite!(self.src, "{{\n"); - if self.generator.opts.tracing { - if let CallStyle::Async | CallStyle::Concurrent = &style { + if flags.contains(FunctionFlags::TRACING) { + if flags.contains(FunctionFlags::ASYNC) { self.src.push_str("use tracing::Instrument;\n"); } @@ -2821,7 +2730,7 @@ impl<'a> InterfaceGenerator<'a> { func.name, )); - if !matches!(&style, CallStyle::Async | CallStyle::Concurrent) { + if !flags.contains(FunctionFlags::ASYNC) { self.src.push_str( " let _enter = span.enter(); @@ -2852,7 +2761,7 @@ impl<'a> InterfaceGenerator<'a> { if func.result.is_some() { uwrite!(self.src, "ret0,"); } - if concurrent { + if flags.contains(FunctionFlags::ASYNC | FunctionFlags::STORE) { uwrite!(self.src, ") = callee.call_concurrent(accessor, ("); } else { uwrite!( @@ -2864,24 +2773,20 @@ impl<'a> InterfaceGenerator<'a> { uwrite!(self.src, "arg{}, ", i); } - let instrument = if matches!(&style, CallStyle::Async | CallStyle::Concurrent) - && self.generator.opts.tracing - { + let instrument = if flags.contains(FunctionFlags::ASYNC | FunctionFlags::TRACING) { ".instrument(span.clone())" } else { "" }; uwriteln!(self.src, ")){instrument}{await_}?;"); - let instrument = if matches!(&style, CallStyle::Async | CallStyle::Concurrent) - && self.generator.opts.tracing - { + let instrument = if flags.contains(FunctionFlags::ASYNC | FunctionFlags::TRACING) { ".instrument(span)" } else { "" }; - if !concurrent { + if !flags.contains(FunctionFlags::STORE) { uwriteln!( self.src, "callee.post_return{async__}(store.as_context_mut()){instrument}{await_}?;" @@ -2930,24 +2835,22 @@ impl<'a> InterfaceGenerator<'a> { path_to_root } - fn import_call_style(&self, func: &Function) -> CallStyle { - self.generator - .opts - .import_call_style(self.qualifier().as_deref(), &func.name) - } - fn partition_concurrent_funcs<'b>( - &self, + &mut self, funcs: impl IntoIterator, ) -> FunctionPartitioning<'b> { - let (concurrent, sync) = - funcs - .into_iter() - .partition(|func| match self.import_call_style(func) { - CallStyle::Concurrent => true, - CallStyle::Async | CallStyle::Sync => false, - }); - FunctionPartitioning { concurrent, sync } + let key = self.current_interface.map(|p| p.1); + let (with_store, without_store) = funcs + .into_iter() + .map(|func| { + let flags = self.generator.opts.imports.flags(self.resolve, key, func); + (func, flags) + }) + .partition(|(_, flags)| flags.contains(FunctionFlags::STORE)); + FunctionPartitioning { + with_store, + without_store, + } } fn generate_trait( @@ -2959,120 +2862,109 @@ impl<'a> InterfaceGenerator<'a> { ) -> GeneratedTrait { let mut ret = GeneratedTrait::default(); let wt = self.generator.wasmtime_path(); - let is_maybe_async = matches!( - self.generator.opts.call_style(), - CallStyle::Async | CallStyle::Concurrent - ); let partition = self.partition_concurrent_funcs(functions.iter().copied()); - ret.any_concurrent = !partition.concurrent.is_empty() - || extra_functions.iter().any(|f| { - matches!( - f, - ExtraTraitMethod::ResourceDrop { name } - if matches!( - self - .generator - .opts - .drop_call_style(self.qualifier().as_deref(), name), - CallStyle::Concurrent - ) - ) - }); - let mut concurrent_supertraits = vec![format!("{wt}::component::HasData")]; - let mut sync_supertraits = vec![]; - if is_maybe_async { - concurrent_supertraits.push("Send".to_string()); - sync_supertraits.push("Send".to_string()); + for (_, flags) in partition.with_store.iter().chain(&partition.without_store) { + ret.all_func_flags |= *flags; } + + let mut with_store_supertraits = vec![format!("{wt}::component::HasData")]; + let mut without_store_supertraits = vec![]; for (id, name) in resources { let camel = name.to_upper_camel_case(); - sync_supertraits.push(format!("Host{camel}")); + without_store_supertraits.push(format!("Host{camel}")); let funcs = self.partition_concurrent_funcs(get_resource_functions(self.resolve, *id)); - let concurrent_drop = matches!( - self.generator - .opts - .drop_call_style(self.qualifier().as_deref(), name), - CallStyle::Concurrent - ); - if concurrent_drop || !funcs.concurrent.is_empty() { - ret.any_concurrent = true; - concurrent_supertraits.push(format!("Host{camel}Concurrent")); + for (_, flags) in funcs.with_store.iter().chain(&funcs.without_store) { + ret.all_func_flags |= *flags; } + ret.all_func_flags |= self.import_resource_drop_flags(name); + with_store_supertraits.push(format!("Host{camel}WithStore")); + } + if ret.all_func_flags.contains(FunctionFlags::ASYNC) { + with_store_supertraits.push("Send".to_string()); + without_store_supertraits.push("Send".to_string()); } - if ret.any_concurrent { - uwriteln!( - self.src, - "#[{wt}::component::__internal::trait_variant_make(::core::marker::Send)]", - ); - uwriteln!( - self.src, - "pub trait {trait_name}Concurrent: {} {{", - concurrent_supertraits.join(" + "), - ); - ret.concurrent_name = Some(format!("{trait_name}Concurrent")); - for func in partition.concurrent.iter() { - self.generate_function_trait_sig(func, false); - self.push_str(";\n"); - } - - for extra in extra_functions { - match extra { - ExtraTraitMethod::ResourceDrop { name } => { - let camel = name.to_upper_camel_case(); - if let CallStyle::Concurrent = self - .generator - .opts - .drop_call_style(self.qualifier().as_deref(), name) - { - uwrite!( - self.src, - "fn drop(accessor: &{wt}::component::Accessor, rep: {wt}::component::Resource<{camel}>) -> impl ::core::future::Future> + Send where Self: Sized;" - ); - } + uwriteln!( + self.src, + "pub trait {trait_name}WithStore: {} {{", + with_store_supertraits.join(" + "), + ); + ret.with_store_name = Some(format!("{trait_name}WithStore")); + + let mut extra_with_store_function = false; + for extra in extra_functions { + match extra { + ExtraTraitMethod::ResourceDrop { name } => { + let flags = self.import_resource_drop_flags(name); + if !flags.contains(FunctionFlags::STORE) { + continue; } - ExtraTraitMethod::ErrorConvert { .. } => {} + let camel = name.to_upper_camel_case(); + + assert!(flags.contains(FunctionFlags::ASYNC)); + + uwrite!( + self.src, + "fn drop(accessor: &{wt}::component::Accessor, rep: {wt}::component::Resource<{camel}>) -> impl ::core::future::Future> + Send where Self: Sized;" + ); + extra_with_store_function = true; } + ExtraTraitMethod::ErrorConvert { .. } => {} } + } - uwriteln!(self.src, "}}"); + for (func, flags) in partition.with_store.iter() { + self.generate_function_trait_sig(func, *flags); + self.push_str(";\n"); } + uwriteln!(self.src, "}}"); - if is_maybe_async { + // If `*WithStore` is empty, generate a blanket impl for the trait since + // it's otherwise not necessary to implement it manually. + if partition.with_store.is_empty() && !extra_with_store_function { + uwriteln!(self.src, "impl<_T: ?Sized> {trait_name}WithStore for _T"); uwriteln!( self.src, - "#[{wt}::component::__internal::trait_variant_make(::core::marker::Send)]", + " where _T: {}", + with_store_supertraits.join(" + ") ); + + uwriteln!(self.src, "{{}}"); } + uwriteln!( self.src, "pub trait {trait_name}: {} {{", - sync_supertraits.join(" + ") + without_store_supertraits.join(" + ") ); ret.name = trait_name.to_string(); - for func in partition.sync.iter() { - self.generate_function_trait_sig(func, false); + for (func, flags) in partition.without_store.iter() { + self.generate_function_trait_sig(func, *flags); self.push_str(";\n"); } for extra in extra_functions { match extra { ExtraTraitMethod::ResourceDrop { name } => { + let flags = self.import_resource_drop_flags(name); + ret.all_func_flags |= flags; + if flags.contains(FunctionFlags::STORE) { + continue; + } let camel = name.to_upper_camel_case(); - let style = self - .generator - .opts - .drop_call_style(self.qualifier().as_deref(), name); - if !matches!(style, CallStyle::Concurrent) { - if let CallStyle::Async = style { - uwrite!(self.src, "async "); - } - uwrite!( - self.src, - "fn drop(&mut self, rep: {wt}::component::Resource<{camel}>) -> {wt}::Result<()>;" - ); + uwrite!( + self.src, + "fn drop(&mut self, rep: {wt}::component::Resource<{camel}>) -> " + ); + if flags.contains(FunctionFlags::ASYNC) { + uwrite!(self.src, "impl ::core::future::Future"); + if flags.contains(FunctionFlags::ASYNC) { + uwrite!(self.src, "> + Send"); + } + uwrite!(self.src, ";"); } ExtraTraitMethod::ErrorConvert { name, id } => { let root = self.path_to_root(); @@ -3096,51 +2988,56 @@ fn convert_{snake}(&mut self, err: {root}{custom_name}) -> {wt}::Result<{camel}> } // Generate impl HostResource for &mut HostResource - let maybe_send = if is_maybe_async { "+ Send" } else { "" }; + let maybe_send = if ret.all_func_flags.contains(FunctionFlags::ASYNC) { + "+ Send" + } else { + "" + }; uwriteln!( self.src, "impl <_T: {trait_name} + ?Sized {maybe_send}> {trait_name} for &mut _T {{" ); - for func in partition.sync.iter() { - let call_style = self.import_call_style(func); - self.generate_function_trait_sig(func, true); + for (func, flags) in partition.without_store.iter() { + self.generate_function_trait_sig(func, *flags); + uwriteln!(self.src, "{{"); + if flags.contains(FunctionFlags::ASYNC) { + uwriteln!(self.src, "async move {{"); + } uwrite!( self.src, - "{{ {trait_name}::{}(*self,", + "{trait_name}::{}(*self,", rust_function_name(func) ); for (name, _) in func.params.iter() { uwrite!(self.src, "{},", to_rust_ident(name)); } uwrite!(self.src, ")"); - if let CallStyle::Async = &call_style { - uwrite!(self.src, ".await"); + if flags.contains(FunctionFlags::ASYNC) { + uwrite!(self.src, ".await\n}}"); } uwriteln!(self.src, "}}"); } for extra in extra_functions { match extra { ExtraTraitMethod::ResourceDrop { name } => { + let flags = self.import_resource_drop_flags(name); + if flags.contains(FunctionFlags::STORE) { + continue; + } let camel = name.to_upper_camel_case(); - let style = self - .generator - .opts - .drop_call_style(self.qualifier().as_deref(), name); let mut await_ = ""; - if !matches!(style, CallStyle::Concurrent) { - if let CallStyle::Async = style { - self.src.push_str("async "); - await_ = ".await"; - } - uwriteln!( - self.src, - " + if flags.contains(FunctionFlags::ASYNC) { + self.src.push_str("async "); + await_ = ".await"; + } + uwriteln!( + self.src, + " fn drop(&mut self, rep: {wt}::component::Resource<{camel}>) -> {wt}::Result<()> {{ {trait_name}::drop(*self, rep){await_} }} ", - ); - } + ); } ExtraTraitMethod::ErrorConvert { name, id } => { let root = self.path_to_root(); @@ -3170,15 +3067,15 @@ enum ExtraTraitMethod<'a> { } struct FunctionPartitioning<'a> { - sync: Vec<&'a Function>, - concurrent: Vec<&'a Function>, + without_store: Vec<(&'a Function, FunctionFlags)>, + with_store: Vec<(&'a Function, FunctionFlags)>, } #[derive(Default)] struct GeneratedTrait { name: String, - concurrent_name: Option, - any_concurrent: bool, + with_store_name: Option, + all_func_flags: FunctionFlags, } impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> { @@ -3387,10 +3284,10 @@ fn formatting_for_arg( name: &str, index: usize, ty: Type, - opts: &Opts, resolve: &Resolve, + flags: FunctionFlags, ) -> String { - if !opts.verbose_tracing && type_contains_lists(ty, resolve) { + if !flags.contains(FunctionFlags::VERBOSE_TRACING) && type_contains_lists(ty, resolve) { return format!("{name} = tracing::field::debug(\"...\")"); } @@ -3399,13 +3296,13 @@ fn formatting_for_arg( } /// Produce a string for tracing function results. -fn formatting_for_results(result: Option, opts: &Opts, resolve: &Resolve) -> String { +fn formatting_for_results(result: Option, resolve: &Resolve, flags: FunctionFlags) -> String { let contains_lists = match result { Some(ty) => type_contains_lists(ty, resolve), None => false, }; - if !opts.verbose_tracing && contains_lists { + if !flags.contains(FunctionFlags::VERBOSE_TRACING) && contains_lists { return format!("result = tracing::field::debug(\"...\")"); } diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 57c51e5d11..d5e2ba189d 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -67,6 +67,7 @@ - [Cross Compiling](./contributing-cross-compiling.md) - [Coding Guidelines](./contributing-coding-guidelines.md) - [Development Process](./contributing-development-process.md) + - [RFC Process](./contributing-rfc-process.md) - [Implementing Wasm Proposals](./contributing-implementing-wasm-proposals.md) - [Maintainer Guidelines](./contributing-maintainer-guidelines.md) - [Code Review](./contributing-code-review.md) diff --git a/docs/cli-install.md b/docs/cli-install.md index 3264b3c560..a4832b0193 100644 --- a/docs/cli-install.md +++ b/docs/cli-install.md @@ -60,6 +60,26 @@ executed normally as the CLI would. [`wasmtime-dev-x86_64-macos.tar.xz`]: https://github.com/bytecodealliance/wasmtime/releases/download/dev/wasmtime-dev-x86_64-macos.tar.xz [`wasmtime-dev-x86_64-windows.zip`]: https://github.com/bytecodealliance/wasmtime/releases/download/dev/wasmtime-dev-x86_64-windows.zip +## Install via Cargo + +If you have [Rust and Cargo](https://www.rust-lang.org/tools/install) available in your system, you can build and install an official `wasmtime` release from its [crates.io](https://crates.io/crates/wasmtime-cli) source: + +```console +cargo install wasmtime-cli +``` + +This compiles and installs `wasmtime` into your Cargo bin directory (typically `$HOME/.cargo/bin`). Make sure that directory is in your `PATH` before running `wasmtime`. For example, add the following line to `~/.bashrc` or `~/.zshrc`: + +```console +export PATH="$HOME/.cargo/bin:$PATH" +``` + +You can also use [`binstall`](https://github.com/cargo-bins/cargo-binstall) to automatically find and install the correct `wasmtime` binary for your system, matching a candidate from [GitHub Releases](https://github.com/bytecodealliance/wasmtime/releases): + +```console +cargo binstall wasmtime-cli +``` + ## Compiling from Source If you'd prefer to compile the `wasmtime` CLI from source, you'll want to diff --git a/docs/contributing-development-process.md b/docs/contributing-development-process.md index 1d4da19d01..25043881ed 100644 --- a/docs/contributing-development-process.md +++ b/docs/contributing-development-process.md @@ -10,7 +10,9 @@ reviewing code submissions. We triage new issues at each of our bi-weekly Consider opening an issue to talk about it. PRs without corresponding issues are appropriate for fairly narrow technical matters, not for fixes to user-facing bugs or for feature implementations, especially when those features -might have multiple implementation strategies that usefully could be discussed. +might have multiple implementation strategies that usefully could be +discussed. Changes that will significantly affect stakeholders should first be +proposed in an [RFC](./contributing-rfc-process.md). Our issue templates might help you through the process. diff --git a/docs/contributing-fuzzing.md b/docs/contributing-fuzzing.md index 0432b76656..e5cb20199f 100644 --- a/docs/contributing-fuzzing.md +++ b/docs/contributing-fuzzing.md @@ -23,7 +23,7 @@ reused with libFuzzer or AFL or any other fuzzing engine or driver. ## libFuzzer and `cargo fuzz` Fuzz Targets -We combine a test case generator and one more more oracles into a *fuzz +We combine a test case generator and one more oracles into a *fuzz target*. Because the target needs to pipe the raw input from a fuzzer into the test case generator, it is specific to a particular fuzzer. This is generally fine, since they're only a couple of lines of glue code. diff --git a/docs/contributing-rfc-process.md b/docs/contributing-rfc-process.md new file mode 100644 index 0000000000..999b313f7e --- /dev/null +++ b/docs/contributing-rfc-process.md @@ -0,0 +1,50 @@ +# RFC Process + +For changes that will significantly affect Wasmtime's or Cranelift's internals, +downstream projects, contributors, and other stakeholders, a [Bytecode Alliance +RFC](https://github.com/bytecodealliance/rfcs/) should be used to gather +feedback on the proposed change's design and implementation, and to build +consensus. + +It is recommended that regular Wasmtime and Cranelift contributors, as well as +any other project stakeholders, subscribe to notifications in [the RFC +repository](https://github.com/bytecodealliance/rfcs/) to stay up to date with +significant change proposals. + +## Authoring New RFCs + +The RFC repository has two templates that can help you author new proposals: + +1. [A draft RFC + template](https://github.com/bytecodealliance/rfcs/blob/main/template-draft.md), + for seeking early feedback on ideas that aren't yet fully baked. It is + expected that as the discussion evolves, these draft RFCs will grow into + fully-formed RFCs. + +2. [A full RFC + template](https://github.com/bytecodealliance/rfcs/blob/main/template-full.md), + for building consensus around a mature proposal. + +You can also look at historical Wasmtime RFCs in [the repository's `accepted` +subdirectory](https://github.com/bytecodealliance/rfcs/tree/main/accepted) and +their associated discussions in its [merged pull +requests](https://github.com/bytecodealliance/rfcs/pulls?q=is%3Apr+is%3Amerged+) +to gather inspiration for your new RFC. A few good examples include: + +* Add a Long Term Support (LTS) Channel of Releases for Wasmtime - + [RFC](https://github.com/bytecodealliance/rfcs/blob/main/accepted/wasmtime-lts.md) - + [Discussion](https://github.com/bytecodealliance/rfcs/pull/42) +* Pulley: A Portable, Optimizing Interpreter for Wasmtime - + [RFC](https://github.com/bytecodealliance/rfcs/blob/main/accepted/pulley.md) - + [Discussion](https://github.com/bytecodealliance/rfcs/pull/35) +* Debugging Support in Wasmtime - + [RFC](https://github.com/bytecodealliance/rfcs/blob/main/accepted/wasmtime-debugging.md) - + [Discussion](https://github.com/bytecodealliance/rfcs/pull/34) +* Redesign Wasmtime's API - + [RFC](https://github.com/bytecodealliance/rfcs/blob/main/accepted/new-api.md) - + [Discussion](https://github.com/bytecodealliance/rfcs/pull/11) + +After creating a pull request for your new RFC, advertise its existence by +creating a new thread in the relevant +[Zulip](https://bytecodealliance.zulipchat.com/) channels (e.g. `#wasmtime` or +`#cranelift`). diff --git a/docs/examples-deterministic-wasm-execution.md b/docs/examples-deterministic-wasm-execution.md index 5adfc7316a..2f7272c479 100644 --- a/docs/examples-deterministic-wasm-execution.md +++ b/docs/examples-deterministic-wasm-execution.md @@ -32,7 +32,7 @@ for more details. The relaxed SIMD proposal gives Wasm programs access to SIMD operations that cannot be made to execute both identically and performantly across different -architecures. The proposal gave up determinism across different achitectures in +architectures. The proposal gave up determinism across different achitectures in order to maintain portable performance. At the cost of worse runtime performance, Wasmtime can deterministically execute diff --git a/docs/stability-tiers.md b/docs/stability-tiers.md index 7bbb83288b..7c6e9b7e2a 100644 --- a/docs/stability-tiers.md +++ b/docs/stability-tiers.md @@ -93,6 +93,8 @@ For explanations of what each tier means see below. [`relaxed-simd`]: https://github.com/WebAssembly/relaxed-simd/blob/main/proposals/relaxed-simd/Overview.md [`function-references`]: https://github.com/WebAssembly/function-references/blob/main/proposals/function-references/Overview.md [`wide-arithmetic`]: https://github.com/WebAssembly/wide-arithmetic/blob/main/proposals/wide-arithmetic/Overview.md +[`exception-handling`]: https://github.com/WebAssembly/exception-handling +[`stack-switching`]: https://github.com/WebAssembly/stack-switching #### Tier 3 @@ -226,6 +228,8 @@ here is: | [`gc`] | ✅ | ❌ | | [`wide-arithmetic`] | ✅ | ✅ | | [`custom-page-sizes`] | ✅ | ✅ | +| [`exception-handling`] | 🚧 | ❌ | +| [`stack-switching`] | 🚧 | ❌ | ##### aarch64 @@ -249,6 +253,8 @@ here is: | [`gc`] | ✅ | ❌ | | [`wide-arithmetic`] | ✅ | ❌ | | [`custom-page-sizes`] | ✅ | ✅ | +| [`exception-handling`] | 🚧 | ❌ | +| [`stack-switching`] | ❌ | ❌ | ##### s390x @@ -272,6 +278,8 @@ here is: | [`gc`] | ✅ | ❌ | | [`wide-arithmetic`] | ✅ | ❌ | | [`custom-page-sizes`] | ✅ | ❌ | +| [`exception-handling`] | 🚧 | ❌ | +| [`stack-switching`] | ❌ | ❌ | ##### riscv64 @@ -295,6 +303,8 @@ here is: | [`gc`] | ✅ | ❌ | | [`wide-arithmetic`] | ✅ | ❌ | | [`custom-page-sizes`] | ✅ | ❌ | +| [`exception-handling`] | 🚧 | ❌ | +| [`stack-switching`] | ❌ | ❌ | ##### Pulley @@ -323,6 +333,8 @@ emitting Pulley bytecode. | [`gc`] | ✅ | ❌ | | [`wide-arithmetic`] | ✅ | ❌ | | [`custom-page-sizes`] | ✅ | ❌ | +| [`exception-handling`] | 🚧 | ❌ | +| [`stack-switching`] | ❌ | ❌ | [^a]: Winch supports some features of the [`reference-types`] proposal such as the change to support multiple tables and LEB-encoding table indices in diff --git a/docs/stability-wasm-proposals.md b/docs/stability-wasm-proposals.md index fab8f747e9..e8612026da 100644 --- a/docs/stability-wasm-proposals.md +++ b/docs/stability-wasm-proposals.md @@ -9,6 +9,12 @@ WebAssembly proposals that want to be [tier 2 or above](./stability-tiers.md) are required to check all boxes in this matrix. An explanation of each matrix column is below. +The emoji legend is: + +* ✅ - fully supported +* 🚧 - work-in-progress +* ❌ - not supported yet + ## On-by-default proposals | Proposal | Phase 4 | Tests | Finished | Fuzzed | API | C API | @@ -20,7 +26,7 @@ column is below. | [`bulk-memory`] | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [`reference-types`] | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [`simd`] | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [`component-model`] | ❌[^1] | ✅ | ✅ | ⚠️[^2] | ✅ | ❌[^5]| +| [`component-model`] | ❌[^1] | ✅ | ✅ | 🚧[^2] | ✅ | ❌[^5]| | [`relaxed-simd`] | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [`multi-memory`] | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [`threads`] | ✅ | ✅ | ✅[^9] | ❌[^3] | ✅ | ✅ | @@ -44,12 +50,14 @@ column is below. ## Off-by-default proposals -| Proposal | Phase 4 | Tests | Finished | Fuzzed | API | C API | -|--------------------------|---------|-------|----------|--------|-----|-------| -| [`function-references`] | ✅ | ✅ | ✅ | ⚠️ | ✅ | ❌ | -| [`gc`] [^6] | ✅ | ✅ | ⚠️[^7] | ⚠️[^8] | ✅ | ❌ | -| [`wide-arithmetic`] | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [`custom-page-sizes`] | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | +| Proposal | Phase 4 | Tests | Finished | Fuzzed | API | C API | +|-----------------------------|---------|-------|----------|--------|-----|-------| +| [`function-references`] | ✅ | ✅ | ✅ | 🚧 | ✅ | ❌ | +| [`gc`] [^6] | ✅ | ✅ | 🚧[^7] | 🚧[^8] | ✅ | ❌ | +| [`wide-arithmetic`] | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [`custom-page-sizes`] | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | +| [`exception-handling`] [^10]| ✅ | ❌ | 🚧 | ❌ | 🚧 | ❌ | +| [`stack-switching`] [^11] | ❌ | 🚧 | 🚧 | ❌ | ❌ | ❌ | [^6]: There is also a [tracking issue](https://github.com/bytecodealliance/wasmtime/issues/5032) for the @@ -62,16 +70,19 @@ column is below. whole-module fuzz targets like `differential`, but we would like to additionally [extend the `table_ops` fuzz target to exercise more of the GC proposal](https://github.com/bytecodealliance/wasmtime/issues/10327). +[^10]: The exception-handling proposal is a work-in-progress being tracked + at [#3427](https://github.com/bytecodealliance/wasmtime/issues/3427) +[^11]: The stack-switching proposal is a work-in-progress being tracked + at [#9465](https://github.com/bytecodealliance/wasmtime/issues/9465). + Currently the implementation is only for x86\_64 Linux. ## Unimplemented proposals | Proposal | Tracking Issue | |-------------------------------|----------------| | [`branch-hinting`] | [#9463](https://github.com/bytecodealliance/wasmtime/issues/9463) | -| [`exception-handling`] | [#3427](https://github.com/bytecodealliance/wasmtime/issues/3427) | | [`flexible-vectors`] | [#9464](https://github.com/bytecodealliance/wasmtime/issues/9464) | | [`memory-control`] | [#9467](https://github.com/bytecodealliance/wasmtime/issues/9467) | -| [`stack-switching`] | [#9465](https://github.com/bytecodealliance/wasmtime/issues/9465) | | [`shared-everything-threads`] | [#9466](https://github.com/bytecodealliance/wasmtime/issues/9466) | [`mutable-globals`]: https://github.com/WebAssembly/mutable-global/blob/master/proposals/mutable-global/Overview.md diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 81a797bbeb..4d6c1ea56d 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -24,9 +24,6 @@ function(CREATE_TARGET TARGET TARGET_PATH) target_include_directories(wasmtime-${TARGET} PUBLIC wasmtime) target_link_libraries(wasmtime-${TARGET} PUBLIC wasmtime) - if(APPLE) - target_link_libraries(wasmtime-${TARGET} PRIVATE "-framework CoreFoundation") - endif() add_test(NAME ${TARGET}-c COMMAND wasmtime-${TARGET} WORKING_DIRECTORY ../..) endfunction() diff --git a/examples/min-platform/embedding/src/wasi.rs b/examples/min-platform/embedding/src/wasi.rs index c3a6b631ba..28294aaf92 100644 --- a/examples/min-platform/embedding/src/wasi.rs +++ b/examples/min-platform/embedding/src/wasi.rs @@ -152,8 +152,9 @@ fn deserialize(engine: &Engine, component: &[u8]) -> Result> { wasmtime::component::bindgen!({ path: "../../../crates/wasi/src/p2/wit", world: "wasi:cli/command", - async: { only_imports: [] }, - trappable_imports: true, + imports: { default: trappable }, + exports: { default: async }, + require_store_data_send: true, // Important: tell bindgen that anywhere it encounters the wasi:io // package, refer to the bindings generated in the wasmtime_wasi_io crate. // This way, all uses of the streams and pollable in the bindings in this diff --git a/examples/resource-component/main.rs b/examples/resource-component/main.rs index 8d7cfe75d2..96aa5cf54f 100644 --- a/examples/resource-component/main.rs +++ b/examples/resource-component/main.rs @@ -50,13 +50,13 @@ impl ComponentRunStates { bindgen!({ path: "./examples/resource-component/kv-store.wit", world: "kv-database", - async: true, + // Interactions with `ResourceTable` can possibly trap so enable the ability + // to return traps from generated functions. + imports: { default: async | trappable }, + exports: { default: async }, with: { "example:kv-store/kvdb/connection": Connection }, - // Interactions with `ResourceTable` can possibly trap so enable the ability - // to return traps from generated functions. - trappable_imports: true, }); pub struct Connection { diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index c3c94c46ee..edca9087c7 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -12,6 +12,9 @@ workspace = true cargo-fuzz = true [dependencies] +mutatis = { workspace = true } +rand = { workspace = true } +postcard = { workspace = true } anyhow = { workspace = true } env_logger = { workspace = true } cranelift-assembler-x64 = { workspace = true, features = ["fuzz"] } @@ -105,3 +108,10 @@ path = "fuzz_targets/misc.rs" test = false doc = false bench = false + +[[bin]] +name = "table_ops" +path = "fuzz_targets/table_ops.rs" +test = false +doc = false +bench = false diff --git a/fuzz/fuzz_targets/misc.rs b/fuzz/fuzz_targets/misc.rs index be166b0f18..792ce941ac 100644 --- a/fuzz/fuzz_targets/misc.rs +++ b/fuzz/fuzz_targets/misc.rs @@ -64,7 +64,6 @@ run_fuzzers! { pulley_roundtrip assembler_roundtrip memory_accesses - table_ops stacks api_calls dominator_tree @@ -87,12 +86,6 @@ fn memory_accesses(u: Unstructured<'_>) -> Result<()> { Ok(()) } -fn table_ops(u: Unstructured<'_>) -> Result<()> { - let (config, ops) = Arbitrary::arbitrary_take_rest(u)?; - let _ = wasmtime_fuzzing::oracles::table_ops(config, ops); - Ok(()) -} - fn stacks(u: Unstructured<'_>) -> Result<()> { wasmtime_fuzzing::oracles::check_stacks(Arbitrary::arbitrary_take_rest(u)?); Ok(()) diff --git a/fuzz/fuzz_targets/table_ops.rs b/fuzz/fuzz_targets/table_ops.rs new file mode 100644 index 0000000000..3eef3c0e5b --- /dev/null +++ b/fuzz/fuzz_targets/table_ops.rs @@ -0,0 +1,58 @@ +#![no_main] + +use libfuzzer_sys::arbitrary::{Arbitrary, Unstructured}; +use libfuzzer_sys::{fuzz_mutator, fuzz_target, fuzzer_mutate}; +use mutatis::Session; +use postcard::{from_bytes, to_slice}; +use rand::{Rng, SeedableRng}; +use wasmtime_fuzzing::generators::table_ops::TableOps; +use wasmtime_fuzzing::oracles::table_ops; + +fuzz_target!(|data: &[u8]| { + let Ok((seed, ops)) = postcard::from_bytes::<(u64, TableOps)>(data) else { + return; + }; + + let mut buf = [0u8; 1024]; + let mut rng = rand::rngs::StdRng::seed_from_u64(seed); + rng.fill(&mut buf); + + let u = Unstructured::new(&buf); + let Ok(config) = wasmtime_fuzzing::generators::Config::arbitrary_take_rest(u) else { + return; + }; + + let _ = table_ops(config, ops); +}); + +fuzz_mutator!(|data: &mut [u8], size: usize, max_size: usize, seed: u32| { + let _ = env_logger::try_init(); + + // With probability of about 1/8, use default mutator + if seed.count_ones() % 8 == 0 { + return fuzzer_mutate(data, size, max_size); + } + + // Try to decode using postcard; fallback to default input on failure + let mut tuple: (u64, TableOps) = from_bytes(&data[..size]).ok().unwrap_or_default(); + + let mut session = Session::new().seed(seed.into()).shrink(max_size < size); + + if session.mutate(&mut tuple).is_ok() { + loop { + if let Ok(encoded) = to_slice(&tuple, data) { + return encoded.len(); + } + + // Attempt to shrink ops if encoding fails (e.g., buffer too small) + if tuple.1.pop() { + continue; + } + + break; + } + } + + // Fallback to default libfuzzer mutator + fuzzer_mutate(data, size, max_size) +}); diff --git a/scripts/publish.rs b/scripts/publish.rs index f3ea0247d8..230039fded 100644 --- a/scripts/publish.rs +++ b/scripts/publish.rs @@ -550,6 +550,8 @@ fn verify(crates: &[Crate]) { } fn verify_and_vendor(krate: &Crate) { + verify_crates_io(krate); + let mut cmd = Command::new("cargo"); cmd.arg("package") .arg("--manifest-path") @@ -607,4 +609,30 @@ fn verify(crates: &[Crate]) { version ); } + + fn verify_crates_io(krate: &Crate) { + let name = &krate.name; + let Some(owners) = curl(&format!("https://crates.io/api/v1/crates/{name}/owners")) else { + panic!("failed to get owners for {name}", name = name); + }; + + let assert_owner = |owner: &str| { + let owner_json = format!("\"{owner}\""); + if !owners.contains(&owner_json) { + panic!( + " +crate {name} is not owned by {owner}, please run: + + cargo owner -a {owner} {name} +", + name = name + ); + } + }; + + // the wasmtime-publish github user + assert_owner("wasmtime-publish"); + // the BA team which can publish crates + assert_owner("github:bytecodealliance:wasmtime-publish"); + } } diff --git a/src/commands/run.rs b/src/commands/run.rs index 8c7ac2b6bf..44e1ea8bf5 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -13,10 +13,10 @@ use std::ffi::OsString; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use std::thread; -use tokio::io::{stderr, stdin, stdout}; use wasi_common::sync::{Dir, TcpListener, WasiCtxBuilder, ambient_authority}; use wasmtime::{Engine, Func, Module, Store, StoreLimits, Val, ValType}; -use wasmtime_wasi::p2::{IoView, WasiView}; +use wasmtime_wasi::p2::{self, IoView as _}; +use wasmtime_wasi::p3; #[cfg(feature = "wasi-nn")] use wasmtime_wasi_nn::wit::WasiNnView; @@ -1080,73 +1080,10 @@ impl RunCommand { } fn set_p3_ctx(&self, store: &mut Store) -> Result<()> { - store.data_mut().p3_ctx = Some(Arc::default()); - - let mut environment = Vec::default(); - if self.run.common.wasi.inherit_env == Some(true) { - for (k, v) in std::env::vars() { - environment.push((k, v)); - } - } - for (key, value) in self.run.vars.iter() { - let value = match value { - Some(value) => value.clone(), - None => match std::env::var_os(key) { - Some(val) => val - .into_string() - .map_err(|_| anyhow!("environment variable `{key}` not valid utf-8"))?, - None => { - // leave the env var un-set in the guest - continue; - } - }, - }; - environment.push((key.clone(), value)); - } - let arguments = self.compute_argv()?; - store.data_mut().p3_cli = Some(Arc::new(Mutex::new(wasmtime_wasi::p3::cli::WasiCliCtx { - environment, - arguments, - initial_cwd: None, - stdin: Box::new(stdin()), - stdout: Box::new(stdout()), - stderr: Box::new(stderr()), - }))); - - let mut p3_filesystem = wasmtime_wasi::p3::filesystem::WasiFilesystemCtx::default(); - p3_filesystem.allow_blocking_current_thread = self.run.common.wasm.timeout.is_none(); - for (host, guest) in self.run.dirs.iter() { - p3_filesystem.preopened_dir( - host, - guest, - wasmtime_wasi::DirPerms::all(), - wasmtime_wasi::FilePerms::all(), - )?; - } - store.data_mut().p3_filesystem = Some(p3_filesystem); - - if self.run.common.wasi.listenfd == Some(true) { - bail!("components do not support --listenfd"); - } - for _ in self.run.compute_preopen_sockets()? { - bail!("components do not support --tcplisten"); - } - - let mut p3_sockets = wasmtime_wasi::p3::sockets::WasiSocketsCtx::default(); - if self.run.common.wasi.inherit_network == Some(true) { - p3_sockets.socket_addr_check = - wasmtime_wasi::p3::sockets::SocketAddrCheck::new(|_, _| Box::pin(async { true })) - } - if let Some(enable) = self.run.common.wasi.allow_ip_name_lookup { - p3_sockets.allowed_network_uses.ip_name_lookup = enable; - } - if let Some(enable) = self.run.common.wasi.tcp { - p3_sockets.allowed_network_uses.tcp = enable; - } - if let Some(enable) = self.run.common.wasi.udp { - p3_sockets.allowed_network_uses.udp = enable; - } - store.data_mut().p3_sockets = Some(p3_sockets); + let mut builder = wasmtime_wasi::p3::WasiCtxBuilder::new(); + builder.inherit_stdio().args(&self.compute_argv()?); + self.run.configure_wasip3(&mut builder)?; + store.data_mut().p3_ctx = Some(Arc::new(Mutex::new(builder.build()))); Ok(()) } @@ -1176,9 +1113,7 @@ struct Host { // access. preview2_ctx: Option>>, - p3_cli: Option>>, p3_filesystem: Option, - p3_sockets: Option, p3_ctx: Option>>, #[cfg(feature = "wasi-http")] p3_http: Option, @@ -1219,48 +1154,49 @@ impl Host { .get_mut() .unwrap() } - - fn p3_ctx(&mut self) -> &mut wasmtime_wasi::p3::WasiCtx { - let ctx = self.p3_ctx.as_mut().expect("wasip3 is not configured"); - Arc::get_mut(ctx) - .expect("wasmtime_wasi is not compatible with threads") - .get_mut() - .unwrap() - } } -impl IoView for Host { +impl p2::IoView for Host { fn table(&mut self) -> &mut wasmtime::component::ResourceTable { self.preview2_ctx().table() } } -impl WasiView for Host { - fn ctx(&mut self) -> &mut wasmtime_wasi::p2::WasiCtx { +impl p2::WasiView for Host { + fn ctx(&mut self) -> &mut p2::WasiCtx { self.preview2_ctx().ctx() } } -impl wasmtime_wasi::p3::ResourceView for Host { - fn table(&mut self) -> &mut wasmtime::component::ResourceTable { - self.preview2_ctx().table() - } -} -impl wasmtime_wasi::p3::WasiView for Host { - fn ctx(&mut self) -> &mut wasmtime_wasi::p3::WasiCtx { - self.p3_ctx() +impl p3::WasiView for Host { + fn ctx(&mut self) -> p3::WasiCtxView<'_> { + p3::WasiCtxView { + ctx: { + let ctx = self.p3_ctx.as_mut().expect("wasip3 is not configured"); + Arc::get_mut(ctx) + .expect("wasmtime_wasi is not compatible with threads") + .get_mut() + .unwrap() + }, + table: { + let ctx = self + .preview2_ctx + .as_mut() + .expect("wasip2 is not configured"); + Arc::get_mut(ctx) + .expect("wasmtime_wasi is not compatible with threads") + .get_mut() + .unwrap() + .table() + }, + } } } -impl wasmtime_wasi::p3::cli::WasiCliView for Host { - fn cli(&mut self) -> &wasmtime_wasi::p3::cli::WasiCliCtx { - let cli = self - .p3_cli - .as_mut() - .and_then(Arc::get_mut) - .expect("`wasi:cli@0.3` not configured"); - cli.get_mut().unwrap() + +impl p3::ResourceView for Host { + fn table(&mut self) -> &mut wasmtime::component::ResourceTable { + self.preview2_ctx().table() } } - -impl wasmtime_wasi::p3::filesystem::WasiFilesystemView for Host { +impl p3::filesystem::WasiFilesystemView for Host { fn filesystem(&self) -> &wasmtime_wasi::p3::filesystem::WasiFilesystemCtx { self.p3_filesystem .as_ref() @@ -1268,14 +1204,6 @@ impl wasmtime_wasi::p3::filesystem::WasiFilesystemView for Host { } } -impl wasmtime_wasi::p3::sockets::WasiSocketsView for Host { - fn sockets(&self) -> &wasmtime_wasi::p3::sockets::WasiSocketsCtx { - self.p3_sockets - .as_ref() - .expect("`wasi:sockets@0.3` not configured") - } -} - #[cfg(feature = "wasi-http")] impl wasmtime_wasi_http::types::WasiHttpView for Host { fn ctx(&mut self) -> &mut WasiHttpCtx { diff --git a/src/commands/serve.rs b/src/commands/serve.rs index f3b95b9908..15a707add7 100644 --- a/src/commands/serve.rs +++ b/src/commands/serve.rs @@ -11,14 +11,13 @@ use std::path::PathBuf; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; -use tokio::io::{stderr, stdin, stdout}; use tokio::sync::Notify; use wasmtime::component::{Component, Instance, InstancePre, Linker}; use wasmtime::{Engine, Store, StoreLimits, UpdateDeadline}; use wasmtime_wasi::p2::{IoView, StreamError, StreamResult, WasiCtx, WasiCtxBuilder, WasiView}; +use wasmtime_wasi::p3; use wasmtime_wasi_http::bindings as p2; use wasmtime_wasi_http::io::TokioIo; -use wasmtime_wasi_http::p3::bindings as p3; use wasmtime_wasi_http::{ DEFAULT_OUTGOING_BODY_BUFFER_CHUNKS, DEFAULT_OUTGOING_BODY_CHUNK_SIZE, WasiHttpCtx, WasiHttpView, @@ -38,12 +37,9 @@ struct Host { http_outgoing_body_buffer_chunks: Option, http_outgoing_body_chunk_size: Option, - p3_cli: Option>>, - p3_clocks: Option>>, p3_filesystem: Option, - p3_random: Option>>, - p3_sockets: Option, p3_http: wasmtime_wasi_http::p3::WasiHttpCtx, + p3_ctx: wasmtime_wasi::p3::WasiCtx, limits: StoreLimits, @@ -92,25 +88,13 @@ impl wasmtime_wasi::p3::ResourceView for Host { &mut self.table } } -impl wasmtime_wasi::p3::cli::WasiCliView for Host { - fn cli(&mut self) -> &wasmtime_wasi::p3::cli::WasiCliCtx { - let cli = self - .p3_cli - .as_mut() - .and_then(Arc::get_mut) - .expect("`wasi:cli@0.3` not configured"); - cli.get_mut().unwrap() - } -} -impl wasmtime_wasi::clocks::WasiClocksView for Host { - fn clocks(&mut self) -> &wasmtime_wasi::clocks::WasiClocksCtx { - let clocks = self - .p3_clocks - .as_mut() - .and_then(Arc::get_mut) - .expect("`wasi:clocks@0.3` not configured"); - clocks.get_mut().unwrap() +impl wasmtime_wasi::p3::WasiView for Host { + fn ctx(&mut self) -> wasmtime_wasi::p3::WasiCtxView<'_> { + wasmtime_wasi::p3::WasiCtxView { + ctx: &mut self.p3_ctx, + table: &mut self.table, + } } } @@ -122,25 +106,6 @@ impl wasmtime_wasi::p3::filesystem::WasiFilesystemView for Host { } } -impl wasmtime_wasi::random::WasiRandomView for Host { - fn random(&mut self) -> &mut wasmtime_wasi::random::WasiRandomCtx { - let random = self - .p3_random - .as_mut() - .and_then(Arc::get_mut) - .expect("`wasi:random@0.3` not configured"); - random.get_mut().unwrap() - } -} - -impl wasmtime_wasi::p3::sockets::WasiSocketsView for Host { - fn sockets(&self) -> &wasmtime_wasi::p3::sockets::WasiSocketsCtx { - self.p3_sockets - .as_ref() - .expect("`wasi:sockets@0.3` not configured") - } -} - impl wasmtime_wasi_http::p3::WasiHttpView for Host { type Client = wasmtime_wasi_http::p3::DefaultClient; @@ -219,83 +184,14 @@ impl ServeCommand { Ok(()) } - fn set_p3_ctx(&self, store: &mut Store) -> Result<()> { - store.data_mut().p3_clocks = Some(Arc::default()); - store.data_mut().p3_random = Some(Arc::default()); - - let mut environment = Vec::default(); - if self.run.common.wasi.inherit_env == Some(true) { - for (k, v) in std::env::vars() { - environment.push((k, v)); - } - } - for (key, value) in self.run.vars.iter() { - let value = match value { - Some(value) => value.clone(), - None => match std::env::var_os(key) { - Some(val) => val - .into_string() - .map_err(|_| anyhow!("environment variable `{key}` not valid utf-8"))?, - None => { - // leave the env var un-set in the guest - continue; - } - }, - }; - environment.push((key.clone(), value)); - } - store.data_mut().p3_cli = Some(Arc::new(Mutex::new(wasmtime_wasi::p3::cli::WasiCliCtx { - environment, - arguments: vec![], - initial_cwd: None, - stdin: Box::new(stdin()), - stdout: Box::new(stdout()), - stderr: Box::new(stderr()), - }))); - - let mut p3_filesystem = wasmtime_wasi::p3::filesystem::WasiFilesystemCtx::default(); - p3_filesystem.allow_blocking_current_thread = self.run.common.wasm.timeout.is_none(); - for (host, guest) in self.run.dirs.iter() { - p3_filesystem.preopened_dir( - host, - guest, - wasmtime_wasi::DirPerms::all(), - wasmtime_wasi::FilePerms::all(), - )?; - } - store.data_mut().p3_filesystem = Some(p3_filesystem); - - if self.run.common.wasi.listenfd == Some(true) { - bail!("components do not support --listenfd"); - } - for _ in self.run.compute_preopen_sockets()? { - bail!("components do not support --tcplisten"); - } - - let mut p3_sockets = wasmtime_wasi::p3::sockets::WasiSocketsCtx::default(); - if self.run.common.wasi.inherit_network == Some(true) { - p3_sockets.socket_addr_check = - wasmtime_wasi::p3::sockets::SocketAddrCheck::new(|_, _| Box::pin(async { true })) - } - if let Some(enable) = self.run.common.wasi.allow_ip_name_lookup { - p3_sockets.allowed_network_uses.ip_name_lookup = enable; - } - if let Some(enable) = self.run.common.wasi.tcp { - p3_sockets.allowed_network_uses.tcp = enable; - } - if let Some(enable) = self.run.common.wasi.udp { - p3_sockets.allowed_network_uses.udp = enable; - } - store.data_mut().p3_sockets = Some(p3_sockets); - - Ok(()) - } - fn new_store(&self, engine: &Engine, req_id: u64) -> Result> { - let mut builder = WasiCtxBuilder::new(); - self.run.configure_wasip2(&mut builder)?; + let mut p2 = WasiCtxBuilder::new(); + let mut p3 = p3::WasiCtxBuilder::new(); + self.run.configure_wasip2(&mut p2)?; + self.run.configure_wasip3(&mut p3)?; - builder.env("REQUEST_ID", req_id.to_string()); + p2.env("REQUEST_ID", req_id.to_string()); + p3.env("REQUEST_ID", req_id.to_string()); let stdout_prefix: String; let stderr_prefix: String; @@ -306,12 +202,15 @@ impl ServeCommand { stdout_prefix = format!("stdout [{req_id}] :: "); stderr_prefix = format!("stderr [{req_id}] :: "); } - builder.stdout(LogStream::new(stdout_prefix, Output::Stdout)); - builder.stderr(LogStream::new(stderr_prefix, Output::Stderr)); + p2.stdout(LogStream::new(stdout_prefix, Output::Stdout)); + p2.stderr(LogStream::new(stderr_prefix, Output::Stderr)); + + // TODO: set stdout/stderr for p3 + p3.inherit_stdout().inherit_stderr(); let mut host = Host { table: wasmtime::component::ResourceTable::new(), - ctx: builder.build(), + ctx: p2.build(), http: WasiHttpCtx::new(), http_outgoing_body_buffer_chunks: self.run.common.wasi.http_outgoing_body_buffer_chunks, http_outgoing_body_chunk_size: self.run.common.wasi.http_outgoing_body_chunk_size, @@ -327,11 +226,21 @@ impl ServeCommand { #[cfg(feature = "profiling")] guest_profiler: None, - p3_cli: None, - p3_clocks: None, - p3_filesystem: None, - p3_random: None, - p3_sockets: None, + p3_ctx: p3.build(), + p3_filesystem: { + let mut p3_filesystem = p3::filesystem::WasiFilesystemCtx::default(); + p3_filesystem.allow_blocking_current_thread = + self.run.common.wasm.timeout.is_none(); + for (host, guest) in self.run.dirs.iter() { + p3_filesystem.preopened_dir( + host, + guest, + wasmtime_wasi::DirPerms::all(), + wasmtime_wasi::FilePerms::all(), + )?; + } + Some(p3_filesystem) + }, p3_http: wasmtime_wasi_http::p3::WasiHttpCtx::default(), }; @@ -384,7 +293,6 @@ impl ServeCommand { } let mut store = Store::new(engine, host); - self.set_p3_ctx(&mut store)?; store.data_mut().limits = self.run.store_limits(); store.limiter(|t| &mut t.limits); @@ -428,6 +336,7 @@ impl ServeCommand { wasmtime_wasi_http::add_only_http_to_linker_async(linker)?; wasmtime_wasi_http::p3::add_only_http_to_linker(linker)?; } else { + wasmtime_wasi::p2::add_to_linker_with_options_async(linker, &Default::default())?; wasmtime_wasi_http::add_to_linker_async(linker)?; wasmtime_wasi_http::p3::add_to_linker(linker)?; } @@ -519,7 +428,7 @@ impl ServeCommand { }; let instance = linker.instantiate_pre(&component)?; - let instance = match p3::ProxyIndices::new(&instance) { + let instance = match wasmtime_wasi_http::p3::bindings::ProxyIndices::new(&instance) { Ok(indices) => ProxyPre::P3(indices, instance), Err(_) => ProxyPre::P2(p2::ProxyPre::new(instance)?), }; @@ -861,7 +770,10 @@ struct ProxyHandlerInner { enum ProxyPre { P2(p2::ProxyPre), - P3(p3::ProxyIndices, InstancePre), + P3( + wasmtime_wasi_http::p3::bindings::ProxyIndices, + InstancePre, + ), } impl ProxyPre { @@ -879,7 +791,7 @@ impl ProxyPre { enum Proxy { P2(p2::Proxy), - P3(p3::Proxy, Instance), + P3(wasmtime_wasi_http::p3::bindings::Proxy, Instance), } impl ProxyHandlerInner { @@ -974,6 +886,8 @@ async fn handle_request( use std::future; use std::pin::pin; use std::task::Poll; + use wasmtime_wasi_http::p3; + use wasmtime_wasi_http::p3::bindings::http::types::ErrorCode; let (tx, rx) = tokio::sync::oneshot::channel(); @@ -981,14 +895,12 @@ async fn handle_request( instance .run_concurrent(&mut store, async move |store| { let (req, body) = req.into_parts(); - let body = - body.map_err(p3::http::types::ErrorCode::from_hyper_request_error); + let body = body.map_err(ErrorCode::from_hyper_request_error); let res = proxy .handle(store, http::Request::from_parts(req, body)) .await??; - let (res, io) = store.with(|mut store| { - wasmtime_wasi_http::p3::Response::resource_into_http(&mut store, res) - })?; + let (res, io) = store + .with(|mut store| p3::Response::resource_into_http(&mut store, res))?; // Poll `io` once before handing the response to Hyper. // This ensures that, if the guest has at least one body // chunk ready to send, it will be available to Hyper @@ -1014,7 +926,7 @@ async fn handle_request( anyhow::Ok(()) }); Ok(rx.await?.map(|body| { - body.map_err(|err| err.unwrap_or(p3::http::types::ErrorCode::InternalError(None))) + body.map_err(|err| err.unwrap_or(ErrorCode::InternalError(None))) .map_err(|err| err.into()) .boxed() })) @@ -1067,10 +979,10 @@ impl wasmtime_wasi::p2::StdoutStream for LogStream { fn stream(&self) -> Box { Box::new(self.clone()) } +} - fn isatty(&self) -> bool { - use std::io::IsTerminal; - +impl wasmtime_wasi::cli::IsTerminal for LogStream { + fn is_terminal(&self) -> bool { match &self.output { Output::Stdout => std::io::stdout().is_terminal(), Output::Stderr => std::io::stderr().is_terminal(), diff --git a/src/commands/wast.rs b/src/commands/wast.rs index ca50c14f66..8c25d88622 100644 --- a/src/commands/wast.rs +++ b/src/commands/wast.rs @@ -21,6 +21,16 @@ pub struct WastCommand { /// transformations to show line numbers in backtraces. #[arg(long, require_equals = true, value_name = "true|false")] generate_dwarf: Option>, + + /// Saves precompiled versions of modules to this path instead of running + /// tests. + #[arg(long)] + precompile_save: Option, + + /// Load precompiled modules from the specified directory instead of + /// compiling natively. + #[arg(long)] + precompile_load: Option, } impl WastCommand { @@ -48,10 +58,17 @@ impl WastCommand { }) .expect("error instantiating \"spectest\""); + if let Some(path) = &self.precompile_save { + wast_context.precompile_save(path); + } + if let Some(path) = &self.precompile_load { + wast_context.precompile_load(path); + } + for script in self.scripts.iter() { wast_context .run_file(script) - .with_context(|| format!("failed to run script file '{}'", script.display()))? + .with_context(|| format!("failed to run script file '{}'", script.display()))?; } Ok(()) diff --git a/src/common.rs b/src/common.rs index 385ee1c4e4..52c6b9cea2 100644 --- a/src/common.rs +++ b/src/common.rs @@ -6,8 +6,8 @@ use std::net::TcpListener; use std::{fs::File, path::Path, time::Duration}; use wasmtime::{Engine, Module, Precompiled, StoreLimits, StoreLimitsBuilder}; use wasmtime_cli_flags::{CommonOptions, opt::WasmtimeOptionValue}; -use wasmtime_wasi::p2::WasiCtxBuilder; use wasmtime_wasi::p2::bindings::LinkOptions; +use wasmtime_wasi::{p2, p3}; #[cfg(feature = "component-model")] use wasmtime::component::Component; @@ -258,7 +258,71 @@ impl RunCommon { }) } - pub fn configure_wasip2(&self, builder: &mut WasiCtxBuilder) -> Result<()> { + pub fn configure_wasip2(&self, builder: &mut p2::WasiCtxBuilder) -> Result<()> { + // It's ok to block the current thread since we're the only thread in + // the program as the CLI. This helps improve the performance of some + // blocking operations in WASI, for example, by skipping the + // back-and-forth between sync and async. + // + // However, do not set this if a timeout is configured, as that would + // cause the timeout to be ignored if the guest does, for example, + // something like `sleep(FOREVER)`. + builder.allow_blocking_current_thread(self.common.wasm.timeout.is_none()); + + if self.common.wasi.inherit_env == Some(true) { + for (k, v) in std::env::vars() { + builder.env(&k, &v); + } + } + for (key, value) in self.vars.iter() { + let value = match value { + Some(value) => value.clone(), + None => match std::env::var_os(key) { + Some(val) => val + .into_string() + .map_err(|_| anyhow!("environment variable `{key}` not valid utf-8"))?, + None => { + // leave the env var un-set in the guest + continue; + } + }, + }; + builder.env(key, &value); + } + + for (host, guest) in self.dirs.iter() { + builder.preopened_dir( + host, + guest, + wasmtime_wasi::DirPerms::all(), + wasmtime_wasi::FilePerms::all(), + )?; + } + + if self.common.wasi.listenfd == Some(true) { + bail!("components do not support --listenfd"); + } + for _ in self.compute_preopen_sockets()? { + bail!("components do not support --tcplisten"); + } + + if self.common.wasi.inherit_network == Some(true) { + builder.inherit_network(); + } + if let Some(enable) = self.common.wasi.allow_ip_name_lookup { + builder.allow_ip_name_lookup(enable); + } + if let Some(enable) = self.common.wasi.tcp { + builder.allow_tcp(enable); + } + if let Some(enable) = self.common.wasi.udp { + builder.allow_udp(enable); + } + + Ok(()) + } + + pub fn configure_wasip3(&self, builder: &mut p3::WasiCtxBuilder) -> Result<()> { // It's ok to block the current thread since we're the only thread in // the program as the CLI. This helps improve the performance of some // blocking operations in WASI, for example, by skipping the diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index 2f1af77fc6..2379b1c144 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -184,6 +184,16 @@ start = "2020-01-14" end = "2025-07-30" notes = "I am an author of this crate" +[[wildcard-audits.json-from-wast]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-28" +end = "2026-07-28" +notes = """ +The Bytecode Alliance is the author of this crate. +""" + [[wildcard-audits.mutatis]] who = "Nick Fitzgerald " criteria = "safe-to-deploy" @@ -247,6 +257,16 @@ start = "2021-10-29" end = "2025-07-30" notes = "The Bytecode Alliance is the author of this crate." +[[wildcard-audits.wasm-compose]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-28" +end = "2026-07-28" +notes = """ +The Bytecode Alliance is the author of this crate +""" + [[wildcard-audits.wasm-encoder]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -499,6 +519,182 @@ start = "2021-10-29" end = "2025-07-30" notes = "The Bytecode Alliance is the author of this crate." +[[wildcard-audits.wasmtime-internal-asm-macros]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-c-api-macros]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 1 # Alex Crichton (alexcrichton) +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-c-api-macros]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-cache]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 1 # Alex Crichton (alexcrichton) +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-cache]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-component-macro]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-component-util]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-cranelift]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 1 # Alex Crichton (alexcrichton) +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-cranelift]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-explorer]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 1 # Alex Crichton (alexcrichton) +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-explorer]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-fiber]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-jit-debug]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-jit-icache-coherence]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-math]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-slab]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-unwinder]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-versioned-export-macros]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-winch]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 1 # Alex Crichton (alexcrichton) +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-winch]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-wit-bindgen]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-internal-wmemcheck]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + [[wildcard-audits.wasmtime-jit]] who = "Bobby Holley " criteria = "safe-to-deploy" @@ -639,6 +835,22 @@ start = "2025-04-21" end = "2026-04-21" notes = "The Bytecode Alliance is the author of this crate." +[[wildcard-audits.wasmtime-wasi-tls-nativetls]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 1 # Alex Crichton (alexcrichton) +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + +[[wildcard-audits.wasmtime-wasi-tls-nativetls]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2025-07-20" +end = "2026-07-20" +notes = "The Bytecode Alliance is the author of this crate." + [[wildcard-audits.wasmtime-wast]] who = "Bobby Holley " criteria = "safe-to-deploy" @@ -4932,6 +5144,12 @@ user-id = 1 # Alex Crichton (alexcrichton) start = "2019-03-04" end = "2025-12-05" +[[trusted.wasm-compose]] +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2024-02-15" +end = "2026-07-28" + [[trusted.wasm-encoder]] criteria = "safe-to-deploy" user-id = 73222 # wasmtime-publish diff --git a/supply-chain/config.toml b/supply-chain/config.toml index d9e3de25fb..b74590ce3f 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -127,6 +127,57 @@ criteria = [] [policy.wasmtime-fuzzing] criteria = [] +[policy.wasmtime-internal-asm-macros] +audit-as-crates-io = true + +[policy.wasmtime-internal-c-api-macros] +audit-as-crates-io = true + +[policy.wasmtime-internal-cache] +audit-as-crates-io = true + +[policy.wasmtime-internal-component-macro] +audit-as-crates-io = true + +[policy.wasmtime-internal-component-util] +audit-as-crates-io = true + +[policy.wasmtime-internal-cranelift] +audit-as-crates-io = true + +[policy.wasmtime-internal-explorer] +audit-as-crates-io = true + +[policy.wasmtime-internal-fiber] +audit-as-crates-io = true + +[policy.wasmtime-internal-jit-debug] +audit-as-crates-io = true + +[policy.wasmtime-internal-jit-icache-coherence] +audit-as-crates-io = true + +[policy.wasmtime-internal-math] +audit-as-crates-io = true + +[policy.wasmtime-internal-slab] +audit-as-crates-io = true + +[policy.wasmtime-internal-unwinder] +audit-as-crates-io = true + +[policy.wasmtime-internal-versioned-export-macros] +audit-as-crates-io = true + +[policy.wasmtime-internal-winch] +audit-as-crates-io = true + +[policy.wasmtime-internal-wit-bindgen] +audit-as-crates-io = true + +[policy.wasmtime-internal-wmemcheck] +audit-as-crates-io = true + [policy.wasmtime-wasi] audit-as-crates-io = true @@ -151,6 +202,9 @@ audit-as-crates-io = true [policy.wasmtime-wasi-tls] audit-as-crates-io = true +[policy.wasmtime-wasi-tls-nativetls] +audit-as-crates-io = true + [policy.wasmtime-wast] audit-as-crates-io = true diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 17fa274e6e..0d162f6865 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -377,6 +377,74 @@ audited_as = "33.0.0" version = "35.0.0" audited_as = "33.0.0" +[[unpublished.wasmtime-internal-asm-macros]] +version = "36.0.0" +audited_as = "35.0.0" + +[[unpublished.wasmtime-internal-c-api-macros]] +version = "36.0.0" +audited_as = "35.0.0" + +[[unpublished.wasmtime-internal-cache]] +version = "36.0.0" +audited_as = "35.0.0" + +[[unpublished.wasmtime-internal-component-macro]] +version = "36.0.0" +audited_as = "35.0.0" + +[[unpublished.wasmtime-internal-component-util]] +version = "36.0.0" +audited_as = "35.0.0" + +[[unpublished.wasmtime-internal-cranelift]] +version = "36.0.0" +audited_as = "35.0.0" + +[[unpublished.wasmtime-internal-explorer]] +version = "36.0.0" +audited_as = "35.0.0" + +[[unpublished.wasmtime-internal-fiber]] +version = "36.0.0" +audited_as = "35.0.0" + +[[unpublished.wasmtime-internal-jit-debug]] +version = "36.0.0" +audited_as = "35.0.0" + +[[unpublished.wasmtime-internal-jit-icache-coherence]] +version = "36.0.0" +audited_as = "35.0.0" + +[[unpublished.wasmtime-internal-math]] +version = "36.0.0" +audited_as = "35.0.0" + +[[unpublished.wasmtime-internal-slab]] +version = "36.0.0" +audited_as = "35.0.0" + +[[unpublished.wasmtime-internal-unwinder]] +version = "36.0.0" +audited_as = "35.0.0" + +[[unpublished.wasmtime-internal-versioned-export-macros]] +version = "36.0.0" +audited_as = "35.0.0" + +[[unpublished.wasmtime-internal-winch]] +version = "36.0.0" +audited_as = "35.0.0" + +[[unpublished.wasmtime-internal-wit-bindgen]] +version = "36.0.0" +audited_as = "35.0.0" + +[[unpublished.wasmtime-internal-wmemcheck]] +version = "36.0.0" +audited_as = "35.0.0" + [[unpublished.wasmtime-jit-debug]] version = "34.0.0" audited_as = "33.0.0" @@ -505,6 +573,10 @@ audited_as = "33.0.0" version = "36.0.0" audited_as = "34.0.1" +[[unpublished.wasmtime-wasi-tls-nativetls]] +version = "36.0.0" +audited_as = "35.0.0" + [[unpublished.wasmtime-wast]] version = "34.0.0" audited_as = "33.0.0" @@ -1062,6 +1134,12 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.json-from-wast]] +version = "0.236.0" +when = "2025-07-28" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.libc]] version = "0.2.146" when = "2023-06-06" @@ -1437,9 +1515,9 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" -[[publisher.wasm-encoder]] -version = "0.230.0" -when = "2025-05-05" +[[publisher.wasm-compose]] +version = "0.236.0" +when = "2025-07-28" user-id = 73222 user-login = "wasmtime-publish" @@ -1449,9 +1527,9 @@ when = "2025-06-13" user-id = 73222 user-login = "wasmtime-publish" -[[publisher.wasm-metadata]] -version = "0.230.0" -when = "2025-05-05" +[[publisher.wasm-encoder]] +version = "0.236.0" +when = "2025-07-28" user-id = 73222 user-login = "wasmtime-publish" @@ -1461,15 +1539,15 @@ when = "2025-06-13" user-id = 73222 user-login = "wasmtime-publish" -[[publisher.wasm-wave]] -version = "0.235.0" -when = "2025-06-13" +[[publisher.wasm-metadata]] +version = "0.236.0" +when = "2025-07-28" user-id = 73222 user-login = "wasmtime-publish" -[[publisher.wasmparser]] -version = "0.230.0" -when = "2025-05-05" +[[publisher.wasm-wave]] +version = "0.236.0" +when = "2025-07-28" user-id = 73222 user-login = "wasmtime-publish" @@ -1479,9 +1557,15 @@ when = "2025-06-13" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmparser]] +version = "0.236.0" +when = "2025-07-28" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmprinter]] -version = "0.235.0" -when = "2025-06-13" +version = "0.236.0" +when = "2025-07-28" user-id = 73222 user-login = "wasmtime-publish" @@ -1509,6 +1593,113 @@ when = "2025-06-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-internal-asm-macros]] +version = "35.0.0" +when = "2025-07-22" +user-id = 73222 +user-login = "wasmtime-publish" + +[[publisher.wasmtime-internal-c-api-macros]] +version = "35.0.0" +when = "2025-07-22" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + +[[publisher.wasmtime-internal-cache]] +version = "35.0.0" +when = "2025-07-22" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + +[[publisher.wasmtime-internal-component-macro]] +version = "35.0.0" +when = "2025-07-22" +user-id = 73222 +user-login = "wasmtime-publish" + +[[publisher.wasmtime-internal-component-util]] +version = "35.0.0" +when = "2025-07-22" +user-id = 73222 +user-login = "wasmtime-publish" + +[[publisher.wasmtime-internal-cranelift]] +version = "35.0.0" +when = "2025-07-22" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + +[[publisher.wasmtime-internal-explorer]] +version = "35.0.0" +when = "2025-07-22" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + +[[publisher.wasmtime-internal-fiber]] +version = "35.0.0" +when = "2025-07-22" +user-id = 73222 +user-login = "wasmtime-publish" + +[[publisher.wasmtime-internal-jit-debug]] +version = "35.0.0" +when = "2025-07-22" +user-id = 73222 +user-login = "wasmtime-publish" + +[[publisher.wasmtime-internal-jit-icache-coherence]] +version = "35.0.0" +when = "2025-07-22" +user-id = 73222 +user-login = "wasmtime-publish" + +[[publisher.wasmtime-internal-math]] +version = "35.0.0" +when = "2025-07-22" +user-id = 73222 +user-login = "wasmtime-publish" + +[[publisher.wasmtime-internal-slab]] +version = "35.0.0" +when = "2025-07-22" +user-id = 73222 +user-login = "wasmtime-publish" + +[[publisher.wasmtime-internal-unwinder]] +version = "35.0.0" +when = "2025-07-22" +user-id = 73222 +user-login = "wasmtime-publish" + +[[publisher.wasmtime-internal-versioned-export-macros]] +version = "35.0.0" +when = "2025-07-22" +user-id = 73222 +user-login = "wasmtime-publish" + +[[publisher.wasmtime-internal-winch]] +version = "35.0.0" +when = "2025-07-22" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + +[[publisher.wasmtime-internal-wit-bindgen]] +version = "35.0.0" +when = "2025-07-22" +user-id = 73222 +user-login = "wasmtime-publish" + +[[publisher.wasmtime-internal-wmemcheck]] +version = "35.0.0" +when = "2025-07-22" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-wasi]] version = "34.0.1" when = "2025-06-24" @@ -1557,6 +1748,13 @@ when = "2025-06-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-wasi-tls-nativetls]] +version = "35.0.0" +when = "2025-07-22" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[publisher.wasmtime-wast]] version = "34.0.1" when = "2025-06-24" @@ -1564,14 +1762,14 @@ user-id = 73222 user-login = "wasmtime-publish" [[publisher.wast]] -version = "235.0.0" -when = "2025-06-13" +version = "236.0.0" +when = "2025-07-28" user-id = 73222 user-login = "wasmtime-publish" [[publisher.wat]] -version = "1.235.0" -when = "2025-06-13" +version = "1.236.0" +when = "2025-07-28" user-id = 73222 user-login = "wasmtime-publish" @@ -1845,26 +2043,26 @@ user-id = 73222 user-login = "wasmtime-publish" [[publisher.wit-component]] -version = "0.230.0" -when = "2025-05-05" +version = "0.235.0" +when = "2025-06-13" user-id = 73222 user-login = "wasmtime-publish" [[publisher.wit-component]] -version = "0.235.0" -when = "2025-06-13" +version = "0.236.0" +when = "2025-07-28" user-id = 73222 user-login = "wasmtime-publish" [[publisher.wit-parser]] -version = "0.230.0" -when = "2025-05-05" +version = "0.235.0" +when = "2025-06-13" user-id = 73222 user-login = "wasmtime-publish" [[publisher.wit-parser]] -version = "0.235.0" -when = "2025-06-13" +version = "0.236.0" +when = "2025-07-28" user-id = 73222 user-login = "wasmtime-publish" @@ -1989,6 +2187,18 @@ delta = "0.2.9 -> 0.2.13" notes = "Audited at https://fxrev.dev/946396" aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" +[[audits.google.audits.rand_chacha]] +who = "Android Legacy" +criteria = "safe-to-run" +version = "0.3.1" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.rand_core]] +who = "Android Legacy" +criteria = "safe-to-run" +version = "0.6.4" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/main/cargo-vet/audits.toml?format=TEXT" + [[audits.google.audits.sha1]] who = "David Koloski " criteria = "safe-to-deploy" @@ -2118,16 +2328,36 @@ who = "Brandon Pitman " criteria = "safe-to-deploy" delta = "1.18.0 -> 1.19.0" +[[audits.isrg.audits.rand]] +who = "David Cook " +criteria = "safe-to-deploy" +delta = "0.8.5 -> 0.9.1" + +[[audits.isrg.audits.rand]] +who = "Tim Geoghegan " +criteria = "safe-to-deploy" +delta = "0.9.1 -> 0.9.2" + [[audits.isrg.audits.rand_chacha]] who = "David Cook " criteria = "safe-to-deploy" version = "0.3.1" +[[audits.isrg.audits.rand_chacha]] +who = "David Cook " +criteria = "safe-to-deploy" +delta = "0.3.1 -> 0.9.0" + [[audits.isrg.audits.rand_core]] who = "David Cook " criteria = "safe-to-deploy" version = "0.6.3" +[[audits.isrg.audits.rand_core]] +who = "David Cook " +criteria = "safe-to-deploy" +delta = "0.6.4 -> 0.9.3" + [[audits.isrg.audits.rayon-core]] who = "Brandon Pitman " criteria = "safe-to-deploy" diff --git a/tests/all/component_model/bindgen.rs b/tests/all/component_model/bindgen.rs index 8bb0e7fb3c..e537f6d040 100644 --- a/tests/all/component_model/bindgen.rs +++ b/tests/all/component_model/bindgen.rs @@ -77,14 +77,12 @@ mod no_imports_concurrent { world no-imports { export foo: interface { - foo: func(); + foo: async func(); } - export bar: func(); + export bar: async func(); } ", - async: true, - concurrent_exports: true, }); #[tokio::test] @@ -111,11 +109,11 @@ mod no_imports_concurrent { (with "" (instance (export "task.return" (func $task-return)))) )) - (func $f (export "bar") + (func $f (export "[async]bar") (canon lift (core func $i "bar") async (callback (func $i "callback"))) ) - (instance $i (export "foo" (func $f))) + (instance $i (export "[async]foo" (func $f))) (export "foo" (instance $i)) ) "#, @@ -212,15 +210,12 @@ mod one_import_concurrent { world no-imports { import foo: interface { - foo: func(); + foo: async func(); } - export bar: func(); + export bar: async func(); } ", - async: true, - concurrent_imports: true, - concurrent_exports: true, }); #[tokio::test] @@ -235,7 +230,7 @@ mod one_import_concurrent { r#" (component (import "foo" (instance $foo-instance - (export "foo" (func)) + (export "[async]foo" (func)) )) (core module $libc (memory (export "memory") 1) @@ -252,7 +247,7 @@ mod one_import_concurrent { ) (func (export "callback") (param i32 i32 i32) (result i32) unreachable) ) - (core func $foo (canon lower (func $foo-instance "foo") async (memory $libc-instance "memory"))) + (core func $foo (canon lower (func $foo-instance "[async]foo") async (memory $libc-instance "memory"))) (core func $task-return (canon task.return)) (core instance $i (instantiate $m (with "" (instance @@ -261,11 +256,11 @@ mod one_import_concurrent { )) )) - (func $f (export "bar") + (func $f (export "[async]bar") (canon lift (core func $i "bar") async (callback (func $i "callback"))) ) - (instance $i (export "foo" (func $f))) + (instance $i (export "[async]foo" (func $f))) (export "foo" (instance $i)) ) "#, @@ -280,7 +275,7 @@ mod one_import_concurrent { type Data<'a> = &'a mut MyImports; } - impl foo::HostConcurrent for MyImports { + impl foo::HostWithStore for MyImports { async fn foo(accessor: &Accessor) { accessor.with(|mut view| view.get().hit = true); } @@ -515,7 +510,8 @@ mod async_config { export z: func(); } ", - async: true, + imports: { default: async }, + exports: { default: async }, }); #[expect(dead_code, reason = "just here for bindings")] @@ -541,9 +537,11 @@ mod async_config { export z: func(); } ", - async: { - except_imports: ["x"], + imports: { + "x": tracing, + default: async, }, + exports: { default: async }, }); impl T2Imports for T { @@ -566,9 +564,8 @@ mod async_config { export z: func(); } ", - async: { - only_imports: ["x"], - }, + imports: { "x": async }, + exports: { default: async }, }); impl T3Imports for T { diff --git a/tests/all/component_model/bindgen/results.rs b/tests/all/component_model/bindgen/results.rs index c74be163e6..09353d3714 100644 --- a/tests/all/component_model/bindgen/results.rs +++ b/tests/all/component_model/bindgen/results.rs @@ -19,7 +19,7 @@ mod empty_error { export empty-error: func(a: f64) -> result; }", - trappable_imports: true, + imports: {default: trappable}, }); #[test] @@ -123,7 +123,7 @@ mod string_error { export string-error: func(a: f64) -> result; }", - trappable_imports: true, + imports: { default: trappable }, }); #[test] @@ -246,7 +246,7 @@ mod enum_error { } }", trappable_error_type: { "inline:inline/imports/e1" => TrappableE1 }, - trappable_imports: true, + imports: { default: trappable }, }); // You can create concrete trap types which make it all the way out to the @@ -428,7 +428,7 @@ mod record_error { } }", trappable_error_type: { "inline:inline/imports/e2" => TrappableE2 }, - trappable_imports: true, + imports: { default: trappable }, }); pub enum TrappableE2 { @@ -601,7 +601,7 @@ mod variant_error { } }", trappable_error_type: { "inline:inline/imports/e3" => TrappableE3 }, - trappable_imports: true, + imports: { default: trappable }, }); pub enum TrappableE3 { @@ -798,7 +798,7 @@ mod multiple_interfaces_error { } }", trappable_error_type: { "inline:inline/types/e1" => TrappableE1 }, - trappable_imports: true, + imports: { default: trappable }, }); pub enum TrappableE1 { @@ -982,7 +982,7 @@ mod with_remapping { import imports: interface { empty-error: func(a: f64) -> result; }", - trappable_imports: true, + imports: { default: trappable }, }); } @@ -999,7 +999,7 @@ mod with_remapping { with: { "imports": interfaces::imports, }, - trappable_imports: true, + imports: { default: trappable }, }); #[test] diff --git a/tests/all/component_model/func.rs b/tests/all/component_model/func.rs index ad5626dcb1..a74dc929f5 100644 --- a/tests/all/component_model/func.rs +++ b/tests/all/component_model/func.rs @@ -1247,19 +1247,17 @@ async fn test_many_parameters(dynamic: bool, concurrent: bool) -> Result<()> { ]; let func = instance.get_func(&mut store, "many-param").unwrap(); - let mut results = if concurrent { + let mut results = vec![Val::Bool(false)]; + if concurrent { instance .run_concurrent(&mut store, async |store| { - func.call_concurrent(store, input).await + func.call_concurrent(store, &input, &mut results).await }) - .await?? - .into_iter() + .await??; } else { - let mut results = vec![Val::Bool(false)]; func.call_async(&mut store, &input, &mut results).await?; - results.into_iter() }; - + let mut results = results.into_iter(); let Some(Val::Tuple(results)) = results.next() else { panic!() }; @@ -1682,18 +1680,17 @@ async fn test_many_results(dynamic: bool, concurrent: bool) -> Result<()> { let actual = if dynamic { let func = instance.get_func(&mut store, "many-results").unwrap(); - let mut results = if concurrent { + let mut results = vec![Val::Bool(false)]; + if concurrent { instance .run_concurrent(&mut store, async |store| { - func.call_concurrent(store, Vec::new()).await + func.call_concurrent(store, &[], &mut results).await }) - .await?? - .into_iter() + .await??; } else { - let mut results = vec![Val::Bool(false)]; func.call_async(&mut store, &[], &mut results).await?; - results.into_iter() }; + let mut results = results.into_iter(); let Some(Val::Tuple(results)) = results.next() else { panic!() diff --git a/tests/all/component_model/import.rs b/tests/all/component_model/import.rs index 9498aeadf4..0b3e62948c 100644 --- a/tests/all/component_model/import.rs +++ b/tests/all/component_model/import.rs @@ -959,7 +959,7 @@ async fn test_stack_and_heap_args_and_rets(concurrent: bool) -> Result<()> { if concurrent { instance .run_concurrent(&mut store, async |store| { - run.call_concurrent(store, Vec::new()).await + run.call_concurrent(store, &[], &mut []).await }) .await??; } else { diff --git a/tests/all/gc.rs b/tests/all/gc.rs index f71593efaf..593b3579b2 100644 --- a/tests/all/gc.rs +++ b/tests/all/gc.rs @@ -818,8 +818,8 @@ fn to_raw_from_raw_doesnt_leak() -> Result<()> { { let mut scope = RootScope::new(&mut store); let x = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?; - let raw = unsafe { x.to_raw(&mut scope)? }; - let _x = unsafe { ExternRef::from_raw(&mut scope, raw) }; + let raw = x.to_raw(&mut scope)?; + let _x = ExternRef::from_raw(&mut scope, raw); } store.gc(None); diff --git a/tests/all/i31ref.rs b/tests/all/i31ref.rs index ba7f94787d..e49dfec536 100644 --- a/tests/all/i31ref.rs +++ b/tests/all/i31ref.rs @@ -33,8 +33,8 @@ fn i31ref_to_raw_round_trip() -> Result<()> { // back again even though we have not forced the allocation of the `GcStore` // yet. let anyref = AnyRef::from_i31(&mut store, I31::wrapping_u32(42)); - let raw = unsafe { anyref.to_raw(&mut store)? }; - let anyref = unsafe { AnyRef::from_raw(&mut store, raw).expect("should be non-null") }; + let raw = anyref.to_raw(&mut store)?; + let anyref = AnyRef::from_raw(&mut store, raw).expect("should be non-null"); assert!(anyref.is_i31(&store)?); assert_eq!(anyref.as_i31(&store)?.unwrap().get_u32(), 42); diff --git a/tests/all/pulley.rs b/tests/all/pulley.rs index 6d95d85ba9..a189e7b66d 100644 --- a/tests/all/pulley.rs +++ b/tests/all/pulley.rs @@ -1,4 +1,5 @@ use anyhow::Result; +use std::ptr::NonNull; use wasmtime::component::{self, Component}; use wasmtime::{ Caller, Config, Engine, Func, FuncType, Instance, Module, Store, Trap, Val, ValType, @@ -525,3 +526,22 @@ fn enabling_debug_info_doesnt_break_anything() -> Result<()> { assert!(Module::from_file(&engine, "./tests/all/cli_tests/greeter_command.wat").is_err()); Ok(()) } + +// Ensure that Pulley doesn't require that the input image is aligned at any +// particular boundary, namely for now this double-checks that the `unaligned` +// feature of the `object` crate is enabled. +#[test] +fn decode_unaligned() -> Result<()> { + let engine = Engine::new(&pulley_config())?; + let mut bytes = Module::new(&engine, "(module)")?.serialize()?; + + for i in 0..10 { + let serialized = &bytes[i..]; + unsafe { + Module::deserialize_raw(&engine, NonNull::from(serialized))?; + } + bytes.insert(0, 0); + } + + Ok(()) +} diff --git a/tests/disas/component-model/direct-adapter-calls-inlining.wat b/tests/disas/component-model/direct-adapter-calls-inlining.wat new file mode 100644 index 0000000000..ea078d34ee --- /dev/null +++ b/tests/disas/component-model/direct-adapter-calls-inlining.wat @@ -0,0 +1,145 @@ +;;! target = "x86_64" +;;! test = "optimize" +;;! filter = "wasm[1]--function" +;;! flags = "-C inlining=y" + +;; Same as `direct-adapter-calls.wat`, except we have enabled function inlining +;; so all the direct calls should get inlined. + +(component + (component $A + (core module $M + (func (export "f'") (param i32) (result i32) + (i32.add (local.get 0) (i32.const 42)) + ) + ) + + (core instance $m (instantiate $M)) + + (func (export "f") (param "x" u32) (result u32) + (canon lift (core func $m "f'")) + ) + ) + + (component $B + (import "f" (func $f (param "x" u32) (result u32))) + + (core func $f' (canon lower (func $f))) + + (core module $N + (import "" "f'" (func $f' (param i32) (result i32))) + (func (export "g'") (result i32) + (call $f' (i32.const 1234)) + ) + ) + + (core instance $n + (instantiate $N + (with "" (instance (export "f'" (func $f')))) + ) + ) + + (func (export "g") (result u32) + (canon lift (core func $n "g'")) + ) + ) + + (instance $a (instantiate $A)) + (instance $b + (instantiate $B + (with "f" (func $a "f")) + ) + ) + + (export "g" (func $b "g")) +) + +;; function u0:1(i64 vmctx, i64) -> i32 tail { +;; gv0 = vmctx +;; gv1 = load.i64 notrap aligned readonly gv0+8 +;; gv2 = load.i64 notrap aligned gv1+16 +;; gv3 = vmctx +;; gv4 = vmctx +;; gv5 = load.i64 notrap aligned readonly gv4+8 +;; gv6 = load.i64 notrap aligned gv5+16 +;; gv7 = vmctx +;; gv8 = load.i64 notrap aligned readonly can_move gv7+96 +;; gv9 = load.i64 notrap aligned readonly can_move gv7+72 +;; gv10 = vmctx +;; gv11 = load.i64 notrap aligned readonly gv10+8 +;; gv12 = load.i64 notrap aligned gv11+16 +;; sig0 = (i64 vmctx, i64, i32) -> i32 tail +;; sig1 = (i64 vmctx, i64, i32) -> i32 tail +;; fn0 = colocated u0:0 sig0 +;; fn1 = colocated u0:0 sig1 +;; stack_limit = gv2 +;; +;; block0(v0: i64, v1: i64): +;; @00ee jump block2 +;; +;; block2: +;; @00ee v5 = load.i64 notrap aligned readonly can_move v0+64 +;; v12 = load.i64 notrap aligned readonly can_move v5+96 +;; v13 = load.i32 notrap aligned table v12 +;; v14 = iconst.i32 1 +;; v15 = band v13, v14 ; v14 = 1 +;; v11 = iconst.i32 0 +;; v17 = icmp eq v15, v11 ; v11 = 0 +;; v18 = uextend.i32 v17 +;; trapnz v18, user11 +;; jump block5 +;; +;; block5: +;; v19 = load.i64 notrap aligned readonly can_move v5+72 +;; v20 = load.i32 notrap aligned table v19 +;; v21 = iconst.i32 2 +;; v22 = band v20, v21 ; v21 = 2 +;; v79 = iconst.i32 0 +;; v80 = icmp eq v22, v79 ; v79 = 0 +;; v25 = uextend.i32 v80 +;; trapnz v25, user11 +;; jump block7 +;; +;; block7: +;; v27 = load.i32 notrap aligned table v19 +;; v28 = iconst.i32 -3 +;; v29 = band v27, v28 ; v28 = -3 +;; store notrap aligned table v29, v19 +;; v60 = iconst.i32 -4 +;; v66 = band v27, v60 ; v60 = -4 +;; store notrap aligned table v66, v19 +;; v81 = iconst.i32 1 +;; v82 = bor v29, v81 ; v81 = 1 +;; store notrap aligned table v82, v19 +;; jump block8 +;; +;; block8: +;; jump block9 +;; +;; block9: +;; jump block10 +;; +;; block10: +;; v45 = load.i32 notrap aligned table v12 +;; v33 = iconst.i32 -2 +;; v47 = band v45, v33 ; v33 = -2 +;; store notrap aligned table v47, v12 +;; v83 = iconst.i32 1 +;; v84 = bor v45, v83 ; v83 = 1 +;; store notrap aligned table v84, v12 +;; v55 = load.i32 notrap aligned table v19 +;; v85 = iconst.i32 2 +;; v86 = bor v55, v85 ; v85 = 2 +;; store notrap aligned table v86, v19 +;; jump block3 +;; +;; block3: +;; jump block11 +;; +;; block11: +;; @00f0 jump block1 +;; +;; block1: +;; v70 = iconst.i32 1276 +;; @00f0 return v70 ; v70 = 1276 +;; } diff --git a/tests/disas/component-model/direct-adapter-calls-x64.wat b/tests/disas/component-model/direct-adapter-calls-x64.wat index da480c9006..c2062cbbe4 100644 --- a/tests/disas/component-model/direct-adapter-calls-x64.wat +++ b/tests/disas/component-model/direct-adapter-calls-x64.wat @@ -1,6 +1,7 @@ ;;! target = "x86_64" ;;! test = 'compile' ;;! filter = "function" +;;! flags = "-C inlining=n" ;; Same as `direct-adapter-calls.wat` but shows full compilation down to x86_64 ;; so that we can exercise our linker's ability to resolve relocations for diff --git a/tests/disas/component-model/direct-adapter-calls.wat b/tests/disas/component-model/direct-adapter-calls.wat index 45f32b5a44..52f88c625b 100644 --- a/tests/disas/component-model/direct-adapter-calls.wat +++ b/tests/disas/component-model/direct-adapter-calls.wat @@ -1,6 +1,7 @@ ;;! target = "x86_64" -;;! test = 'optimize' +;;! test = "optimize" ;;! filter = "function" +;;! flags = "-C inlining=n" ;; The following component links two sub-components together and each are only ;; instantiated the once, so we statically know what their core modules' diff --git a/tests/disas/duplicate-function-types.wat b/tests/disas/duplicate-function-types.wat new file mode 100644 index 0000000000..7f8e8206d3 --- /dev/null +++ b/tests/disas/duplicate-function-types.wat @@ -0,0 +1,101 @@ +;;! target = "x86_64" + +(module + ;; These two types should be deduped to the same `ir::Signature` in the + ;; translated CLIF. + (type (func (result i32))) + (type (func (result i32))) + + (import "" "" (table 0 funcref)) + + (func (param i32) (result i32 i32) + local.get 0 + call_indirect (type 0) + local.get 0 + call_indirect (type 1) + ) +) + +;; function u0:0(i64 vmctx, i64, i32) -> i32, i32 tail { +;; gv0 = vmctx +;; gv1 = load.i64 notrap aligned readonly gv0+8 +;; gv2 = load.i64 notrap aligned gv1+16 +;; gv3 = vmctx +;; gv4 = load.i64 notrap aligned readonly can_move gv3+48 +;; gv5 = load.i64 notrap aligned gv4 +;; gv6 = load.i64 notrap aligned gv4+8 +;; sig0 = (i64 vmctx, i64) -> i32 tail +;; sig1 = (i64 vmctx, i32, i64) -> i64 tail +;; fn0 = colocated u1:9 sig1 +;; stack_limit = gv2 +;; +;; block0(v0: i64, v1: i64, v2: i32): +;; @002d v64 = load.i64 notrap aligned readonly can_move v0+48 +;; @002d v5 = load.i64 notrap aligned v64+8 +;; @002d v6 = ireduce.i32 v5 +;; @002d v7 = icmp uge v2, v6 +;; @002d v8 = uextend.i64 v2 +;; @002d v62 = load.i64 notrap aligned readonly can_move v0+48 +;; @002d v9 = load.i64 notrap aligned v62 +;; v61 = iconst.i64 3 +;; @002d v10 = ishl v8, v61 ; v61 = 3 +;; @002d v11 = iadd v9, v10 +;; @002d v12 = iconst.i64 0 +;; @002d v13 = select_spectre_guard v7, v12, v11 ; v12 = 0 +;; @002d v14 = load.i64 user5 aligned table v13 +;; v60 = iconst.i64 -2 +;; @002d v15 = band v14, v60 ; v60 = -2 +;; @002d brif v14, block3(v15), block2 +;; +;; block2 cold: +;; @002d v17 = iconst.i32 0 +;; @002d v19 = uextend.i64 v2 +;; @002d v20 = call fn0(v0, v17, v19) ; v17 = 0 +;; @002d jump block3(v20) +;; +;; block3(v16: i64): +;; @002d v22 = load.i64 notrap aligned readonly can_move v0+40 +;; @002d v23 = load.i32 notrap aligned readonly can_move v22 +;; @002d v24 = load.i32 user6 aligned readonly v16+16 +;; @002d v25 = icmp eq v24, v23 +;; @002d trapz v25, user7 +;; @002d v26 = load.i64 notrap aligned readonly v16+8 +;; @002d v27 = load.i64 notrap aligned readonly v16+24 +;; @002d v28 = call_indirect sig0, v26(v27, v0) +;; @0032 v58 = load.i64 notrap aligned readonly can_move v0+48 +;; @0032 v30 = load.i64 notrap aligned v58+8 +;; @0032 v31 = ireduce.i32 v30 +;; @0032 v32 = icmp.i32 uge v2, v31 +;; @0032 v33 = uextend.i64 v2 +;; @0032 v56 = load.i64 notrap aligned readonly can_move v0+48 +;; @0032 v34 = load.i64 notrap aligned v56 +;; v55 = iconst.i64 3 +;; @0032 v35 = ishl v33, v55 ; v55 = 3 +;; @0032 v36 = iadd v34, v35 +;; @0032 v37 = iconst.i64 0 +;; @0032 v38 = select_spectre_guard v32, v37, v36 ; v37 = 0 +;; @0032 v39 = load.i64 user5 aligned table v38 +;; v54 = iconst.i64 -2 +;; @0032 v40 = band v39, v54 ; v54 = -2 +;; @0032 brif v39, block5(v40), block4 +;; +;; block4 cold: +;; @0032 v42 = iconst.i32 0 +;; @0032 v44 = uextend.i64 v2 +;; @0032 v45 = call fn0(v0, v42, v44) ; v42 = 0 +;; @0032 jump block5(v45) +;; +;; block5(v41: i64): +;; @0032 v47 = load.i64 notrap aligned readonly can_move v0+40 +;; @0032 v48 = load.i32 notrap aligned readonly can_move v47 +;; @0032 v49 = load.i32 user6 aligned readonly v41+16 +;; @0032 v50 = icmp eq v49, v48 +;; @0032 trapz v50, user7 +;; @0032 v51 = load.i64 notrap aligned readonly v41+8 +;; @0032 v52 = load.i64 notrap aligned readonly v41+24 +;; @0032 v53 = call_indirect sig0, v51(v52, v0) +;; @0035 jump block1 +;; +;; block1: +;; @0035 return v28, v53 +;; } diff --git a/tests/misc_testsuite/component-model-async/empty-wait.wast b/tests/misc_testsuite/component-model-async/empty-wait.wast index b412dfc846..a8364d58bf 100644 --- a/tests/misc_testsuite/component-model-async/empty-wait.wast +++ b/tests/misc_testsuite/component-model-async/empty-wait.wast @@ -96,8 +96,8 @@ (canon waitable-set.new (core func $waitable-set.new)) (canon waitable-set.wait (memory $memory "mem") (core func $waitable-set.wait)) (canon future.new $FT (core func $future.new)) - (canon future.read $FT async (memory $memory "mem") (core func $future.read)) - (canon future.write $FT async (memory $memory "mem") (core func $future.write)) + (canon future.read $FT async (core func $future.read)) + (canon future.write $FT async (core func $future.write)) (canon future.drop-readable $FT (core func $future.drop-readable)) (canon future.drop-writable $FT (core func $future.drop-writable)) (core instance $cm (instantiate $CM (with "" (instance diff --git a/tests/misc_testsuite/component-model/fused.wast b/tests/misc_testsuite/component-model/fused.wast index 7b6a5e5437..90f2ed4a1c 100644 --- a/tests/misc_testsuite/component-model/fused.wast +++ b/tests/misc_testsuite/component-model/fused.wast @@ -1340,7 +1340,7 @@ (core instance $m (instantiate $m)) ;; This adapter, when fused with itself on the second instantiation of this - ;; component, will dependend on the prior instance `$m` so it which means + ;; component, will depended on the prior instance `$m` so it which means ;; that the adapter module containing this must be placed in the right ;; location. (core func $execute diff --git a/tests/spec_testsuite b/tests/spec_testsuite index 970d36b216..f50a6621db 160000 --- a/tests/spec_testsuite +++ b/tests/spec_testsuite @@ -1 +1 @@ -Subproject commit 970d36b21676828f0d4482abee5554cd2c9813e6 +Subproject commit f50a6621db90a94e9284b903a5adac4ac3aa076b diff --git a/tests/wast.rs b/tests/wast.rs index f65862c933..b7066a387a 100644 --- a/tests/wast.rs +++ b/tests/wast.rs @@ -244,7 +244,7 @@ fn run_wast(test: &WastTest, config: WastConfig) -> anyhow::Result<()> { suppress_prints: true, })?; wast_context - .run_buffer(test.path.to_str().unwrap(), test.contents.as_bytes()) + .run_wast(test.path.to_str().unwrap(), test.contents.as_bytes()) .with_context(|| format!("failed to run spec test with {desc} engine")) }); diff --git a/winch/codegen/src/isa/x64/address.rs b/winch/codegen/src/isa/x64/address.rs index cab35620a6..eae32e2831 100644 --- a/winch/codegen/src/isa/x64/address.rs +++ b/winch/codegen/src/isa/x64/address.rs @@ -30,7 +30,7 @@ impl Address { Self::Const(data) } - /// Check if the address is a made made of a base and offset. + /// Check if the address is a made of a base and offset. pub fn is_offset(&self) -> bool { match self { Self::Offset { .. } => true, diff --git a/winch/codegen/src/isa/x64/asm.rs b/winch/codegen/src/isa/x64/asm.rs index b760a6919d..283a20bf00 100644 --- a/winch/codegen/src/isa/x64/asm.rs +++ b/winch/codegen/src/isa/x64/asm.rs @@ -18,9 +18,8 @@ use cranelift_codegen::{ x64::{ AtomicRmwSeqOp, EmitInfo, EmitState, Inst, args::{ - self, Amode, Avx512Opcode, CC, ExtMode, FromWritableReg, Gpr, GprMem, GprMemImm, - RegMem, RegMemImm, SyntheticAmode, WritableGpr, WritableXmm, Xmm, XmmMem, - XmmMemImm, + self, Amode, CC, ExtMode, FromWritableReg, Gpr, GprMem, GprMemImm, RegMem, + RegMemImm, SyntheticAmode, WritableGpr, WritableXmm, Xmm, XmmMem, XmmMemImm, }, external::{PairedGpr, PairedXmm}, settings as x64_settings, @@ -2311,21 +2310,10 @@ impl Assembler { self.emit(Inst::External { inst }); } - pub(crate) fn xmm_rm_rvex3( - &mut self, - op: Avx512Opcode, - src1: Reg, - src2: Reg, - dst: WritableReg, - ) { - self.emit(Inst::XmmRmREvex3 { - op, - // `src1` reuses `dst`, and is ignored in emission - src1: dst.to_reg().into(), - src2: src1.into(), - src3: src2.into(), - dst: dst.map(Into::into), - }); + pub(crate) fn vpmullq(&mut self, src1: Reg, src2: Reg, dst: WritableReg) { + let dst: WritableXmm = dst.map(|r| r.into()); + let inst = asm::inst::vpmullq_c::new(dst, src1, src2).into(); + self.emit(Inst::External { inst }); } /// Creates a mask made up of the most significant bit of each byte of diff --git a/winch/codegen/src/isa/x64/masm.rs b/winch/codegen/src/isa/x64/masm.rs index 3e78a56c54..e5561ffe80 100644 --- a/winch/codegen/src/isa/x64/masm.rs +++ b/winch/codegen/src/isa/x64/masm.rs @@ -38,11 +38,7 @@ use cranelift_codegen::{ ir::{MemFlags, RelSourceLoc, SourceLoc}, isa::{ unwind::UnwindInst, - x64::{ - AtomicRmwSeqOp, - args::{Avx512Opcode, CC}, - settings as x64_settings, - }, + x64::{AtomicRmwSeqOp, args::CC, settings as x64_settings}, }, settings, }; @@ -2188,8 +2184,7 @@ impl Masm for MacroAssembler { let lhs = context.pop_to_reg(self, None)?; let mul_i64x2_avx512 = |this: &mut Self| { - this.asm - .xmm_rm_rvex3(Avx512Opcode::Vpmullq, lhs.reg, rhs.reg, writable!(lhs.reg)); + this.asm.vpmullq(lhs.reg, rhs.reg, writable!(lhs.reg)); }; let mul_i64x2_fallback = |this: &mut Self,